[
  {
    "path": ".gitignore",
    "content": "# built application files\n*.apk\n*.ap_\n\n# files for the dex VM\n*.dex\n\n# Java class files\n*.class\n\n# generated files\nbin/\ngen/\nbuild/\ntarget/\n\n# Local configuration file (sdk path, etc)\nlocal.properties\n\n# Eclipse project files\n.classpath\n.project\n.settings\n\n# Proguard folder generated by Eclipse\nproguard/\n\n# Intellij project files\n*.iml\n*.ipr\n*.iws\n.idea/\n\n\n/.settings\n/.gradle\n"
  },
  {
    "path": "LICENSE.txt",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "# HelloCharts for Android\n\nCharting library for Android compatible with API 8+(Android 2.2).\nWorks best when hardware acceleration is available, so API 14+(Android 4.0) is recommended.\nApache License 2.0.\n\n[![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-HelloCharts%20for%20Android-brightgreen.svg?style=flat)](https://android-arsenal.com/details/1/1068)\n<a href=\"https://scan.coverity.com/projects/4121\">\n  <img alt=\"Coverity Scan Build Status\"\n       src=\"https://scan.coverity.com/projects/4121/badge.svg\"/>\n</a>\n[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.lecho/hellocharts-library/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.github.lecho/hellocharts-library)\n[![Release](https://img.shields.io/github/release/lecho/hellocharts-android.svg?label=JitPack.io)](https://jitpack.io/#lecho/hellocharts-android)\n\n## Features\n\n - Line chart(cubic lines, filled lines, scattered points)\n - Column chart(grouped, stacked, negative values)\n - Pie chart\n - Bubble chart\n - Combo chart(columns/lines)\n - Preview charts(for column chart and line chart)\n - Zoom(pinch to zoom, double tap zoom), scroll and fling\n - Custom and auto-generated axes(top, bottom, left, right, inside)\n - Animations\n\n## Screens and Demos\n\n - Code of a demo application is in `hellocharts-samples` directory, requires appcompat v21. \n - The **demo app** is also ready for download on [**Google Play**](https://play.google.com/store/apps/details?id=lecho.lib.hellocharts.samples).  \n - Short **video** is available on [**YouTube**](https://www.youtube.com/watch?v=xbSBjyjH2SY).\n\n![](screens/scr_dependecy_preview.gif)\n\n![](screens/scr-tempo.png)\n\n![](screens/scr-dependency.png)\n\n![](screens/scr-preview-column.png)\n\n![](screens/scr-pie1.png)\n\n![](screens/scr-bubble1.png)\n\n![](screens/scr-combo.png)\n\n## Download and Import\n\n#### Android Studio/Gradle\n\n - Maven Central/jCenter, add dependency to your `build.gradle`:\n \n ```groovy\n\tdependencies{\n \t\tcompile 'com.github.lecho:hellocharts-library:1.5.8@aar'\n\t}\n ```\n \n - JitPack.io, add `jitpack.io` repositiory and dependency to your `build.gradle`:\n \n ```groovy\n    repositories {\n        maven {\n            url \"https://jitpack.io\"\n        }\n    }\n\t\n    dependencies {\n        compile 'com.github.lecho:hellocharts-android:v1.5.8'\n    }\n ```\n \n#### Eclipse/ADT\n\n - Download the latest [release jar file](https://github.com/lecho/hellocharts-android/releases).\n - Copy `hellocharts-library-<version>.jar` into the `libs` folder of your application project.\n\n## Usage\n\nEvery chart view can be defined in layout xml file:\n\n ```xml\n    <lecho.lib.hellocharts.view.LineChartView\n        android:id=\"@+id/chart\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" />\n ```\n\n or created in code and added to layout later:\n\n ```java\n    LineChartView chart = new LineChartView(context);\n    layout.addView(chart);\n ```\n\n Use methods from *Chart classes to define chart behaviour, example methods:\n\n ```java\n    Chart.setInteractive(boolean isInteractive);\n    Chart.setZoomType(ZoomType zoomType);\n    Chart.setContainerScrollEnabled(boolean isEnabled, ContainerScrollType type);\n ```\n\n Use methods from data models to define how chart looks like, example methods:\n\n ```java\n    ChartData.setAxisXBottom(Axis axisX);\n    ColumnChartData.setStacked(boolean isStacked);\n    Line.setStrokeWidth(int strokeWidthDp);\n ```\n\n Every chart has its own method to set chart data and its own data model, example for line chart:\n\n ```java\n    List<PointValue> values = new ArrayList<PointValue>();\n    values.add(new PointValue(0, 2));\n    values.add(new PointValue(1, 4));\n    values.add(new PointValue(2, 3));\n    values.add(new PointValue(3, 4));\n\n    //In most cased you can call data model methods in builder-pattern-like manner.\n    Line line = new Line(values).setColor(Color.BLUE).setCubic(true);\n    List<Line> lines = new ArrayList<Line>();\n    lines.add(line);\n\n    LineChartData data = new LineChartData();\n    data.setLines(lines);\n\n\tLineChartView chart = new LineChartView(context);\n    chart.setLineChartData(data);\n ```\n\n After the chart data has been set you can still modify its attributes but right after that you should call\n `set*ChartData()` method again to let chart recalculate and redraw data. There is also an option to use copy constructor for deep copy of\n chart data. You can safely modify copy in other threads and pass it to `set*ChartData()` method later.\n\n\n## Contributing\n\nYes:) If you found a bug, have an idea how to improve library or have a question, please create new issue or comment existing one. If you would like to contribute code fork the repository and send a pull request.\n\n# License\n\n\tHelloCharts\t\n    Copyright 2014 Leszek Wach\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n\n---\n     HelloCharts library uses code from InteractiveChart sample available \n     on Android Developers page:\n\t \n       http://developer.android.com/training/gestures/scale.html\n"
  },
  {
    "path": "build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\nbuildscript {\n    repositories {\n        mavenCentral()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:2.1.0'\n    }\n}\n"
  },
  {
    "path": "documentation/MAIN.md",
    "content": ""
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Wed Apr 10 15:27:10 PDT 2013\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=http\\://services.gradle.org/distributions/gradle-2.2.1-all.zip\n"
  },
  {
    "path": "gradle.properties",
    "content": "VERSION_NAME=1.5.8\nVERSION_CODE=13\nGROUP=com.github.lecho\n\nPOM_DESCRIPTION=Charting library for Android compatible with API 8+(Android 2.2).\nPOM_URL=https://github.com/lecho/hellocharts-android\nPOM_SCM_URL=https://github.com/lecho/hellocharts-android\nPOM_SCM_CONNECTION=scm:git@github.com:lecho/hellocharts-android.git\nPOM_SCM_DEV_CONNECTION=scm:git@github.com:lecho/hellocharts-android.git\nPOM_LICENCE_NAME=The Apache Software License, Version 2.0\nPOM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt\nPOM_LICENCE_DIST=repo\nPOM_DEVELOPER_ID=lecho\nPOM_DEVELOPER_NAME=Leszek Wach\n\nANDROID_BUILD_MIN_SDK_VERSION=8\nANDROID_BUILD_TARGET_SDK_VERSION=23\nANDROID_BUILD_TOOLS_VERSION=23.0.3\nANDROID_BUILD_SDK_VERSION=23\n"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn ( ) {\n    echo \"$*\"\n}\n\ndie ( ) {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\nesac\n\n# For Cygwin, ensure paths are in UNIX format before anything is touched.\nif $cygwin ; then\n    [ -n \"$JAVA_HOME\" ] && JAVA_HOME=`cygpath --unix \"$JAVA_HOME\"`\nfi\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >&-\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >&-\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules\nfunction splitJvmOpts() {\n    JVM_OPTS=(\"$@\")\n}\neval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\nJVM_OPTS[${#JVM_OPTS[*]}]=\"-Dorg.gradle.appname=$APP_BASE_NAME\"\n\nexec \"$JAVACMD\" \"${JVM_OPTS[@]}\" -classpath \"$CLASSPATH\" org.gradle.wrapper.GradleWrapperMain \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem  Gradle startup script for Windows\n@rem\n@rem ##########################################################################\n\n@rem Set local scope for the variables with windows NT shell\nif \"%OS%\"==\"Windows_NT\" setlocal\n\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nset DEFAULT_JVM_OPTS=\n\nset DIRNAME=%~dp0\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\n\n@rem Find java.exe\nif defined JAVA_HOME goto findJavaFromJavaHome\n\nset JAVA_EXE=java.exe\n%JAVA_EXE% -version >NUL 2>&1\nif \"%ERRORLEVEL%\" == \"0\" goto init\n\necho.\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:findJavaFromJavaHome\nset JAVA_HOME=%JAVA_HOME:\"=%\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\n\nif exist \"%JAVA_EXE%\" goto init\n\necho.\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:init\n@rem Get command-line arguments, handling Windowz variants\n\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\nif \"%@eval[2+2]\" == \"4\" goto 4NT_args\n\n:win9xME_args\n@rem Slurp the command line arguments.\nset CMD_LINE_ARGS=\nset _SKIP=2\n\n:win9xME_args_slurp\nif \"x%~1\" == \"x\" goto execute\n\nset CMD_LINE_ARGS=%*\ngoto execute\n\n:4NT_args\n@rem Get arguments from the 4NT Shell from JP Software\nset CMD_LINE_ARGS=%$\n\n:execute\n@rem Setup the command line\n\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\n\n@rem Execute Gradle\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\n\n:end\n@rem End local scope for the variables with windows NT shell\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\n\n:fail\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\nrem the _cmd.exe /c_ return code!\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\nexit /b 1\n\n:mainEnd\nif \"%OS%\"==\"Windows_NT\" endlocal\n\n:omega\n"
  },
  {
    "path": "hellocharts-library/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"lecho.lib.hellocharts\"\n    android:versionCode=\"13\"\n    android:versionName=\"1.5.8\" >\n\n    <uses-sdk\n        android:minSdkVersion=\"8\"\n        android:targetSdkVersion=\"22\" />\n\n</manifest>"
  },
  {
    "path": "hellocharts-library/build.gradle",
    "content": "apply plugin: 'com.android.library'\napply plugin: 'maven-publish'\napply from: 'https://raw.github.com/chrisbanes/gradle-mvn-push/master/gradle-mvn-push.gradle'\n\n//To upload to maven central\n//http://zserge.com/blog/gradle-maven-publish.html\n//gradle uploadArchives\n\nbuildscript {\n    repositories {\n        mavenCentral()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:2.1.0'\n    }\n}\n\ndependencies {\n    //compile fileTree(dir: 'libs', include: '*.jar')\n    compile 'com.android.support:support-v4:23.4.0'\n}\n\n\nandroid {\n    compileSdkVersion Integer.parseInt(project.ANDROID_BUILD_SDK_VERSION)\n    buildToolsVersion project.ANDROID_BUILD_TOOLS_VERSION\n\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_7\n        targetCompatibility JavaVersion.VERSION_1_7\n    }\n\n    defaultConfig {\n        minSdkVersion Integer.parseInt(project.ANDROID_BUILD_MIN_SDK_VERSION)\n        targetSdkVersion Integer.parseInt(project.ANDROID_BUILD_TARGET_SDK_VERSION)\n        versionCode Integer.parseInt(project.VERSION_CODE)\n        versionName project.VERSION_NAME\n    }\n\n    sourceSets {\n        main {\n            manifest.srcFile 'AndroidManifest.xml'\n            java.srcDirs = ['src']\n            resources.srcDirs = ['src']\n            aidl.srcDirs = ['src']\n            renderscript.srcDirs = ['src']\n            res.srcDirs = ['res']\n            assets.srcDirs = ['assets']\n        }\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n\n    lintOptions {\n        abortOnError false\n    }\n\n}\n\n//Execute \"gradle clean jarRelease\" to cook jar.\nandroid.libraryVariants.all { variant ->\n    def name = variant.buildType.name\n    if (name.equals(com.android.builder.core.BuilderConstants.DEBUG)) {\n        return; // Skip debug builds.\n    }\n    def task = project.tasks.create \"jar${name.capitalize()}\", Jar\n    task.dependsOn variant.javaCompile\n    task.from variant.javaCompile.destinationDir\n    artifacts.add('archives', task);\n}\n\n// To publish to maven local execute \"gradle clean build publishToMavenLocal\"\n// To publish to nexus execute \"gradle clean build publish\"\nandroid.libraryVariants\npublishing {\n    publications {\n        maven(MavenPublication) {\n            artifact \"${project.buildDir}/outputs/aar/${project.name}-release.aar\"\n            artifactId = POM_ARTIFACT_ID\n            groupId = GROUP\n            version = VERSION_NAME\n\n            // Task androidSourcesJar is provided by gradle-mvn-push.gradle\n            //artifact androidSourcesJar {\n            //    classifier \"sources\"\n            //}\n        }\n    }\n\n    repositories {\n        maven {\n            credentials {\n                username 'nexusUser'\n                password 'nexusPass'\n            }\n            url \"http://your-nexus-url/\"\n        }\n    }\n}\n"
  },
  {
    "path": "hellocharts-library/gradle.properties",
    "content": "POM_NAME=HelloCharts Library for Android\nPOM_ARTIFACT_ID=hellocharts-library\nPOM_PACKAGING=aar\n"
  },
  {
    "path": "hellocharts-library/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <groupId>lecho.lib.hellocharts</groupId>\n    <artifactId>hellocharts-library</artifactId>\n    <name>HelloCharts Android</name>\n    <version>1.0</version>\n    <packaging>jar</packaging>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <android.version>4.4</android.version>\n        <support.version>19</support.version>\n    </properties>\n\n\n    <dependencies>\n        <!--Upload android.jar(sdk\\platforms\\android-19 with version 4.4 in pom.xml) and android-support-v4.jar(sdk\\extras\\android\\support\\v4 with version 19 in pom.xml)-->\n        <!--to your local maven repository to use maven build.-->\n        <dependency>\n            <groupId>com.google.android</groupId>\n            <artifactId>android</artifactId>\n            <version>${android.version}</version>\n            <scope>provided</scope>\n        </dependency>\n        <dependency>\n            <groupId>com.android.support</groupId>\n            <artifactId>support-v4</artifactId>\n            <version>${support.version}</version>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <sourceDirectory>src</sourceDirectory>\n\n        <plugins>\n            <plugin>\n                <groupId>com.jayway.maven.plugins.android.generation2</groupId>\n                <artifactId>android-maven-plugin</artifactId>\n                <extensions>true</extensions>\n            </plugin>\n        </plugins>\n    </build>\n\n    <licenses>\n        <license>\n            <name>Apache License Version 2.0</name>\n            <url>http://www.apache.org/licenses/LICENSE-2.0.html</url>\n            <distribution>repo</distribution>\n        </license>\n    </licenses>\n</project>\n"
  },
  {
    "path": "hellocharts-library/proguard-project.txt",
    "content": "# To enable ProGuard in your project, edit project.properties\n# to define the proguard.config property as described in that file.\n#\n# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in ${sdk.dir}/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the ProGuard\n# include property in project.properties.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n"
  },
  {
    "path": "hellocharts-library/project.properties",
    "content": "# This file is automatically generated by Android Tools.\n# Do not modify this file -- YOUR CHANGES WILL BE ERASED!\n#\n# This file must be checked in Version Control Systems.\n#\n# To customize properties used by the Ant build system edit\n# \"ant.properties\", and override values to adapt the script to your\n# project structure.\n#\n# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):\n#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt\n\n# Project target.\ntarget=android-21\nandroid.library=true\n"
  },
  {
    "path": "hellocharts-library/res/values/strings.xml",
    "content": "<resources>\n\n</resources>\n"
  },
  {
    "path": "hellocharts-library/res/values/styles.xml",
    "content": "<resources>\n\n</resources>\n"
  },
  {
    "path": "hellocharts-library/res/values-v11/styles.xml",
    "content": "<resources>\n\n</resources>\n"
  },
  {
    "path": "hellocharts-library/res/values-v14/styles.xml",
    "content": "<resources>\n\n</resources>\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/animation/ChartAnimationListener.java",
    "content": "package lecho.lib.hellocharts.animation;\n\nimport java.util.EventListener;\n\n/**\n * Listener used to listen for chart animation start and stop events. Implementations of this interface can be used for\n * all types of chart animations(data, viewport, PieChart rotation).\n */\npublic interface ChartAnimationListener extends EventListener {\n\n    public void onAnimationStarted();\n\n    public void onAnimationFinished();\n\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/animation/ChartDataAnimator.java",
    "content": "package lecho.lib.hellocharts.animation;\n\npublic interface ChartDataAnimator {\n\n    public static final long DEFAULT_ANIMATION_DURATION = 500;\n\n    public void startAnimation(long duration);\n\n    public void cancelAnimation();\n\n    public boolean isAnimationStarted();\n\n    public void setChartAnimationListener(ChartAnimationListener animationListener);\n\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/animation/ChartDataAnimatorV14.java",
    "content": "package lecho.lib.hellocharts.animation;\n\nimport android.animation.Animator;\nimport android.animation.Animator.AnimatorListener;\nimport android.animation.ValueAnimator;\nimport android.animation.ValueAnimator.AnimatorUpdateListener;\nimport android.annotation.SuppressLint;\n\nimport lecho.lib.hellocharts.view.Chart;\n\n@SuppressLint(\"NewApi\")\npublic class ChartDataAnimatorV14 implements ChartDataAnimator, AnimatorListener, AnimatorUpdateListener {\n    private final Chart chart;\n    private ValueAnimator animator;\n    private ChartAnimationListener animationListener = new DummyChartAnimationListener();\n\n    public ChartDataAnimatorV14(Chart chart) {\n        this.chart = chart;\n        animator = ValueAnimator.ofFloat(0.0f, 1.0f);\n        animator.addListener(this);\n        animator.addUpdateListener(this);\n    }\n\n    @Override\n    public void startAnimation(long duration) {\n        if (duration >= 0) {\n            animator.setDuration(duration);\n        } else {\n            animator.setDuration(DEFAULT_ANIMATION_DURATION);\n        }\n        animator.start();\n    }\n\n    @Override\n    public void cancelAnimation() {\n        animator.cancel();\n    }\n\n    @Override\n    public void onAnimationUpdate(ValueAnimator animation) {\n        chart.animationDataUpdate(animation.getAnimatedFraction());\n    }\n\n    @Override\n    public void onAnimationCancel(Animator animation) {\n    }\n\n    @Override\n    public void onAnimationEnd(Animator animation) {\n        chart.animationDataFinished();\n        animationListener.onAnimationFinished();\n    }\n\n    @Override\n    public void onAnimationRepeat(Animator animation) {\n    }\n\n    @Override\n    public void onAnimationStart(Animator animation) {\n        animationListener.onAnimationStarted();\n    }\n\n    @Override\n    public boolean isAnimationStarted() {\n        return animator.isStarted();\n    }\n\n    @Override\n    public void setChartAnimationListener(ChartAnimationListener animationListener) {\n        if (null == animationListener) {\n            this.animationListener = new DummyChartAnimationListener();\n        } else {\n            this.animationListener = animationListener;\n        }\n    }\n\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/animation/ChartDataAnimatorV8.java",
    "content": "package lecho.lib.hellocharts.animation;\n\nimport android.os.Handler;\nimport android.os.SystemClock;\nimport android.view.animation.AccelerateDecelerateInterpolator;\nimport android.view.animation.Interpolator;\n\nimport lecho.lib.hellocharts.view.Chart;\n\npublic class ChartDataAnimatorV8 implements ChartDataAnimator {\n\n    final Chart chart;\n    final Handler handler;\n    final Interpolator interpolator = new AccelerateDecelerateInterpolator();\n    long start;\n    boolean isAnimationStarted = false;\n    long duration;\n    private final Runnable runnable = new Runnable() {\n\n        @Override\n        public void run() {\n            long elapsed = SystemClock.uptimeMillis() - start;\n            if (elapsed > duration) {\n                isAnimationStarted = false;\n                handler.removeCallbacks(runnable);\n                chart.animationDataFinished();\n                return;\n            }\n            float scale = Math.min(interpolator.getInterpolation((float) elapsed / duration), 1);\n            chart.animationDataUpdate(scale);\n            handler.postDelayed(this, 16);\n\n        }\n    };\n    private ChartAnimationListener animationListener = new DummyChartAnimationListener();\n\n    public ChartDataAnimatorV8(Chart chart) {\n        this.chart = chart;\n        this.handler = new Handler();\n    }\n\n    @Override\n    public void startAnimation(long duration) {\n        if (duration >= 0) {\n            this.duration = duration;\n        } else {\n            this.duration = DEFAULT_ANIMATION_DURATION;\n        }\n\n        isAnimationStarted = true;\n        animationListener.onAnimationStarted();\n        start = SystemClock.uptimeMillis();\n        handler.post(runnable);\n    }\n\n    @Override\n    public void cancelAnimation() {\n        isAnimationStarted = false;\n        handler.removeCallbacks(runnable);\n        chart.animationDataFinished();\n        animationListener.onAnimationFinished();\n    }\n\n    @Override\n    public boolean isAnimationStarted() {\n        return isAnimationStarted;\n    }\n\n    @Override\n    public void setChartAnimationListener(ChartAnimationListener animationListener) {\n        if (null == animationListener) {\n            this.animationListener = new DummyChartAnimationListener();\n        } else {\n            this.animationListener = animationListener;\n        }\n    }\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/animation/ChartViewportAnimator.java",
    "content": "package lecho.lib.hellocharts.animation;\n\nimport lecho.lib.hellocharts.model.Viewport;\n\npublic interface ChartViewportAnimator {\n\n    public static final int FAST_ANIMATION_DURATION = 300;\n\n    public void startAnimation(Viewport startViewport, Viewport targetViewport);\n\n    public void startAnimation(Viewport startViewport, Viewport targetViewport, long duration);\n\n    public void cancelAnimation();\n\n    public boolean isAnimationStarted();\n\n    public void setChartAnimationListener(ChartAnimationListener animationListener);\n\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/animation/ChartViewportAnimatorV14.java",
    "content": "package lecho.lib.hellocharts.animation;\n\nimport android.animation.Animator;\nimport android.animation.Animator.AnimatorListener;\nimport android.animation.ValueAnimator;\nimport android.animation.ValueAnimator.AnimatorUpdateListener;\nimport android.annotation.SuppressLint;\n\nimport lecho.lib.hellocharts.model.Viewport;\nimport lecho.lib.hellocharts.view.Chart;\n\n@SuppressLint(\"NewApi\")\npublic class ChartViewportAnimatorV14 implements ChartViewportAnimator, AnimatorListener, AnimatorUpdateListener {\n    private final Chart chart;\n    private ValueAnimator animator;\n    private Viewport startViewport = new Viewport();\n    private Viewport targetViewport = new Viewport();\n    private Viewport newViewport = new Viewport();\n    private ChartAnimationListener animationListener = new DummyChartAnimationListener();\n\n    public ChartViewportAnimatorV14(Chart chart) {\n        this.chart = chart;\n        animator = ValueAnimator.ofFloat(0.0f, 1.0f);\n        animator.addListener(this);\n        animator.addUpdateListener(this);\n        animator.setDuration(FAST_ANIMATION_DURATION);\n    }\n\n    @Override\n    public void startAnimation(Viewport startViewport, Viewport targetViewport) {\n        this.startViewport.set(startViewport);\n        this.targetViewport.set(targetViewport);\n        animator.setDuration(FAST_ANIMATION_DURATION);\n        animator.start();\n    }\n\n    @Override\n    public void startAnimation(Viewport startViewport, Viewport targetViewport, long duration) {\n        this.startViewport.set(startViewport);\n        this.targetViewport.set(targetViewport);\n        animator.setDuration(duration);\n        animator.start();\n    }\n\n    @Override\n    public void cancelAnimation() {\n        animator.cancel();\n    }\n\n    @Override\n    public void onAnimationUpdate(ValueAnimator animation) {\n        float scale = animation.getAnimatedFraction();\n        float diffLeft = (targetViewport.left - startViewport.left) * scale;\n        float diffTop = (targetViewport.top - startViewport.top) * scale;\n        float diffRight = (targetViewport.right - startViewport.right) * scale;\n        float diffBottom = (targetViewport.bottom - startViewport.bottom) * scale;\n        newViewport.set(startViewport.left + diffLeft, startViewport.top + diffTop, startViewport.right + diffRight,\n                startViewport.bottom + diffBottom);\n        chart.setCurrentViewport(newViewport);\n    }\n\n    @Override\n    public void onAnimationCancel(Animator animation) {\n    }\n\n    @Override\n    public void onAnimationEnd(Animator animation) {\n        chart.setCurrentViewport(targetViewport);\n        animationListener.onAnimationFinished();\n    }\n\n    @Override\n    public void onAnimationRepeat(Animator animation) {\n    }\n\n    @Override\n    public void onAnimationStart(Animator animation) {\n        animationListener.onAnimationStarted();\n    }\n\n    @Override\n    public boolean isAnimationStarted() {\n        return animator.isStarted();\n    }\n\n    @Override\n    public void setChartAnimationListener(ChartAnimationListener animationListener) {\n        if (null == animationListener) {\n            this.animationListener = new DummyChartAnimationListener();\n        } else {\n            this.animationListener = animationListener;\n        }\n    }\n\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/animation/ChartViewportAnimatorV8.java",
    "content": "package lecho.lib.hellocharts.animation;\n\nimport android.os.Handler;\nimport android.os.SystemClock;\nimport android.view.animation.AccelerateDecelerateInterpolator;\nimport android.view.animation.Interpolator;\n\nimport lecho.lib.hellocharts.model.Viewport;\nimport lecho.lib.hellocharts.view.Chart;\n\npublic class ChartViewportAnimatorV8 implements ChartViewportAnimator {\n\n    final Chart chart;\n    final Handler handler;\n    final Interpolator interpolator = new AccelerateDecelerateInterpolator();\n    long start;\n    boolean isAnimationStarted = false;\n    private Viewport startViewport = new Viewport();\n    private Viewport targetViewport = new Viewport();\n    private Viewport newViewport = new Viewport();\n    private long duration;\n    private ChartAnimationListener animationListener = new DummyChartAnimationListener();\n    private final Runnable runnable = new Runnable() {\n\n        @Override\n        public void run() {\n            long elapsed = SystemClock.uptimeMillis() - start;\n            if (elapsed > duration) {\n                isAnimationStarted = false;\n                handler.removeCallbacks(runnable);\n                chart.setCurrentViewport(targetViewport);\n                animationListener.onAnimationFinished();\n                return;\n            }\n            float scale = Math.min(interpolator.getInterpolation((float) elapsed / duration), 1);\n            float diffLeft = (targetViewport.left - startViewport.left) * scale;\n            float diffTop = (targetViewport.top - startViewport.top) * scale;\n            float diffRight = (targetViewport.right - startViewport.right) * scale;\n            float diffBottom = (targetViewport.bottom - startViewport.bottom) * scale;\n            newViewport.set(startViewport.left + diffLeft, startViewport.top + diffTop,\n                    startViewport.right + diffRight, startViewport.bottom + diffBottom);\n            chart.setCurrentViewport(newViewport);\n\n            handler.postDelayed(this, 16);\n        }\n    };\n\n    public ChartViewportAnimatorV8(Chart chart) {\n        this.chart = chart;\n        this.duration = FAST_ANIMATION_DURATION;\n        this.handler = new Handler();\n    }\n\n    @Override\n    public void startAnimation(Viewport startViewport, Viewport targetViewport) {\n        this.startViewport.set(startViewport);\n        this.targetViewport.set(targetViewport);\n        duration = FAST_ANIMATION_DURATION;\n        isAnimationStarted = true;\n        animationListener.onAnimationStarted();\n        start = SystemClock.uptimeMillis();\n        handler.post(runnable);\n    }\n\n    @Override\n    public void startAnimation(Viewport startViewport, Viewport targetViewport, long duration) {\n        this.startViewport.set(startViewport);\n        this.targetViewport.set(targetViewport);\n        this.duration = duration;\n        isAnimationStarted = true;\n        animationListener.onAnimationStarted();\n        start = SystemClock.uptimeMillis();\n        handler.post(runnable);\n    }\n\n    @Override\n    public void cancelAnimation() {\n        isAnimationStarted = false;\n        handler.removeCallbacks(runnable);\n        chart.setCurrentViewport(targetViewport);\n        animationListener.onAnimationFinished();\n    }\n\n    @Override\n    public boolean isAnimationStarted() {\n        return isAnimationStarted;\n    }\n\n    @Override\n    public void setChartAnimationListener(ChartAnimationListener animationListener) {\n        if (null == animationListener) {\n            this.animationListener = new DummyChartAnimationListener();\n        } else {\n            this.animationListener = animationListener;\n        }\n    }\n\n\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/animation/DummyChartAnimationListener.java",
    "content": "package lecho.lib.hellocharts.animation;\n\npublic class DummyChartAnimationListener implements ChartAnimationListener {\n\n    @Override\n    public void onAnimationStarted() {\n        // do nothing\n\n    }\n\n    @Override\n    public void onAnimationFinished() {\n        // do nothing\n\n    }\n\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/animation/PieChartRotationAnimator.java",
    "content": "package lecho.lib.hellocharts.animation;\n\npublic interface PieChartRotationAnimator {\n\n    public static final int FAST_ANIMATION_DURATION = 200;\n\n    public void startAnimation(float startAngle, float angleToRotate);\n\n    public void cancelAnimation();\n\n    public boolean isAnimationStarted();\n\n    public void setChartAnimationListener(ChartAnimationListener animationListener);\n\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/animation/PieChartRotationAnimatorV14.java",
    "content": "package lecho.lib.hellocharts.animation;\n\nimport android.animation.Animator;\nimport android.animation.Animator.AnimatorListener;\nimport android.animation.ValueAnimator;\nimport android.animation.ValueAnimator.AnimatorUpdateListener;\nimport android.annotation.SuppressLint;\n\nimport lecho.lib.hellocharts.view.PieChartView;\n\n@SuppressLint(\"NewApi\")\npublic class PieChartRotationAnimatorV14 implements PieChartRotationAnimator, AnimatorListener, AnimatorUpdateListener {\n    private final PieChartView chart;\n    private ValueAnimator animator;\n    private float startRotation = 0;\n    private float targetRotation = 0;\n    private ChartAnimationListener animationListener = new DummyChartAnimationListener();\n\n    public PieChartRotationAnimatorV14(PieChartView chart) {\n        this(chart, FAST_ANIMATION_DURATION);\n    }\n\n    public PieChartRotationAnimatorV14(PieChartView chart, long duration) {\n        this.chart = chart;\n        animator = ValueAnimator.ofFloat(0.0f, 1.0f);\n        animator.setDuration(duration);\n        animator.addListener(this);\n        animator.addUpdateListener(this);\n    }\n\n    @Override\n    public void startAnimation(float startRotation, float targetRotation) {\n        this.startRotation = (startRotation % 360 + 360) % 360;\n        this.targetRotation = (targetRotation % 360 + 360) % 360;\n        animator.start();\n    }\n\n    @Override\n    public void cancelAnimation() {\n        animator.cancel();\n    }\n\n    @Override\n    public void onAnimationUpdate(ValueAnimator animation) {\n        float scale = animation.getAnimatedFraction();\n        float rotation = startRotation + (targetRotation - startRotation) * scale;\n        rotation = (rotation % 360 + 360) % 360;\n        chart.setChartRotation((int) rotation, false);\n    }\n\n    @Override\n    public void onAnimationCancel(Animator animation) {\n    }\n\n    @Override\n    public void onAnimationEnd(Animator animation) {\n        chart.setChartRotation((int) targetRotation, false);\n        animationListener.onAnimationFinished();\n    }\n\n    @Override\n    public void onAnimationRepeat(Animator animation) {\n    }\n\n    @Override\n    public void onAnimationStart(Animator animation) {\n        animationListener.onAnimationStarted();\n    }\n\n    @Override\n    public boolean isAnimationStarted() {\n        return animator.isStarted();\n    }\n\n    @Override\n    public void setChartAnimationListener(ChartAnimationListener animationListener) {\n        if (null == animationListener) {\n            this.animationListener = new DummyChartAnimationListener();\n        } else {\n            this.animationListener = animationListener;\n        }\n    }\n\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/animation/PieChartRotationAnimatorV8.java",
    "content": "package lecho.lib.hellocharts.animation;\n\nimport android.os.Handler;\nimport android.os.SystemClock;\nimport android.view.animation.AccelerateDecelerateInterpolator;\nimport android.view.animation.Interpolator;\n\nimport lecho.lib.hellocharts.view.PieChartView;\n\npublic class PieChartRotationAnimatorV8 implements PieChartRotationAnimator {\n\n    final PieChartView chart;\n    final long duration;\n    final Handler handler;\n    final Interpolator interpolator = new AccelerateDecelerateInterpolator();\n    long start;\n    boolean isAnimationStarted = false;\n    private float startRotation = 0;\n    private float targetRotation = 0;\n    private ChartAnimationListener animationListener = new DummyChartAnimationListener();\n    private final Runnable runnable = new Runnable() {\n\n        @Override\n        public void run() {\n            long elapsed = SystemClock.uptimeMillis() - start;\n            if (elapsed > duration) {\n                isAnimationStarted = false;\n                handler.removeCallbacks(runnable);\n                chart.setChartRotation((int) targetRotation, false);\n                animationListener.onAnimationFinished();\n                return;\n            }\n            float scale = Math.min(interpolator.getInterpolation((float) elapsed / duration), 1);\n            float rotation = startRotation + (targetRotation - startRotation) * scale;\n            rotation = (rotation % 360 + 360) % 360;\n            chart.setChartRotation((int) rotation, false);\n            handler.postDelayed(this, 16);\n        }\n    };\n\n    public PieChartRotationAnimatorV8(PieChartView chart) {\n        this(chart, FAST_ANIMATION_DURATION);\n    }\n\n    public PieChartRotationAnimatorV8(PieChartView chart, long duration) {\n        this.chart = chart;\n        this.duration = duration;\n        this.handler = new Handler();\n    }\n\n    @Override\n    public void startAnimation(float startRotation, float targetRotation) {\n        this.startRotation = (startRotation % 360 + 360) % 360;\n        this.targetRotation = (targetRotation % 360 + 360) % 360;\n        isAnimationStarted = true;\n        animationListener.onAnimationStarted();\n        start = SystemClock.uptimeMillis();\n        handler.post(runnable);\n    }\n\n    @Override\n    public void cancelAnimation() {\n        isAnimationStarted = false;\n        handler.removeCallbacks(runnable);\n        chart.setChartRotation((int) targetRotation, false);\n        animationListener.onAnimationFinished();\n    }\n\n    @Override\n    public boolean isAnimationStarted() {\n        return isAnimationStarted;\n    }\n\n    @Override\n    public void setChartAnimationListener(ChartAnimationListener animationListener) {\n        if (null == animationListener) {\n            this.animationListener = new DummyChartAnimationListener();\n        } else {\n            this.animationListener = animationListener;\n        }\n    }\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/computator/ChartComputator.java",
    "content": "package lecho.lib.hellocharts.computator;\n\nimport android.graphics.Point;\nimport android.graphics.PointF;\nimport android.graphics.Rect;\n\nimport lecho.lib.hellocharts.listener.DummyVieportChangeListener;\nimport lecho.lib.hellocharts.listener.ViewportChangeListener;\nimport lecho.lib.hellocharts.model.Viewport;\n\n/**\n * Computes raw points coordinates(in pixels), holds content area dimensions and chart viewport.\n */\npublic class ChartComputator {\n\n    /**\n     * Maximum chart zoom.\n     */\n    protected static final float DEFAULT_MAXIMUM_ZOOM = 20f;\n    protected float maxZoom = DEFAULT_MAXIMUM_ZOOM;\n    protected int chartWidth;\n    protected int chartHeight;\n    //contentRectMinusAllMargins <= contentRectMinusAxesMargins <= maxContentRect\n    protected Rect contentRectMinusAllMargins = new Rect();\n    protected Rect contentRectMinusAxesMargins = new Rect();\n    protected Rect maxContentRect = new Rect();\n    /**\n     * This rectangle represents the currently visible chart values ranges. The currently visible chart X values are\n     * from this rectangle's left to its right. The currently visible chart Y values are from this rectangle's top to\n     * its bottom.\n     */\n    protected Viewport currentViewport = new Viewport();\n    protected Viewport maxViewport = new Viewport();\n    protected float minViewportWidth;\n    protected float minViewportHeight;\n    /**\n     * Warning! Viewport listener is disabled for all charts beside preview charts to avoid additional method calls\n     * during animations.\n     */\n    protected ViewportChangeListener viewportChangeListener = new DummyVieportChangeListener();\n\n    /**\n     * Calculates available width and height. Should be called when chart dimensions change. ContentRect is relative to\n     * chart view not the device's screen.\n     */\n    public void setContentRect(int width, int height, int paddingLeft, int paddingTop, int paddingRight,\n                               int paddingBottom) {\n        chartWidth = width;\n        chartHeight = height;\n        maxContentRect.set(paddingLeft, paddingTop, width - paddingRight, height - paddingBottom);\n        contentRectMinusAxesMargins.set(maxContentRect);\n        contentRectMinusAllMargins.set(maxContentRect);\n    }\n\n    public void resetContentRect() {\n        contentRectMinusAxesMargins.set(maxContentRect);\n        contentRectMinusAllMargins.set(maxContentRect);\n    }\n\n    public void insetContentRect(int deltaLeft, int deltaTop, int deltaRight, int deltaBottom) {\n        contentRectMinusAxesMargins.left = contentRectMinusAxesMargins.left + deltaLeft;\n        contentRectMinusAxesMargins.top = contentRectMinusAxesMargins.top + deltaTop;\n        contentRectMinusAxesMargins.right = contentRectMinusAxesMargins.right - deltaRight;\n        contentRectMinusAxesMargins.bottom = contentRectMinusAxesMargins.bottom - deltaBottom;\n\n        insetContentRectByInternalMargins(deltaLeft, deltaTop, deltaRight, deltaBottom);\n    }\n\n    public void insetContentRectByInternalMargins(int deltaLeft, int deltaTop, int deltaRight, int deltaBottom) {\n        contentRectMinusAllMargins.left = contentRectMinusAllMargins.left + deltaLeft;\n        contentRectMinusAllMargins.top = contentRectMinusAllMargins.top + deltaTop;\n        contentRectMinusAllMargins.right = contentRectMinusAllMargins.right - deltaRight;\n        contentRectMinusAllMargins.bottom = contentRectMinusAllMargins.bottom - deltaBottom;\n    }\n\n    /**\n     * Checks if new viewport doesn't exceed max available viewport.\n     */\n    public void constrainViewport(float left, float top, float right, float bottom) {\n\n        if (right - left < minViewportWidth) {\n            // Minimum width - constrain horizontal zoom!\n            right = left + minViewportWidth;\n            if (left < maxViewport.left) {\n                left = maxViewport.left;\n                right = left + minViewportWidth;\n            } else if (right > maxViewport.right) {\n                right = maxViewport.right;\n                left = right - minViewportWidth;\n            }\n        }\n\n        if (top - bottom < minViewportHeight) {\n            // Minimum height - constrain vertical zoom!\n            bottom = top - minViewportHeight;\n            if (top > maxViewport.top) {\n                top = maxViewport.top;\n                bottom = top - minViewportHeight;\n            } else if (bottom < maxViewport.bottom) {\n                bottom = maxViewport.bottom;\n                top = bottom + minViewportHeight;\n            }\n        }\n\n        currentViewport.left = Math.max(maxViewport.left, left);\n        currentViewport.top = Math.min(maxViewport.top, top);\n        currentViewport.right = Math.min(maxViewport.right, right);\n        currentViewport.bottom = Math.max(maxViewport.bottom, bottom);\n\n        viewportChangeListener.onViewportChanged(currentViewport);\n    }\n\n    /**\n     * Sets the current viewport (defined by {@link #currentViewport}) to the given X and Y positions.\n     */\n    public void setViewportTopLeft(float left, float top) {\n        /**\n         * Constrains within the scroll range. The scroll range is simply the viewport extremes (AXIS_X_MAX,\n         * etc.) minus\n         * the viewport size. For example, if the extrema were 0 and 10, and the viewport size was 2, the scroll range\n         * would be 0 to 8.\n         */\n\n        final float curWidth = currentViewport.width();\n        final float curHeight = currentViewport.height();\n\n        left = Math.max(maxViewport.left, Math.min(left, maxViewport.right - curWidth));\n        top = Math.max(maxViewport.bottom + curHeight, Math.min(top, maxViewport.top));\n        constrainViewport(left, top, left + curWidth, top - curHeight);\n    }\n\n    /**\n     * Translates chart value into raw pixel value. Returned value is absolute pixel X coordinate. If this method\n     * return\n     * 0 that means left most pixel of the screen.\n     */\n    public float computeRawX(float valueX) {\n        // TODO: (contentRectMinusAllMargins.width() / currentViewport.width()) can be recalculated only when viewport\n        // change.\n        final float pixelOffset = (valueX - currentViewport.left) * (contentRectMinusAllMargins.width() /\n                currentViewport.width());\n        return contentRectMinusAllMargins.left + pixelOffset;\n    }\n\n    /**\n     * Translates chart value into raw pixel value. Returned value is absolute pixel Y coordinate. If this method\n     * return\n     * 0 that means top most pixel of the screen.\n     */\n    public float computeRawY(float valueY) {\n        final float pixelOffset = (valueY - currentViewport.bottom) * (contentRectMinusAllMargins.height() /\n                currentViewport.height());\n        return contentRectMinusAllMargins.bottom - pixelOffset;\n    }\n\n    /**\n     * Translates viewport distance int pixel distance for X coordinates.\n     */\n    public float computeRawDistanceX(float distance) {\n        return distance * (contentRectMinusAllMargins.width() / currentViewport.width());\n    }\n\n    /**\n     * Translates viewport distance int pixel distance for X coordinates.\n     */\n    public float computeRawDistanceY(float distance) {\n        return distance * (contentRectMinusAllMargins.height() / currentViewport.height());\n    }\n\n    /**\n     * Finds the chart point (i.e. within the chart's domain and range) represented by the given pixel coordinates, if\n     * that pixel is within the chart region described by {@link #contentRectMinusAllMargins}. If the point is found,\n     * the \"dest\"\n     * argument is set to the point and this function returns true. Otherwise, this function returns false and\n     * \"dest\" is\n     * unchanged.\n     */\n    public boolean rawPixelsToDataPoint(float x, float y, PointF dest) {\n        if (!contentRectMinusAllMargins.contains((int) x, (int) y)) {\n            return false;\n        }\n        dest.set(currentViewport.left + (x - contentRectMinusAllMargins.left) * currentViewport.width() /\n                        contentRectMinusAllMargins.width(),\n                currentViewport.bottom + (y - contentRectMinusAllMargins.bottom) * currentViewport.height() /\n                        -contentRectMinusAllMargins.height());\n        return true;\n    }\n\n    /**\n     * Computes the current scrollable surface size, in pixels. For example, if the entire chart area is visible, this\n     * is simply the current size of {@link #contentRectMinusAllMargins}. If the chart is zoomed in 200% in both\n     * directions, the\n     * returned size will be twice as large horizontally and vertically.\n     */\n    public void computeScrollSurfaceSize(Point out) {\n        out.set((int) (maxViewport.width() * contentRectMinusAllMargins.width() / currentViewport.width()),\n                (int) (maxViewport.height() * contentRectMinusAllMargins.height() / currentViewport.height()));\n    }\n\n    /**\n     * Check if given coordinates lies inside contentRectMinusAllMargins.\n     */\n    public boolean isWithinContentRect(float x, float y, float precision) {\n        if (x >= contentRectMinusAllMargins.left - precision && x <= contentRectMinusAllMargins.right + precision) {\n            if (y <= contentRectMinusAllMargins.bottom + precision && y >= contentRectMinusAllMargins.top -\n                    precision) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * Returns content rectangle in pixels.\n     *\n     * @see #setContentRect(int, int, int, int, int, int)\n     */\n    public Rect getContentRectMinusAllMargins() {\n        return contentRectMinusAllMargins;\n    }\n\n    /**\n     * Returns content rectangle with chart internal margins, for example for LineChart contentRectMinusAxesMargins is\n     * bigger\n     * than contentRectMinusAllMargins by point radius, thanks to that points are not cut on edges.\n     *\n     * @see #setContentRect(int, int, int, int, int, int)\n     */\n    public Rect getContentRectMinusAxesMargins() {\n        return contentRectMinusAxesMargins;\n    }\n\n    /**\n     * Returns current chart viewport, returned object is mutable but should not be modified.\n     *\n     * @return\n     */\n    public Viewport getCurrentViewport() {\n        return currentViewport;\n    }\n\n    /**\n     * Set current viewport to the same values as viewport passed in parameter. This method use deep copy so parameter\n     * can be safely modified later. Current viewport must be equal or smaller than maximum viewport.\n     *\n     * @param viewport\n     */\n    public void setCurrentViewport(Viewport viewport) {\n        constrainViewport(viewport.left, viewport.top, viewport.right, viewport.bottom);\n    }\n\n    /**\n     * Set new values for curent viewport, that will change what part of chart is visible. Current viewport must be\n     * equal or smaller than maximum viewport.\n     */\n    public void setCurrentViewport(float left, float top, float right, float bottom) {\n        constrainViewport(left, top, right, bottom);\n    }\n\n    /**\n     * Returns maximum viewport - values ranges extremes.\n     */\n    public Viewport getMaximumViewport() {\n        return maxViewport;\n    }\n\n    /**\n     * Set maximum viewport to the same values as viewport passed in parameter. This method use deep copy so parameter\n     * can be safely modified later.\n     *\n     * @param maxViewport\n     */\n    public void setMaxViewport(Viewport maxViewport) {\n        setMaxViewport(maxViewport.left, maxViewport.top, maxViewport.right, maxViewport.bottom);\n    }\n\n    /**\n     * Set new values for maximum viewport, that will change what part of chart is visible.\n     */\n    public void setMaxViewport(float left, float top, float right, float bottom) {\n        this.maxViewport.set(left, top, right, bottom);\n        computeMinimumWidthAndHeight();\n    }\n\n    /**\n     * Returns viewport for visible part of chart, for most charts it is equal to current viewport.\n     *\n     * @return\n     */\n    public Viewport getVisibleViewport() {\n        return currentViewport;\n    }\n\n    public void setVisibleViewport(Viewport visibleViewport) {\n        setCurrentViewport(visibleViewport);\n    }\n\n    public float getMinimumViewportWidth() {\n        return minViewportWidth;\n    }\n\n    public float getMinimumViewportHeight() {\n        return minViewportHeight;\n    }\n\n    public void setViewportChangeListener(ViewportChangeListener viewportChangeListener) {\n        if (null == viewportChangeListener) {\n            this.viewportChangeListener = new DummyVieportChangeListener();\n        } else {\n            this.viewportChangeListener = viewportChangeListener;\n        }\n    }\n\n    public int getChartWidth() {\n        return chartWidth;\n    }\n\n    public int getChartHeight() {\n        return chartHeight;\n    }\n\n    public float getMaxZoom() {\n        return maxZoom;\n    }\n\n    /**\n     * Set maximum zoom level, default is 20.\n     *\n     * @param maxZoom\n     */\n    public void setMaxZoom(float maxZoom) {\n        if (maxZoom < 1) {\n            maxZoom = 1;\n        }\n\n        this.maxZoom = maxZoom;\n\n        computeMinimumWidthAndHeight();\n\n        setCurrentViewport(currentViewport);\n\n    }\n\n    private void computeMinimumWidthAndHeight() {\n        minViewportWidth = this.maxViewport.width() / maxZoom;\n        minViewportHeight = this.maxViewport.height() / maxZoom;\n    }\n\n}"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/computator/PreviewChartComputator.java",
    "content": "package lecho.lib.hellocharts.computator;\n\nimport lecho.lib.hellocharts.model.Viewport;\n\n/**\n * Version of ChartComputator for preview charts. It always uses maxViewport as visible viewport and currentViewport as\n * preview area.\n */\npublic class PreviewChartComputator extends ChartComputator {\n\n    public float computeRawX(float valueX) {\n        final float pixelOffset = (valueX - maxViewport.left) * (contentRectMinusAllMargins.width() / maxViewport\n                .width());\n        return contentRectMinusAllMargins.left + pixelOffset;\n    }\n\n    public float computeRawY(float valueY) {\n        final float pixelOffset = (valueY - maxViewport.bottom) * (contentRectMinusAllMargins.height() / maxViewport\n                .height());\n        return contentRectMinusAllMargins.bottom - pixelOffset;\n    }\n\n    public Viewport getVisibleViewport() {\n        return maxViewport;\n    }\n\n    public void setVisibleViewport(Viewport visibleViewport) {\n        setMaxViewport(visibleViewport);\n    }\n\n    public void constrainViewport(float left, float top, float right, float bottom) {\n        super.constrainViewport(left, top, right, bottom);\n        viewportChangeListener.onViewportChanged(currentViewport);\n    }\n\n}"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/formatter/AxisValueFormatter.java",
    "content": "package lecho.lib.hellocharts.formatter;\n\n\nimport lecho.lib.hellocharts.model.AxisValue;\n\npublic interface AxisValueFormatter {\n\n    /**\n     * Formats AxisValue for manual(custom) axis. Result is stored in (output) formattedValue array. Method\n     * returns number of chars of formatted value. The formatted value starts at index [formattedValue.length -\n     * charsNumber] and ends at index [formattedValue.length-1].\n     */\n    public int formatValueForManualAxis(char[] formattedValue, AxisValue axisValue);\n\n    /**\n     * Used only for auto-generated axes. If you are not going to use your implementation for aut-generated axes you can\n     * skip implementation of this method and just return 0. SFormats values with given number of digits after\n     * decimal separator. Result is stored in given array. Method returns number of chars for formatted value. The\n     * formatted value starts at index [formattedValue.length - charsNumber] and ends at index [formattedValue\n     * .length-1].\n     */\n    public int formatValueForAutoGeneratedAxis(char[] formattedValue, float value, int autoDecimalDigits);\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/formatter/BubbleChartValueFormatter.java",
    "content": "package lecho.lib.hellocharts.formatter;\n\nimport lecho.lib.hellocharts.model.BubbleValue;\n\npublic interface BubbleChartValueFormatter {\n\n    public int formatChartValue(char[] formattedValue, BubbleValue value);\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/formatter/ColumnChartValueFormatter.java",
    "content": "package lecho.lib.hellocharts.formatter;\n\nimport lecho.lib.hellocharts.model.SubcolumnValue;\n\npublic interface ColumnChartValueFormatter {\n\n    public int formatChartValue(char[] formattedValue, SubcolumnValue value);\n\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/formatter/LineChartValueFormatter.java",
    "content": "package lecho.lib.hellocharts.formatter;\n\n\nimport lecho.lib.hellocharts.model.PointValue;\n\npublic interface LineChartValueFormatter {\n\n    public int formatChartValue(char[] formattedValue, PointValue value);\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/formatter/PieChartValueFormatter.java",
    "content": "package lecho.lib.hellocharts.formatter;\n\nimport lecho.lib.hellocharts.model.SliceValue;\n\npublic interface PieChartValueFormatter {\n\n    public int formatChartValue(char[] formattedValue, SliceValue value);\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/formatter/SimpleAxisValueFormatter.java",
    "content": "package lecho.lib.hellocharts.formatter;\n\nimport lecho.lib.hellocharts.model.AxisValue;\n\npublic class SimpleAxisValueFormatter implements AxisValueFormatter {\n\n    private ValueFormatterHelper valueFormatterHelper = new ValueFormatterHelper();\n\n    public SimpleAxisValueFormatter() {\n        valueFormatterHelper.determineDecimalSeparator();\n    }\n\n    public SimpleAxisValueFormatter(int decimalDigitsNumber) {\n        this();\n        valueFormatterHelper.setDecimalDigitsNumber(decimalDigitsNumber);\n    }\n\n    @Override\n    public int formatValueForManualAxis(char[] formattedValue, AxisValue axisValue) {\n        final int charsNumber = valueFormatterHelper.formatFloatValueWithPrependedAndAppendedText(formattedValue,\n                axisValue.getValue(), axisValue.getLabelAsChars());\n        return charsNumber;\n    }\n\n    @Override\n    public int formatValueForAutoGeneratedAxis(char[] formattedValue, float value, int autoDecimalDigits) {\n        final int charsNumber = valueFormatterHelper.formatFloatValueWithPrependedAndAppendedText(formattedValue,\n                value, autoDecimalDigits);\n        return charsNumber;\n    }\n\n    public int getDecimalDigitsNumber() {\n        return valueFormatterHelper.getDecimalDigitsNumber();\n    }\n\n    public SimpleAxisValueFormatter setDecimalDigitsNumber(int decimalDigitsNumber) {\n        valueFormatterHelper.setDecimalDigitsNumber(decimalDigitsNumber);\n        return this;\n    }\n\n    public char[] getAppendedText() {\n        return valueFormatterHelper.getAppendedText();\n    }\n\n    public SimpleAxisValueFormatter setAppendedText(char[] appendedText) {\n        valueFormatterHelper.setAppendedText(appendedText);\n        return this;\n    }\n\n    public char[] getPrependedText() {\n        return valueFormatterHelper.getPrependedText();\n    }\n\n    public SimpleAxisValueFormatter setPrependedText(char[] prependedText) {\n        valueFormatterHelper.setPrependedText(prependedText);\n        return this;\n    }\n\n    public char getDecimalSeparator() {\n        return valueFormatterHelper.getDecimalSeparator();\n    }\n\n    public SimpleAxisValueFormatter setDecimalSeparator(char decimalSeparator) {\n        valueFormatterHelper.setDecimalSeparator(decimalSeparator);\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/formatter/SimpleBubbleChartValueFormatter.java",
    "content": "package lecho.lib.hellocharts.formatter;\n\nimport lecho.lib.hellocharts.model.BubbleValue;\n\n\npublic class SimpleBubbleChartValueFormatter implements BubbleChartValueFormatter {\n\n    private ValueFormatterHelper valueFormatterHelper = new ValueFormatterHelper();\n\n    public SimpleBubbleChartValueFormatter() {\n        valueFormatterHelper.determineDecimalSeparator();\n    }\n\n    public SimpleBubbleChartValueFormatter(int decimalDigitsNumber) {\n        this();\n        valueFormatterHelper.setDecimalDigitsNumber(decimalDigitsNumber);\n    }\n\n    @Override\n    public int formatChartValue(char[] formattedValue, BubbleValue value) {\n        return valueFormatterHelper.formatFloatValueWithPrependedAndAppendedText(formattedValue, value.getZ(), value\n                .getLabelAsChars());\n    }\n\n    public int getDecimalDigitsNumber() {\n        return valueFormatterHelper.getDecimalDigitsNumber();\n    }\n\n    public SimpleBubbleChartValueFormatter setDecimalDigitsNumber(int decimalDigitsNumber) {\n        valueFormatterHelper.setDecimalDigitsNumber(decimalDigitsNumber);\n        return this;\n    }\n\n    public char[] getAppendedText() {\n        return valueFormatterHelper.getAppendedText();\n    }\n\n    public SimpleBubbleChartValueFormatter setAppendedText(char[] appendedText) {\n        valueFormatterHelper.setAppendedText(appendedText);\n        return this;\n    }\n\n    public char[] getPrependedText() {\n        return valueFormatterHelper.getPrependedText();\n    }\n\n    public SimpleBubbleChartValueFormatter setPrependedText(char[] prependedText) {\n        valueFormatterHelper.setPrependedText(prependedText);\n        return this;\n    }\n\n    public char getDecimalSeparator() {\n        return valueFormatterHelper.getDecimalSeparator();\n    }\n\n    public SimpleBubbleChartValueFormatter setDecimalSeparator(char decimalSeparator) {\n        valueFormatterHelper.setDecimalSeparator(decimalSeparator);\n        return this;\n    }\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/formatter/SimpleColumnChartValueFormatter.java",
    "content": "package lecho.lib.hellocharts.formatter;\n\nimport lecho.lib.hellocharts.model.SubcolumnValue;\n\npublic class SimpleColumnChartValueFormatter implements ColumnChartValueFormatter {\n\n    private ValueFormatterHelper valueFormatterHelper = new ValueFormatterHelper();\n\n    public SimpleColumnChartValueFormatter() {\n        valueFormatterHelper.determineDecimalSeparator();\n    }\n\n    public SimpleColumnChartValueFormatter(int decimalDigitsNumber) {\n        this();\n        valueFormatterHelper.setDecimalDigitsNumber(decimalDigitsNumber);\n    }\n\n    @Override\n    public int formatChartValue(char[] formattedValue, SubcolumnValue value) {\n        return valueFormatterHelper.formatFloatValueWithPrependedAndAppendedText(formattedValue, value.getValue(),\n                value.getLabelAsChars());\n    }\n\n    public int getDecimalDigitsNumber() {\n        return valueFormatterHelper.getDecimalDigitsNumber();\n    }\n\n    public SimpleColumnChartValueFormatter setDecimalDigitsNumber(int decimalDigitsNumber) {\n        valueFormatterHelper.setDecimalDigitsNumber(decimalDigitsNumber);\n        return this;\n    }\n\n    public char[] getAppendedText() {\n        return valueFormatterHelper.getAppendedText();\n    }\n\n    public SimpleColumnChartValueFormatter setAppendedText(char[] appendedText) {\n        valueFormatterHelper.setAppendedText(appendedText);\n        return this;\n    }\n\n    public char[] getPrependedText() {\n        return valueFormatterHelper.getPrependedText();\n    }\n\n    public SimpleColumnChartValueFormatter setPrependedText(char[] prependedText) {\n        valueFormatterHelper.setPrependedText(prependedText);\n        return this;\n    }\n\n    public char getDecimalSeparator() {\n        return valueFormatterHelper.getDecimalSeparator();\n    }\n\n    public SimpleColumnChartValueFormatter setDecimalSeparator(char decimalSeparator) {\n        valueFormatterHelper.setDecimalSeparator(decimalSeparator);\n        return this;\n    }\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/formatter/SimpleLineChartValueFormatter.java",
    "content": "package lecho.lib.hellocharts.formatter;\n\nimport lecho.lib.hellocharts.model.PointValue;\n\npublic class SimpleLineChartValueFormatter implements LineChartValueFormatter {\n\n    private ValueFormatterHelper valueFormatterHelper = new ValueFormatterHelper();\n\n    public SimpleLineChartValueFormatter() {\n        valueFormatterHelper.determineDecimalSeparator();\n    }\n\n    public SimpleLineChartValueFormatter(int decimalDigitsNumber) {\n        this();\n        valueFormatterHelper.setDecimalDigitsNumber(decimalDigitsNumber);\n    }\n\n    @Override\n    public int formatChartValue(char[] formattedValue, PointValue value) {\n        return valueFormatterHelper.formatFloatValueWithPrependedAndAppendedText(formattedValue, value.getY(), value\n                .getLabelAsChars());\n    }\n\n    public int getDecimalDigitsNumber() {\n        return valueFormatterHelper.getDecimalDigitsNumber();\n    }\n\n    public SimpleLineChartValueFormatter setDecimalDigitsNumber(int decimalDigitsNumber) {\n        valueFormatterHelper.setDecimalDigitsNumber(decimalDigitsNumber);\n        return this;\n    }\n\n    public char[] getAppendedText() {\n        return valueFormatterHelper.getAppendedText();\n    }\n\n    public SimpleLineChartValueFormatter setAppendedText(char[] appendedText) {\n        valueFormatterHelper.setAppendedText(appendedText);\n        return this;\n    }\n\n    public char[] getPrependedText() {\n        return valueFormatterHelper.getPrependedText();\n    }\n\n    public SimpleLineChartValueFormatter setPrependedText(char[] prependedText) {\n        valueFormatterHelper.setPrependedText(prependedText);\n        return this;\n    }\n\n    public char getDecimalSeparator() {\n        return valueFormatterHelper.getDecimalSeparator();\n    }\n\n    public SimpleLineChartValueFormatter setDecimalSeparator(char decimalSeparator) {\n        valueFormatterHelper.setDecimalSeparator(decimalSeparator);\n        return this;\n    }\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/formatter/SimplePieChartValueFormatter.java",
    "content": "package lecho.lib.hellocharts.formatter;\n\nimport lecho.lib.hellocharts.model.SliceValue;\n\n\npublic class SimplePieChartValueFormatter implements PieChartValueFormatter {\n\n    private ValueFormatterHelper valueFormatterHelper = new ValueFormatterHelper();\n\n    public SimplePieChartValueFormatter() {\n        valueFormatterHelper.determineDecimalSeparator();\n    }\n\n    public SimplePieChartValueFormatter(int decimalDigitsNumber) {\n        this();\n        valueFormatterHelper.setDecimalDigitsNumber(decimalDigitsNumber);\n    }\n\n    @Override\n    public int formatChartValue(char[] formattedValue, SliceValue value) {\n        return valueFormatterHelper.formatFloatValueWithPrependedAndAppendedText(formattedValue, value.getValue(),\n                value.getLabelAsChars());\n    }\n\n    public int getDecimalDigitsNumber() {\n        return valueFormatterHelper.getDecimalDigitsNumber();\n    }\n\n    public SimplePieChartValueFormatter setDecimalDigitsNumber(int decimalDigitsNumber) {\n        valueFormatterHelper.setDecimalDigitsNumber(decimalDigitsNumber);\n        return this;\n    }\n\n    public char[] getAppendedText() {\n        return valueFormatterHelper.getAppendedText();\n    }\n\n    public SimplePieChartValueFormatter setAppendedText(char[] appendedText) {\n        valueFormatterHelper.setAppendedText(appendedText);\n        return this;\n    }\n\n    public char[] getPrependedText() {\n        return valueFormatterHelper.getPrependedText();\n    }\n\n    public SimplePieChartValueFormatter setPrependedText(char[] prependedText) {\n        valueFormatterHelper.setPrependedText(prependedText);\n        return this;\n    }\n\n    public char getDecimalSeparator() {\n        return valueFormatterHelper.getDecimalSeparator();\n    }\n\n    public SimplePieChartValueFormatter setDecimalSeparator(char decimalSeparator) {\n        valueFormatterHelper.setDecimalSeparator(decimalSeparator);\n        return this;\n    }\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/formatter/ValueFormatterHelper.java",
    "content": "package lecho.lib.hellocharts.formatter;\n\nimport android.util.Log;\n\nimport java.text.DecimalFormat;\nimport java.text.NumberFormat;\n\nimport lecho.lib.hellocharts.util.FloatUtils;\n\npublic class ValueFormatterHelper {\n    public static final int DEFAULT_DIGITS_NUMBER = 0;\n    private static final String TAG = \"ValueFormatterHelper\";\n    private int decimalDigitsNumber = Integer.MIN_VALUE;\n    private char[] appendedText = new char[0];\n    private char[] prependedText = new char[0];\n    private char decimalSeparator = '.';\n\n    public void determineDecimalSeparator() {\n        NumberFormat numberFormat = NumberFormat.getInstance();\n        if (numberFormat instanceof DecimalFormat) {\n            decimalSeparator = ((DecimalFormat) numberFormat).getDecimalFormatSymbols().getDecimalSeparator();\n        }\n    }\n\n    public int getDecimalDigitsNumber() {\n        return decimalDigitsNumber;\n    }\n\n    public ValueFormatterHelper setDecimalDigitsNumber(int decimalDigitsNumber) {\n        this.decimalDigitsNumber = decimalDigitsNumber;\n        return this;\n    }\n\n    public char[] getAppendedText() {\n        return appendedText;\n    }\n\n    public ValueFormatterHelper setAppendedText(char[] appendedText) {\n        if (null != appendedText) {\n            this.appendedText = appendedText;\n        }\n        return this;\n    }\n\n    public char[] getPrependedText() {\n        return prependedText;\n    }\n\n    public ValueFormatterHelper setPrependedText(char[] prependedText) {\n        if (null != prependedText) {\n            this.prependedText = prependedText;\n        }\n        return this;\n    }\n\n    public char getDecimalSeparator() {\n        return decimalSeparator;\n    }\n\n    public ValueFormatterHelper setDecimalSeparator(char decimalSeparator) {\n        char nullChar = '\\0';\n        if (nullChar != decimalSeparator) {\n            this.decimalSeparator = decimalSeparator;\n        }\n        return this;\n    }\n\n    /**\n     * Formats float value. Result is stored in (output) formattedValue array. Method\n     * returns number of chars of formatted value. The formatted value starts at index [formattedValue.length -\n     * charsNumber] and ends at index [formattedValue.length-1].\n     * Note: If label is not null it will be used as formattedValue instead of float value.\n     * Note: Parameter defaultDigitsNumber is used only if you didn't change decimalDigintsNumber value using\n     * method {@link #setDecimalDigitsNumber(int)}.\n     */\n    public int formatFloatValueWithPrependedAndAppendedText(char[] formattedValue, float value, int\n            defaultDigitsNumber, char[] label) {\n        if (null != label) {\n            // If custom label is not null use only name characters as formatted value.\n            // Copy label into formatted value array.\n            int labelLength = label.length;\n            if (labelLength > formattedValue.length) {\n                Log.w(TAG, \"Label length is larger than buffer size(64chars), some chars will be skipped!\");\n                labelLength = formattedValue.length;\n            }\n            System.arraycopy(label, 0, formattedValue, formattedValue.length - labelLength, labelLength);\n            return labelLength;\n        }\n\n        final int appliedDigitsNumber = getAppliedDecimalDigitsNumber(defaultDigitsNumber);\n        final int charsNumber = formatFloatValue(formattedValue, value, appliedDigitsNumber);\n        appendText(formattedValue);\n        prependText(formattedValue, charsNumber);\n        return charsNumber + getPrependedText().length + getAppendedText().length;\n    }\n\n    /**\n     * @see #formatFloatValueWithPrependedAndAppendedText(char[], float, int, char[])\n     */\n    public int formatFloatValueWithPrependedAndAppendedText(char[] formattedValue, float value, char[] label) {\n        return formatFloatValueWithPrependedAndAppendedText(formattedValue, value, DEFAULT_DIGITS_NUMBER, label);\n    }\n\n    /**\n     * @see #formatFloatValueWithPrependedAndAppendedText(char[], float, int, char[])\n     */\n    public int formatFloatValueWithPrependedAndAppendedText(char[] formattedValue, float value, int\n            defaultDigitsNumber) {\n        return formatFloatValueWithPrependedAndAppendedText(formattedValue, value, defaultDigitsNumber, null);\n    }\n\n    public int formatFloatValue(char[] formattedValue, float value, int decimalDigitsNumber) {\n        return FloatUtils.formatFloat(formattedValue, value, formattedValue.length - appendedText.length,\n                decimalDigitsNumber,\n                decimalSeparator);\n    }\n\n    public void appendText(char[] formattedValue) {\n        if (appendedText.length > 0) {\n            System.arraycopy(appendedText, 0, formattedValue, formattedValue.length - appendedText.length,\n                    appendedText.length);\n        }\n    }\n\n    public void prependText(char[] formattedValue, int charsNumber) {\n        if (prependedText.length > 0) {\n            System.arraycopy(prependedText, 0, formattedValue, formattedValue.length - charsNumber - appendedText.length\n                    - prependedText.length, prependedText.length);\n        }\n    }\n\n    public int getAppliedDecimalDigitsNumber(int defaultDigitsNumber) {\n        final int appliedDecimalDigitsNumber;\n        if (decimalDigitsNumber < 0) {\n            //When decimalDigitsNumber < 0 that means that user didn't set that value and defaultDigitsNumber should\n            // be used.\n            appliedDecimalDigitsNumber = defaultDigitsNumber;\n        } else {\n            appliedDecimalDigitsNumber = decimalDigitsNumber;\n        }\n        return appliedDecimalDigitsNumber;\n    }\n\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/gesture/ChartScroller.java",
    "content": "package lecho.lib.hellocharts.gesture;\n\nimport android.content.Context;\nimport android.graphics.Point;\nimport android.graphics.Rect;\nimport android.support.v4.widget.ScrollerCompat;\n\nimport lecho.lib.hellocharts.computator.ChartComputator;\nimport lecho.lib.hellocharts.model.Viewport;\n\n/**\n * Encapsulates scrolling functionality.\n */\npublic class ChartScroller {\n\n    private Viewport scrollerStartViewport = new Viewport(); // Used only for zooms and flings\n    private Point surfaceSizeBuffer = new Point();// Used for scroll and flings\n    private ScrollerCompat scroller;\n\n    public ChartScroller(Context context) {\n        scroller = ScrollerCompat.create(context);\n    }\n\n    public boolean startScroll(ChartComputator computator) {\n        scroller.abortAnimation();\n        scrollerStartViewport.set(computator.getCurrentViewport());\n        return true;\n    }\n\n    public boolean scroll(ChartComputator computator, float distanceX, float distanceY, ScrollResult scrollResult) {\n\n        // Scrolling uses math based on the viewport (as opposed to math using pixels). Pixel offset is the offset in\n        // screen pixels, while viewport offset is the offset within the current viewport. For additional\n        // information on\n        // surface sizes and pixel offsets, see the docs for {@link computeScrollSurfaceSize()}. For additional\n        // information about the viewport, see the comments for {@link mCurrentViewport}.\n\n        final Viewport maxViewport = computator.getMaximumViewport();\n        final Viewport visibleViewport = computator.getVisibleViewport();\n        final Viewport currentViewport = computator.getCurrentViewport();\n        final Rect contentRect = computator.getContentRectMinusAllMargins();\n\n        final boolean canScrollLeft = currentViewport.left > maxViewport.left;\n        final boolean canScrollRight = currentViewport.right < maxViewport.right;\n        final boolean canScrollTop = currentViewport.top < maxViewport.top;\n        final boolean canScrollBottom = currentViewport.bottom > maxViewport.bottom;\n\n        boolean canScrollX = false;\n        boolean canScrollY = false;\n\n        if (canScrollLeft && distanceX <= 0) {\n            canScrollX = true;\n        } else if (canScrollRight && distanceX >= 0) {\n            canScrollX = true;\n        }\n\n        if (canScrollTop && distanceY <= 0) {\n            canScrollY = true;\n        } else if (canScrollBottom && distanceY >= 0) {\n            canScrollY = true;\n        }\n\n        if (canScrollX || canScrollY) {\n\n            computator.computeScrollSurfaceSize(surfaceSizeBuffer);\n\n            float viewportOffsetX = distanceX * visibleViewport.width() / contentRect.width();\n            float viewportOffsetY = -distanceY * visibleViewport.height() / contentRect.height();\n\n            computator\n                    .setViewportTopLeft(currentViewport.left + viewportOffsetX, currentViewport.top + viewportOffsetY);\n        }\n\n        scrollResult.canScrollX = canScrollX;\n        scrollResult.canScrollY = canScrollY;\n\n        return canScrollX || canScrollY;\n    }\n\n    public boolean computeScrollOffset(ChartComputator computator) {\n        if (scroller.computeScrollOffset()) {\n            // The scroller isn't finished, meaning a fling or programmatic pan operation is\n            // currently active.\n\n            final Viewport maxViewport = computator.getMaximumViewport();\n\n            computator.computeScrollSurfaceSize(surfaceSizeBuffer);\n\n            final float currXRange = maxViewport.left + maxViewport.width() * scroller.getCurrX() /\n                    surfaceSizeBuffer.x;\n            final float currYRange = maxViewport.top - maxViewport.height() * scroller.getCurrY() /\n                    surfaceSizeBuffer.y;\n\n            computator.setViewportTopLeft(currXRange, currYRange);\n\n            return true;\n        }\n\n        return false;\n    }\n\n    public boolean fling(int velocityX, int velocityY, ChartComputator computator) {\n        // Flings use math in pixels (as opposed to math based on the viewport).\n        computator.computeScrollSurfaceSize(surfaceSizeBuffer);\n        scrollerStartViewport.set(computator.getCurrentViewport());\n\n        int startX = (int) (surfaceSizeBuffer.x * (scrollerStartViewport.left - computator.getMaximumViewport().left)\n                / computator.getMaximumViewport().width());\n        int startY = (int) (surfaceSizeBuffer.y * (computator.getMaximumViewport().top - scrollerStartViewport.top) /\n                computator.getMaximumViewport().height());\n\n        // TODO probably should be mScroller.forceFinish but ScrollerCompat doesn't have that method.\n        scroller.abortAnimation();\n\n        final int width = computator.getContentRectMinusAllMargins().width();\n        final int height = computator.getContentRectMinusAllMargins().height();\n        scroller.fling(startX, startY, velocityX, velocityY, 0, surfaceSizeBuffer.x - width + 1, 0,\n                surfaceSizeBuffer.y - height + 1);\n        return true;\n    }\n\n    public static class ScrollResult {\n        public boolean canScrollX;\n        public boolean canScrollY;\n    }\n\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/gesture/ChartTouchHandler.java",
    "content": "package lecho.lib.hellocharts.gesture;\n\nimport android.content.Context;\nimport android.view.GestureDetector;\nimport android.view.MotionEvent;\nimport android.view.ScaleGestureDetector;\nimport android.view.ViewParent;\n\nimport lecho.lib.hellocharts.computator.ChartComputator;\nimport lecho.lib.hellocharts.gesture.ChartScroller.ScrollResult;\nimport lecho.lib.hellocharts.model.SelectedValue;\nimport lecho.lib.hellocharts.renderer.ChartRenderer;\nimport lecho.lib.hellocharts.view.Chart;\n\n/**\n * Default touch handler for most charts. Handles value touch, scroll, fling and zoom.\n */\npublic class ChartTouchHandler {\n    protected GestureDetector gestureDetector;\n    protected ScaleGestureDetector scaleGestureDetector;\n    protected ChartScroller chartScroller;\n    protected ChartZoomer chartZoomer;\n    protected Chart chart;\n    protected ChartComputator computator;\n    protected ChartRenderer renderer;\n\n    protected boolean isZoomEnabled = true;\n    protected boolean isScrollEnabled = true;\n    protected boolean isValueTouchEnabled = true;\n    protected boolean isValueSelectionEnabled = false;\n\n    /**\n     * Used only for selection mode to avoid calling listener multiple times for the same selection. Small thing but it\n     * is more intuitive this way.\n     */\n    protected SelectedValue selectionModeOldValue = new SelectedValue();\n\n    protected SelectedValue selectedValue = new SelectedValue();\n    protected SelectedValue oldSelectedValue = new SelectedValue();\n\n    /**\n     * ViewParent to disallow touch events interception if chart is within scroll container.\n     */\n    protected ViewParent viewParent;\n\n    /**\n     * Type of scroll of container, horizontal or vertical.\n     */\n    protected ContainerScrollType containerScrollType;\n\n    public ChartTouchHandler(Context context, Chart chart) {\n        this.chart = chart;\n        this.computator = chart.getChartComputator();\n        this.renderer = chart.getChartRenderer();\n        gestureDetector = new GestureDetector(context, new ChartGestureListener());\n        scaleGestureDetector = new ScaleGestureDetector(context, new ChartScaleGestureListener());\n        chartScroller = new ChartScroller(context);\n        chartZoomer = new ChartZoomer(context, ZoomType.HORIZONTAL_AND_VERTICAL);\n    }\n\n    public void resetTouchHandler() {\n        this.computator = chart.getChartComputator();\n        this.renderer = chart.getChartRenderer();\n    }\n\n    /**\n     * Computes scroll and zoom using {@link ChartScroller} and {@link ChartZoomer}. This method returns true if\n     * scroll/zoom was computed and chart needs to be invalidated.\n     */\n    public boolean computeScroll() {\n        boolean needInvalidate = false;\n        if (isScrollEnabled && chartScroller.computeScrollOffset(computator)) {\n            needInvalidate = true;\n        }\n        if (isZoomEnabled && chartZoomer.computeZoom(computator)) {\n            needInvalidate = true;\n        }\n        return needInvalidate;\n    }\n\n    /**\n     * Handle chart touch event(gestures, clicks). Return true if gesture was handled and chart needs to be\n     * invalidated.\n     */\n    public boolean handleTouchEvent(MotionEvent event) {\n        boolean needInvalidate = false;\n\n        // TODO: detectors always return true, use class member needInvalidate instead local variable as workaround.\n        // This flag should be computed inside gesture listeners methods to avoid invalidation.\n        needInvalidate = gestureDetector.onTouchEvent(event);\n\n        needInvalidate = scaleGestureDetector.onTouchEvent(event) || needInvalidate;\n\n        if (isZoomEnabled && scaleGestureDetector.isInProgress()) {\n            // Special case: if view is inside scroll container and user is scaling disable touch interception by\n            // parent.\n            disallowParentInterceptTouchEvent();\n        }\n\n        if (isValueTouchEnabled) {\n            needInvalidate = computeTouch(event) || needInvalidate;\n        }\n\n        return needInvalidate;\n    }\n\n    /**\n     * Handle chart touch event(gestures, clicks). Return true if gesture was handled and chart needs to be\n     * invalidated.\n     * If viewParent and containerScrollType are not null chart can be scrolled and scaled within horizontal or\n     * vertical\n     * scroll container like ViewPager.\n     */\n    public boolean handleTouchEvent(MotionEvent event, ViewParent viewParent,\n                                    ContainerScrollType containerScrollType) {\n        this.viewParent = viewParent;\n        this.containerScrollType = containerScrollType;\n\n        return handleTouchEvent(event);\n    }\n\n    /**\n     * Disallow parent view from intercepting touch events. Use it for chart that is within some scroll container i.e.\n     * ViewPager.\n     */\n    private void disallowParentInterceptTouchEvent() {\n        if (null != viewParent) {\n            viewParent.requestDisallowInterceptTouchEvent(true);\n        }\n    }\n\n    /**\n     * Allow parent view to intercept touch events if chart cannot be scroll horizontally or vertically according to\n     * the\n     * current value of {@link #containerScrollType}.\n     */\n    private void allowParentInterceptTouchEvent(ScrollResult scrollResult) {\n        if (null != viewParent) {\n            if (ContainerScrollType.HORIZONTAL == containerScrollType && !scrollResult.canScrollX\n                    && !scaleGestureDetector.isInProgress()) {\n                viewParent.requestDisallowInterceptTouchEvent(false);\n            } else if (ContainerScrollType.VERTICAL == containerScrollType && !scrollResult.canScrollY\n                    && !scaleGestureDetector.isInProgress()) {\n                viewParent.requestDisallowInterceptTouchEvent(false);\n            }\n        }\n    }\n\n    private boolean computeTouch(MotionEvent event) {\n        boolean needInvalidate = false;\n        switch (event.getAction()) {\n            case MotionEvent.ACTION_DOWN:\n                boolean wasTouched = renderer.isTouched();\n                boolean isTouched = checkTouch(event.getX(), event.getY());\n                if (wasTouched != isTouched) {\n                    needInvalidate = true;\n\n                    if (isValueSelectionEnabled) {\n                        selectionModeOldValue.clear();\n                        if (wasTouched && !renderer.isTouched()) {\n                            chart.callTouchListener();\n                        }\n                    }\n                }\n                break;\n            case MotionEvent.ACTION_UP:\n                if (renderer.isTouched()) {\n                    if (checkTouch(event.getX(), event.getY())) {\n                        if (isValueSelectionEnabled) {\n                            // For selection mode call listener only if selected value changed,\n                            // that means that should be\n                            // first(selection) click on given value.\n                            if (!selectionModeOldValue.equals(selectedValue)) {\n                                selectionModeOldValue.set(selectedValue);\n                                chart.callTouchListener();\n                            }\n                        } else {\n                            chart.callTouchListener();\n                            renderer.clearTouch();\n                        }\n                    } else {\n                        renderer.clearTouch();\n                    }\n                    needInvalidate = true;\n                }\n                break;\n            case MotionEvent.ACTION_MOVE:\n                // If value was touched and now touch point is outside of value area - clear touch and invalidate, user\n                // probably moved finger away from given chart value.\n                if (renderer.isTouched()) {\n                    if (!checkTouch(event.getX(), event.getY())) {\n                        renderer.clearTouch();\n                        needInvalidate = true;\n                    }\n                }\n\n                break;\n            case MotionEvent.ACTION_CANCEL:\n                if (renderer.isTouched()) {\n                    renderer.clearTouch();\n                    needInvalidate = true;\n                }\n                break;\n        }\n        return needInvalidate;\n    }\n\n    private boolean checkTouch(float touchX, float touchY) {\n        oldSelectedValue.set(selectedValue);\n        selectedValue.clear();\n\n        if (renderer.checkTouch(touchX, touchY)) {\n            selectedValue.set(renderer.getSelectedValue());\n        }\n\n        // Check if selection is still on the same value, if not return false.\n        if (oldSelectedValue.isSet() && selectedValue.isSet() && !oldSelectedValue.equals(selectedValue)) {\n            return false;\n        } else {\n            return renderer.isTouched();\n        }\n    }\n\n    public boolean isZoomEnabled() {\n        return isZoomEnabled;\n    }\n\n    public void setZoomEnabled(boolean isZoomEnabled) {\n        this.isZoomEnabled = isZoomEnabled;\n\n    }\n\n    public boolean isScrollEnabled() {\n        return isScrollEnabled;\n    }\n\n    public void setScrollEnabled(boolean isScrollEnabled) {\n        this.isScrollEnabled = isScrollEnabled;\n    }\n\n    public ZoomType getZoomType() {\n        return chartZoomer.getZoomType();\n    }\n\n    public void setZoomType(ZoomType zoomType) {\n        chartZoomer.setZoomType(zoomType);\n    }\n\n    public boolean isValueTouchEnabled() {\n        return isValueTouchEnabled;\n    }\n\n    public void setValueTouchEnabled(boolean isValueTouchEnabled) {\n        this.isValueTouchEnabled = isValueTouchEnabled;\n    }\n\n    public boolean isValueSelectionEnabled() {\n        return isValueSelectionEnabled;\n    }\n\n    public void setValueSelectionEnabled(boolean isValueSelectionEnabled) {\n        this.isValueSelectionEnabled = isValueSelectionEnabled;\n    }\n\n    protected class ChartScaleGestureListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {\n\n        @Override\n        public boolean onScale(ScaleGestureDetector detector) {\n            if (isZoomEnabled) {\n                float scale = 2.0f - detector.getScaleFactor();\n                if (Float.isInfinite(scale)) {\n                    scale = 1;\n                }\n                return chartZoomer.scale(computator, detector.getFocusX(), detector.getFocusY(), scale);\n            }\n\n            return false;\n        }\n    }\n\n    protected class ChartGestureListener extends GestureDetector.SimpleOnGestureListener {\n\n        protected ScrollResult scrollResult = new ScrollResult();\n\n        @Override\n        public boolean onDown(MotionEvent e) {\n            if (isScrollEnabled) {\n\n                disallowParentInterceptTouchEvent();\n\n                return chartScroller.startScroll(computator);\n            }\n\n            return false;\n\n        }\n\n        @Override\n        public boolean onDoubleTap(MotionEvent e) {\n            if (isZoomEnabled) {\n                return chartZoomer.startZoom(e, computator);\n            }\n\n            return false;\n        }\n\n        @Override\n        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {\n            if (isScrollEnabled) {\n                boolean canScroll = chartScroller\n                        .scroll(computator, distanceX, distanceY, scrollResult);\n\n                allowParentInterceptTouchEvent(scrollResult);\n\n                return canScroll;\n            }\n\n            return false;\n\n        }\n\n        @Override\n        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {\n            if (isScrollEnabled) {\n                return chartScroller.fling((int) -velocityX, (int) -velocityY, computator);\n            }\n\n            return false;\n        }\n    }\n\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/gesture/ChartZoomer.java",
    "content": "package lecho.lib.hellocharts.gesture;\n\nimport android.content.Context;\nimport android.graphics.PointF;\nimport android.view.MotionEvent;\n\nimport lecho.lib.hellocharts.computator.ChartComputator;\nimport lecho.lib.hellocharts.model.Viewport;\n\n/**\n * Encapsulates zooming functionality.\n */\npublic class ChartZoomer {\n    public static final float ZOOM_AMOUNT = 0.25f;\n    private ZoomerCompat zoomer;\n    private ZoomType zoomType;\n    private PointF zoomFocalPoint = new PointF();// Used for double tap zoom\n    private PointF viewportFocus = new PointF();\n    private Viewport scrollerStartViewport = new Viewport(); // Used only for zooms and flings\n\n    public ChartZoomer(Context context, ZoomType zoomType) {\n        zoomer = new ZoomerCompat(context);\n        this.zoomType = zoomType;\n    }\n\n    public boolean startZoom(MotionEvent e, ChartComputator computator) {\n        zoomer.forceFinished(true);\n        scrollerStartViewport.set(computator.getCurrentViewport());\n        if (!computator.rawPixelsToDataPoint(e.getX(), e.getY(), zoomFocalPoint)) {\n            // Focus point is not within content area.\n            return false;\n        }\n        zoomer.startZoom(ZOOM_AMOUNT);\n        return true;\n    }\n\n    public boolean computeZoom(ChartComputator computator) {\n        if (zoomer.computeZoom()) {\n            // Performs the zoom since a zoom is in progress.\n            final float newWidth = (1.0f - zoomer.getCurrZoom()) * scrollerStartViewport.width();\n            final float newHeight = (1.0f - zoomer.getCurrZoom()) * scrollerStartViewport.height();\n            final float pointWithinViewportX = (zoomFocalPoint.x - scrollerStartViewport.left)\n                    / scrollerStartViewport.width();\n            final float pointWithinViewportY = (zoomFocalPoint.y - scrollerStartViewport.bottom)\n                    / scrollerStartViewport.height();\n\n            float left = zoomFocalPoint.x - newWidth * pointWithinViewportX;\n            float top = zoomFocalPoint.y + newHeight * (1 - pointWithinViewportY);\n            float right = zoomFocalPoint.x + newWidth * (1 - pointWithinViewportX);\n            float bottom = zoomFocalPoint.y - newHeight * pointWithinViewportY;\n            setCurrentViewport(computator, left, top, right, bottom);\n            return true;\n        }\n        return false;\n    }\n\n    public boolean scale(ChartComputator computator, float focusX, float focusY, float scale) {\n        /**\n         * Smaller viewport means bigger zoom so for zoomIn scale should have value <1, for zoomOout >1\n         */\n        final float newWidth = scale * computator.getCurrentViewport().width();\n        final float newHeight = scale * computator.getCurrentViewport().height();\n        if (!computator.rawPixelsToDataPoint(focusX, focusY, viewportFocus)) {\n            // Focus point is not within content area.\n            return false;\n        }\n\n        float left = viewportFocus.x - (focusX - computator.getContentRectMinusAllMargins().left)\n                * (newWidth / computator.getContentRectMinusAllMargins().width());\n        float top = viewportFocus.y + (focusY - computator.getContentRectMinusAllMargins().top)\n                * (newHeight / computator.getContentRectMinusAllMargins().height());\n        float right = left + newWidth;\n        float bottom = top - newHeight;\n        setCurrentViewport(computator, left, top, right, bottom);\n        return true;\n    }\n\n    private void setCurrentViewport(ChartComputator computator, float left, float top, float right, float bottom) {\n        Viewport currentViewport = computator.getCurrentViewport();\n        if (ZoomType.HORIZONTAL_AND_VERTICAL == zoomType) {\n            computator.setCurrentViewport(left, top, right, bottom);\n        } else if (ZoomType.HORIZONTAL == zoomType) {\n            computator.setCurrentViewport(left, currentViewport.top, right, currentViewport.bottom);\n        } else if (ZoomType.VERTICAL == zoomType) {\n            computator.setCurrentViewport(currentViewport.left, top, currentViewport.right, bottom);\n        }\n    }\n\n    public ZoomType getZoomType() {\n        return zoomType;\n    }\n\n    public void setZoomType(ZoomType zoomType) {\n        this.zoomType = zoomType;\n    }\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/gesture/ContainerScrollType.java",
    "content": "package lecho.lib.hellocharts.gesture;\n\n/**\n * Enum used to inform chart in which type of container it exists.\n */\npublic enum ContainerScrollType {\n    HORIZONTAL, VERTICAL\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/gesture/PieChartTouchHandler.java",
    "content": "package lecho.lib.hellocharts.gesture;\n\nimport android.content.Context;\nimport android.graphics.RectF;\nimport android.support.v4.widget.ScrollerCompat;\nimport android.view.GestureDetector;\nimport android.view.MotionEvent;\nimport android.view.ScaleGestureDetector;\n\nimport lecho.lib.hellocharts.view.PieChartView;\n\n/**\n * Touch handler for PieChart. It doesn't handle zoom and scroll like default ChartTouchHandler. Instead it uses\n * Scroller(ScrollerCompat) directly to compute PieChart rotation when user scroll. ChartScroller and ChartZoomer are\n * not really used here.\n */\npublic class PieChartTouchHandler extends ChartTouchHandler {\n    /**\n     * The initial fling velocity is divided by this amount.\n     */\n    public static final int FLING_VELOCITY_DOWNSCALE = 4;\n    /**\n     * PieChartTouchHandler uses its own instance of Scroller.\n     */\n    protected ScrollerCompat scroller;\n    /**\n     * Reference to PieChartView to use some methods specific for that kind of chart.\n     */\n    protected PieChartView pieChart;\n\n    private boolean isRotationEnabled = true;\n\n    public PieChartTouchHandler(Context context, PieChartView chart) {\n        super(context, chart);\n        pieChart = (PieChartView) chart;\n        scroller = ScrollerCompat.create(context);\n        gestureDetector = new GestureDetector(context, new ChartGestureListener());\n        scaleGestureDetector = new ScaleGestureDetector(context, new ChartScaleGestureListener());\n        isZoomEnabled = false;// Zoom is not supported by PieChart.\n    }\n\n    @Override\n    public boolean computeScroll() {\n        if (!isRotationEnabled) {\n            return false;\n        }\n        if (scroller.computeScrollOffset()) {\n            pieChart.setChartRotation(scroller.getCurrY(), false);\n            // pieChart.setChartRotation() will invalidate view so no need to return true;\n        }\n        return false;\n    }\n\n    @Override\n    public boolean handleTouchEvent(MotionEvent event) {\n        boolean needInvalidate = super.handleTouchEvent(event);\n\n        if (isRotationEnabled) {\n            needInvalidate = gestureDetector.onTouchEvent(event) || needInvalidate;\n        }\n        return needInvalidate;\n    }\n\n    public boolean isRotationEnabled() {\n        return isRotationEnabled;\n    }\n\n    public void setRotationEnabled(boolean isRotationEnabled) {\n        this.isRotationEnabled = isRotationEnabled;\n    }\n\n    private class ChartScaleGestureListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {\n\n        @Override\n        public boolean onScale(ScaleGestureDetector detector) {\n            // No scale for PieChart.\n            return false;\n        }\n    }\n\n    private class ChartGestureListener extends GestureDetector.SimpleOnGestureListener {\n        @Override\n        public boolean onDown(MotionEvent e) {\n            if (isRotationEnabled) {\n                scroller.abortAnimation();\n                return true;\n            }\n\n            return false;\n\n        }\n\n        @Override\n        public boolean onDoubleTap(MotionEvent e) {\n            return false;\n        }\n\n        @Override\n        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {\n            if (isRotationEnabled) {\n                // Set the pie rotation directly.\n                final RectF circleOval = pieChart.getCircleOval();\n                final float centerX = circleOval.centerX();\n                final float centerY = circleOval.centerY();\n                float scrollTheta = vectorToScalarScroll(distanceX, distanceY, e2.getX() - centerX, e2.getY() -\n                        centerY);\n                pieChart.setChartRotation(pieChart.getChartRotation() - (int) scrollTheta / FLING_VELOCITY_DOWNSCALE,\n                        false);\n                return true;\n            }\n\n            return false;\n        }\n\n        @Override\n        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {\n            if (isRotationEnabled) {\n                // Set up the Scroller for a fling\n                final RectF circleOval = pieChart.getCircleOval();\n                final float centerX = circleOval.centerX();\n                final float centerY = circleOval.centerY();\n                float scrollTheta = vectorToScalarScroll(velocityX, velocityY, e2.getX() - centerX, e2.getY() -\n                        centerY);\n                scroller.abortAnimation();\n                scroller.fling(0, (int) pieChart.getChartRotation(), 0, (int) scrollTheta / FLING_VELOCITY_DOWNSCALE,\n                        0, 0, Integer.MIN_VALUE, Integer.MAX_VALUE);\n                return true;\n            }\n\n            return false;\n        }\n\n        /**\n         * Helper method for translating (x,y) scroll vectors into scalar rotation of the pie.\n         *\n         * @param dx The x component of the current scroll vector.\n         * @param dy The y component of the current scroll vector.\n         * @param x  The x position of the current touch, relative to the pie center.\n         * @param y  The y position of the current touch, relative to the pie center.\n         * @return The scalar representing the change in angular position for this scroll.\n         */\n        private float vectorToScalarScroll(float dx, float dy, float x, float y) {\n            // get the length of the vector\n            float l = (float) Math.sqrt(dx * dx + dy * dy);\n\n            // decide if the scalar should be negative or positive by finding\n            // the dot product of the vector perpendicular to (x,y).\n            float crossX = -y;\n            float crossY = x;\n\n            float dot = (crossX * dx + crossY * dy);\n            float sign = Math.signum(dot);\n\n            return l * sign;\n        }\n    }\n\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/gesture/PreviewChartTouchHandler.java",
    "content": "package lecho.lib.hellocharts.gesture;\n\nimport android.content.Context;\nimport android.view.GestureDetector;\nimport android.view.MotionEvent;\nimport android.view.ScaleGestureDetector;\n\nimport lecho.lib.hellocharts.view.Chart;\n\n/**\n * Touch Handler for preview charts. It scroll and zoom only preview area, not all preview chart data.\n */\npublic class PreviewChartTouchHandler extends ChartTouchHandler {\n\n    public PreviewChartTouchHandler(Context context, Chart chart) {\n        super(context, chart);\n        gestureDetector = new GestureDetector(context, new PreviewChartGestureListener());\n        scaleGestureDetector = new ScaleGestureDetector(context, new ChartScaleGestureListener());\n\n        // Disable value touch and selection mode, by default not needed for preview chart.\n        isValueTouchEnabled = false;\n        isValueSelectionEnabled = false;\n    }\n\n    protected class ChartScaleGestureListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {\n\n        @Override\n        public boolean onScale(ScaleGestureDetector detector) {\n            if (isZoomEnabled) {\n                float scale = detector.getCurrentSpan() / detector.getPreviousSpan();\n                if (Float.isInfinite(scale)) {\n                    scale = 1;\n                }\n                return chartZoomer.scale(computator, detector.getFocusX(), detector.getFocusY(), scale);\n            }\n\n            return false;\n        }\n    }\n\n    protected class PreviewChartGestureListener extends ChartGestureListener {\n\n        @Override\n        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {\n            return super.onScroll(e1, e2, -distanceX, -distanceY);\n        }\n\n        @Override\n        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {\n            return super.onFling(e1, e2, -velocityX, -velocityY);\n        }\n    }\n\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/gesture/ZoomType.java",
    "content": "package lecho.lib.hellocharts.gesture;\n\npublic enum ZoomType {\n\n    HORIZONTAL, VERTICAL, HORIZONTAL_AND_VERTICAL;\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/gesture/ZoomerCompat.java",
    "content": "/*\n * Copyright 2013 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage lecho.lib.hellocharts.gesture;\n\nimport android.content.Context;\nimport android.os.SystemClock;\nimport android.view.animation.DecelerateInterpolator;\nimport android.view.animation.Interpolator;\n\n/**\n * A simple class that animates double-touch zoom gestures. Functionally similar to a {@link android.widget.Scroller}.\n */\npublic class ZoomerCompat {\n    private static final int DEFAULT_SHORT_ANIMATION_DURATION = 200;\n    /**\n     * The interpolator, used for making zooms animate 'naturally.'\n     */\n    private Interpolator mInterpolator;\n\n    /**\n     * The total animation duration for a zoom.\n     */\n    private long mAnimationDurationMillis;\n\n    /**\n     * Whether or not the current zoom has finished.\n     */\n    private boolean mFinished = true;\n\n    /**\n     * The current zoom value; computed by {@link #computeZoom()}.\n     */\n    private float mCurrentZoom;\n\n    /**\n     * The time the zoom started, computed using {@link android.os.SystemClock#elapsedRealtime()}.\n     */\n    private long mStartRTC;\n\n    /**\n     * The destination zoom factor.\n     */\n    private float mEndZoom;\n\n    public ZoomerCompat(Context context) {\n        mInterpolator = new DecelerateInterpolator();\n        // TODO: use constant\n        mAnimationDurationMillis = DEFAULT_SHORT_ANIMATION_DURATION;\n    }\n\n    /**\n     * Forces the zoom finished state to the given value. Unlike {@link #abortAnimation()}, the current zoom value isn't\n     * set to the ending value.\n     *\n     * @see android.widget.Scroller#forceFinished(boolean)\n     */\n    public void forceFinished(boolean finished) {\n        mFinished = finished;\n    }\n\n    /**\n     * Aborts the animation, setting the current zoom value to the ending value.\n     *\n     * @see android.widget.Scroller#abortAnimation()\n     */\n    public void abortAnimation() {\n        mFinished = true;\n        mCurrentZoom = mEndZoom;\n    }\n\n    /**\n     * Starts a zoom from 1.0 to (1.0 + endZoom). That is, to zoom from 100% to 125%, endZoom should by 0.25f.\n     *\n     * @see android.widget.Scroller#startScroll(int, int, int, int)\n     */\n    public void startZoom(float endZoom) {\n        mStartRTC = SystemClock.elapsedRealtime();\n        mEndZoom = endZoom;\n\n        mFinished = false;\n        mCurrentZoom = 1f;\n    }\n\n    /**\n     * Computes the current zoom level, returning true if the zoom is still active and false if the zoom has finished.\n     *\n     * @see android.widget.Scroller#computeScrollOffset()\n     */\n    public boolean computeZoom() {\n        if (mFinished) {\n            return false;\n        }\n\n        long tRTC = SystemClock.elapsedRealtime() - mStartRTC;\n        if (tRTC >= mAnimationDurationMillis) {\n            mFinished = true;\n            mCurrentZoom = mEndZoom;\n            return false;\n        }\n\n        float t = tRTC * 1f / mAnimationDurationMillis;\n        mCurrentZoom = mEndZoom * mInterpolator.getInterpolation(t);\n        return true;\n    }\n\n    /**\n     * Returns the current zoom level.\n     *\n     * @see android.widget.Scroller#getCurrX()\n     */\n    public float getCurrZoom() {\n        return mCurrentZoom;\n    }\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/listener/BubbleChartOnValueSelectListener.java",
    "content": "package lecho.lib.hellocharts.listener;\n\n\nimport lecho.lib.hellocharts.model.BubbleValue;\n\npublic interface BubbleChartOnValueSelectListener extends OnValueDeselectListener {\n\n    public void onValueSelected(int bubbleIndex, BubbleValue value);\n\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/listener/ColumnChartOnValueSelectListener.java",
    "content": "package lecho.lib.hellocharts.listener;\n\n\nimport lecho.lib.hellocharts.model.SubcolumnValue;\n\npublic interface ColumnChartOnValueSelectListener extends OnValueDeselectListener {\n\n    public void onValueSelected(int columnIndex, int subcolumnIndex, SubcolumnValue value);\n\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/listener/ComboLineColumnChartOnValueSelectListener.java",
    "content": "package lecho.lib.hellocharts.listener;\n\n\nimport lecho.lib.hellocharts.model.PointValue;\nimport lecho.lib.hellocharts.model.SubcolumnValue;\n\npublic interface ComboLineColumnChartOnValueSelectListener extends OnValueDeselectListener {\n\n    public void onColumnValueSelected(int columnIndex, int subcolumnIndex, SubcolumnValue value);\n\n    public void onPointValueSelected(int lineIndex, int pointIndex, PointValue value);\n\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/listener/DummyBubbleChartOnValueSelectListener.java",
    "content": "package lecho.lib.hellocharts.listener;\n\n\nimport lecho.lib.hellocharts.model.BubbleValue;\n\npublic class DummyBubbleChartOnValueSelectListener implements BubbleChartOnValueSelectListener {\n\n    @Override\n    public void onValueSelected(int bubbleIndex, BubbleValue value) {\n\n    }\n\n    @Override\n    public void onValueDeselected() {\n\n    }\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/listener/DummyColumnChartOnValueSelectListener.java",
    "content": "package lecho.lib.hellocharts.listener;\n\n\nimport lecho.lib.hellocharts.model.SubcolumnValue;\n\npublic class DummyColumnChartOnValueSelectListener implements ColumnChartOnValueSelectListener {\n\n    @Override\n    public void onValueSelected(int columnIndex, int subcolumnIndex, SubcolumnValue value) {\n\n    }\n\n    @Override\n    public void onValueDeselected() {\n\n    }\n\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/listener/DummyCompoLineColumnChartOnValueSelectListener.java",
    "content": "package lecho.lib.hellocharts.listener;\n\n\nimport lecho.lib.hellocharts.model.PointValue;\nimport lecho.lib.hellocharts.model.SubcolumnValue;\n\npublic class DummyCompoLineColumnChartOnValueSelectListener implements ComboLineColumnChartOnValueSelectListener {\n\n    @Override\n    public void onColumnValueSelected(int columnIndex, int subcolumnIndex, SubcolumnValue value) {\n\n    }\n\n    @Override\n    public void onPointValueSelected(int lineIndex, int pointIndex, PointValue value) {\n\n    }\n\n    @Override\n    public void onValueDeselected() {\n\n    }\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/listener/DummyLineChartOnValueSelectListener.java",
    "content": "package lecho.lib.hellocharts.listener;\n\n\nimport lecho.lib.hellocharts.model.PointValue;\n\npublic class DummyLineChartOnValueSelectListener implements LineChartOnValueSelectListener {\n\n    @Override\n    public void onValueSelected(int lineIndex, int pointIndex, PointValue value) {\n\n    }\n\n    @Override\n    public void onValueDeselected() {\n\n    }\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/listener/DummyPieChartOnValueSelectListener.java",
    "content": "package lecho.lib.hellocharts.listener;\n\n\nimport lecho.lib.hellocharts.model.SliceValue;\n\npublic class DummyPieChartOnValueSelectListener implements PieChartOnValueSelectListener {\n\n    @Override\n    public void onValueSelected(int arcIndex, SliceValue value) {\n\n    }\n\n    @Override\n    public void onValueDeselected() {\n\n    }\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/listener/DummyVieportChangeListener.java",
    "content": "package lecho.lib.hellocharts.listener;\n\nimport lecho.lib.hellocharts.model.Viewport;\n\npublic class DummyVieportChangeListener implements ViewportChangeListener {\n\n    @Override\n    public void onViewportChanged(Viewport viewport) {\n        // Do nothing\n    }\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/listener/LineChartOnValueSelectListener.java",
    "content": "package lecho.lib.hellocharts.listener;\n\n\nimport lecho.lib.hellocharts.model.PointValue;\n\npublic interface LineChartOnValueSelectListener extends OnValueDeselectListener {\n\n    public void onValueSelected(int lineIndex, int pointIndex, PointValue value);\n\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/listener/OnValueDeselectListener.java",
    "content": "package lecho.lib.hellocharts.listener;\n\n\npublic interface OnValueDeselectListener {\n\n    /**\n     * Called only in chart selection mode when user touch empty space causing value deselection.\n     * Note: this method is not called when selection mode is disabled.\n     */\n    public void onValueDeselected();\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/listener/PieChartOnValueSelectListener.java",
    "content": "package lecho.lib.hellocharts.listener;\n\n\nimport lecho.lib.hellocharts.model.SliceValue;\n\npublic interface PieChartOnValueSelectListener extends OnValueDeselectListener {\n\n    public void onValueSelected(int arcIndex, SliceValue value);\n\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/listener/ViewportChangeListener.java",
    "content": "package lecho.lib.hellocharts.listener;\n\nimport lecho.lib.hellocharts.model.Viewport;\n\n/**\n * Use implementations of this listener to be notified when chart viewport changed. For now it works only for preview\n * charts. To make it works for other chart types you just need to uncomment last line in\n * {@link lecho.lib.hellocharts.computator.ChartComputator#constrainViewport(float, float, float, float)} method.\n */\npublic interface ViewportChangeListener {\n\n    /**\n     * Called when current viewport of chart changed. You should not modify that viewport.\n     */\n    public void onViewportChanged(Viewport viewport);\n\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/model/AbstractChartData.java",
    "content": "package lecho.lib.hellocharts.model;\n\nimport android.graphics.Color;\nimport android.graphics.Typeface;\n\nimport lecho.lib.hellocharts.util.ChartUtils;\n\n/**\n * Base class for most chart data models.\n */\npublic abstract class AbstractChartData implements ChartData {\n    public static final int DEFAULT_TEXT_SIZE_SP = 12;\n    protected Axis axisXBottom;\n    protected Axis axisYLeft;\n    protected Axis axisXTop;\n    protected Axis axisYRight;\n    protected int valueLabelTextColor = Color.WHITE;\n    protected int valueLabelTextSize = DEFAULT_TEXT_SIZE_SP;\n    protected Typeface valueLabelTypeface;\n\n    /**\n     * If true each value label will have background rectangle\n     */\n    protected boolean isValueLabelBackgroundEnabled = true;\n\n    /**\n     * If true and {@link #isValueLabelBackgroundEnabled} is true each label will have background rectangle and that\n     * rectangle will be filled with color specified for given value.\n     */\n    protected boolean isValueLabelBackgrountAuto = true;\n\n    /**\n     * If {@link #isValueLabelBackgroundEnabled} is true and {@link #isValueLabelBackgrountAuto} is false each label\n     * will have background rectangle and that rectangle will be filled with this color. Helpful if you want all labels\n     * to have the same background color.\n     */\n    protected int valueLabelBackgroundColor = ChartUtils.darkenColor(ChartUtils.DEFAULT_DARKEN_COLOR);\n\n    public AbstractChartData() {\n\n    }\n\n    /**\n     * Copy constructor for deep copy.\n     *\n     * @param data\n     */\n    public AbstractChartData(AbstractChartData data) {\n        if (null != data.axisXBottom) {\n            this.axisXBottom = new Axis(data.axisXBottom);\n        }\n        if (null != data.axisXTop) {\n            this.axisXTop = new Axis(data.axisXTop);\n        }\n        if (null != data.axisYLeft) {\n            this.axisYLeft = new Axis(data.axisYLeft);\n        }\n        if (null != data.axisYRight) {\n            this.axisYRight = new Axis(data.axisYRight);\n        }\n        this.valueLabelTextColor = data.valueLabelTextColor;\n        this.valueLabelTextSize = data.valueLabelTextSize;\n        this.valueLabelTypeface = data.valueLabelTypeface;\n    }\n\n    @Override\n    public Axis getAxisXBottom() {\n        return axisXBottom;\n    }\n\n    @Override\n    public void setAxisXBottom(Axis axisX) {\n        this.axisXBottom = axisX;\n    }\n\n    @Override\n    public Axis getAxisYLeft() {\n        return axisYLeft;\n    }\n\n    @Override\n    public void setAxisYLeft(Axis axisY) {\n        this.axisYLeft = axisY;\n    }\n\n    @Override\n    public Axis getAxisXTop() {\n        return axisXTop;\n    }\n\n    @Override\n    public void setAxisXTop(Axis axisX) {\n        this.axisXTop = axisX;\n    }\n\n    @Override\n    public Axis getAxisYRight() {\n        return axisYRight;\n    }\n\n    @Override\n    public void setAxisYRight(Axis axisY) {\n        this.axisYRight = axisY;\n    }\n\n    @Override\n    public int getValueLabelTextColor() {\n        return valueLabelTextColor;\n    }\n\n    @Override\n    public void setValueLabelsTextColor(int valueLabelTextColor) {\n        this.valueLabelTextColor = valueLabelTextColor;\n    }\n\n    @Override\n    public int getValueLabelTextSize() {\n        return valueLabelTextSize;\n    }\n\n    @Override\n    public void setValueLabelTextSize(int valueLabelTextSize) {\n        this.valueLabelTextSize = valueLabelTextSize;\n    }\n\n    @Override\n    public Typeface getValueLabelTypeface() {\n        return valueLabelTypeface;\n    }\n\n    @Override\n    public void setValueLabelTypeface(Typeface typeface) {\n        this.valueLabelTypeface = typeface;\n    }\n\n    @Override\n    public boolean isValueLabelBackgroundEnabled() {\n        return isValueLabelBackgroundEnabled;\n    }\n\n    @Override\n    public void setValueLabelBackgroundEnabled(boolean isValueLabelBackgroundEnabled) {\n        this.isValueLabelBackgroundEnabled = isValueLabelBackgroundEnabled;\n    }\n\n    @Override\n    public boolean isValueLabelBackgroundAuto() {\n        return isValueLabelBackgrountAuto;\n    }\n\n    @Override\n    public void setValueLabelBackgroundAuto(boolean isValueLabelBackgrountAuto) {\n        this.isValueLabelBackgrountAuto = isValueLabelBackgrountAuto;\n    }\n\n    @Override\n    public int getValueLabelBackgroundColor() {\n        return valueLabelBackgroundColor;\n    }\n\n    @Override\n    public void setValueLabelBackgroundColor(int valueLabelBackgroundColor) {\n        this.valueLabelBackgroundColor = valueLabelBackgroundColor;\n    }\n\n}"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/model/Axis.java",
    "content": "package lecho.lib.hellocharts.model;\n\nimport android.graphics.Color;\nimport android.graphics.Typeface;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport lecho.lib.hellocharts.formatter.AxisValueFormatter;\nimport lecho.lib.hellocharts.formatter.SimpleAxisValueFormatter;\nimport lecho.lib.hellocharts.util.ChartUtils;\n\n/**\n * Single axis model. By default axis is auto-generated. Use {@link #setAutoGenerated(boolean)} to disable axis values\n * generation and set values manually using {@link #setValues(List)}. If Axis is auto-generated {@link #setValues(List)}\n * will be ignored but if you set some values Axis will switch to manual mode. Change how axis labels are displayed by\n * changing formatter {@link #setFormatter(lecho.lib.hellocharts.formatter.AxisValueFormatter)}. Axis can have a name\n * that should be displayed next to\n * labels(that depends on renderer implementation), you can change name using {@link #setName(String)}, by default axis\n * name is empty and therefore not displayed.\n */\npublic class Axis {\n    public static final int DEFAULT_TEXT_SIZE_SP = 12;\n    public static final int DEFAULT_MAX_AXIS_LABEL_CHARS = 3;\n    /**\n     * Text size for axis labels and name.\n     */\n    private int textSize = DEFAULT_TEXT_SIZE_SP;\n    /**\n     * Maximum number of characters used for this axis. Used to determine axis dimensions.\n     */\n    private int maxLabelChars = DEFAULT_MAX_AXIS_LABEL_CHARS;\n    /**\n     * Axis values, each value will be used to calculate its label position.\n     */\n    private List<AxisValue> values = new ArrayList<AxisValue>();\n    /**\n     * Name for this axis.\n     */\n    private String name;\n    /**\n     * If true axis will be generated to automatically fit chart ranges. *\n     */\n    private boolean isAutoGenerated = true;\n    /**\n     * If true renderer will draw lines(grid) for this axis.\n     */\n    private boolean hasLines = false;\n    /**\n     * If true axis labels will be drown inside chart area.\n     */\n    private boolean isInside = false;\n    /**\n     * Axis labels and name text color.\n     */\n    private int textColor = Color.LTGRAY;\n    /**\n     * Axis grid lines color.\n     */\n    private int lineColor = ChartUtils.DEFAULT_DARKEN_COLOR;\n    /**\n     * Typeface for labels and name text.\n     */\n    private Typeface typeface;\n\n    /**\n     * Formatter used to format labels.\n     */\n    private AxisValueFormatter formatter = new SimpleAxisValueFormatter();\n\n    /**\n     * If true draws a line between the labels and the graph *\n     */\n    private boolean hasSeparationLine = true;\n\n    private boolean hasTiltedLabels = false;\n\n    /**\n     * Creates auto-generated axis without name and with default formatter.\n     */\n    public Axis() {\n    }\n\n    /**\n     * Creates axis with given values(not auto-generated) without name and with default formatter.\n     */\n    public Axis(List<AxisValue> values) {\n        setValues(values);\n    }\n\n    public Axis(Axis axis) {\n        this.name = axis.name;\n        this.isAutoGenerated = axis.isAutoGenerated;\n        this.hasLines = axis.hasLines;\n        this.isInside = axis.isInside;\n        this.textColor = axis.textColor;\n        this.lineColor = axis.lineColor;\n        this.textSize = axis.textSize;\n        this.maxLabelChars = axis.maxLabelChars;\n        this.typeface = axis.typeface;\n        this.formatter = axis.formatter;\n        this.hasSeparationLine = axis.hasSeparationLine;\n\n        for (AxisValue axisValue : axis.values) {\n            this.values.add(new AxisValue(axisValue));\n        }\n    }\n\n    /**\n     * Generates Axis with values from start to stop inclusive.\n     */\n    public static Axis generateAxisFromRange(float start, float stop, float step) {\n\n        List<AxisValue> values = new ArrayList<AxisValue>();\n        for (float value = start; value <= stop; value += step) {\n            AxisValue axisValue = new AxisValue(value);\n            values.add(axisValue);\n        }\n\n        Axis axis = new Axis(values);\n        return axis;\n    }\n\n    /**\n     * Generates Axis with values from given list.\n     */\n    public static Axis generateAxisFromCollection(List<Float> axisValues) {\n        List<AxisValue> values = new ArrayList<AxisValue>();\n        int index = 0;\n        for (float value : axisValues) {\n            AxisValue axisValue = new AxisValue(value);\n            values.add(axisValue);\n            ++index;\n        }\n\n        Axis axis = new Axis(values);\n        return axis;\n    }\n\n    /**\n     * Generates Axis with values and labels from given lists, both lists must have the same size.\n     */\n    public static Axis generateAxisFromCollection(List<Float> axisValues, List<String> axisValuesLabels) {\n        if (axisValues.size() != axisValuesLabels.size()) {\n            throw new IllegalArgumentException(\"Values and labels lists must have the same size!\");\n        }\n\n        List<AxisValue> values = new ArrayList<AxisValue>();\n        int index = 0;\n        for (float value : axisValues) {\n            AxisValue axisValue = new AxisValue(value).setLabel(axisValuesLabels.get(index));\n            values.add(axisValue);\n            ++index;\n        }\n\n        Axis axis = new Axis(values);\n        return axis;\n    }\n\n    public List<AxisValue> getValues() {\n        return values;\n    }\n\n    public Axis setValues(List<AxisValue> values) {\n        if (null == values) {\n            this.values = new ArrayList<AxisValue>();\n        } else {\n            this.values = values;\n        }\n\n        this.isAutoGenerated = false;\n        return this;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public Axis setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public boolean isAutoGenerated() {\n        return isAutoGenerated;\n    }\n\n    public Axis setAutoGenerated(boolean isAutoGenerated) {\n        this.isAutoGenerated = isAutoGenerated;\n        return this;\n    }\n\n    public boolean hasLines() {\n        return hasLines;\n    }\n\n    public Axis setHasLines(boolean hasLines) {\n        this.hasLines = hasLines;\n        return this;\n    }\n\n    public int getTextColor() {\n        return textColor;\n    }\n\n    public Axis setTextColor(int color) {\n        this.textColor = color;\n        return this;\n    }\n\n    /**\n     * @see #setInside(boolean)\n     */\n    public boolean isInside() {\n        return isInside;\n    }\n\n    /**\n     * Set to true if you want axis values to be drawn inside chart area(axis name still will be drawn outside), by\n     * default this is set to false and axis is drawn outside chart area.\n     */\n    public Axis setInside(boolean isInside) {\n        this.isInside = isInside;\n        return this;\n    }\n\n    public int getLineColor() {\n        return lineColor;\n    }\n\n    public Axis setLineColor(int lineColor) {\n        this.lineColor = lineColor;\n        return this;\n    }\n\n    public int getTextSize() {\n        return textSize;\n    }\n\n    public Axis setTextSize(int textSize) {\n        this.textSize = textSize;\n        return this;\n    }\n\n    public int getMaxLabelChars() {\n        return maxLabelChars;\n    }\n\n    /**\n     * Set maximum number of characters for axis labels, min 0, max 32.\n     */\n    public Axis setMaxLabelChars(int maxLabelChars) {\n        if (maxLabelChars < 0) {\n            maxLabelChars = 0;\n        } else if (maxLabelChars > 32) {\n            maxLabelChars = 32;\n        }\n        this.maxLabelChars = maxLabelChars;\n        return this;\n    }\n\n    public Typeface getTypeface() {\n        return typeface;\n    }\n\n    public Axis setTypeface(Typeface typeface) {\n        this.typeface = typeface;\n        return this;\n    }\n\n    public AxisValueFormatter getFormatter() {\n        return formatter;\n    }\n\n    public Axis setFormatter(AxisValueFormatter formatter) {\n        if (null == formatter) {\n            this.formatter = new SimpleAxisValueFormatter();\n        } else {\n            this.formatter = formatter;\n        }\n        return this;\n    }\n\n    /**\n     * Set true if you want to draw separation line for this axis, set false to hide separation line, by default true.\n     */\n    public Axis setHasSeparationLine(boolean hasSeparationLine) {\n        this.hasSeparationLine = hasSeparationLine;\n        return this;\n    }\n\n    public boolean hasSeparationLine() {\n        return hasSeparationLine;\n    }\n\n    public boolean hasTiltedLabels() {\n        return hasTiltedLabels;\n    }\n\n    public Axis setHasTiltedLabels(boolean hasTiltedLabels) {\n        this.hasTiltedLabels = hasTiltedLabels;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/model/AxisValue.java",
    "content": "package lecho.lib.hellocharts.model;\n\nimport java.util.Arrays;\n\n/**\n * Single axis value, use it to manually set axis labels position. You can use label attribute to display text instead\n * of number but value formatter implementation have to handle it.\n */\npublic class AxisValue {\n    private float value;\n    private char[] label;\n\n    public AxisValue(float value) {\n        setValue(value);\n    }\n\n    @Deprecated\n    public AxisValue(float value, char[] label) {\n        this.value = value;\n        this.label = label;\n    }\n\n    public AxisValue(AxisValue axisValue) {\n        this.value = axisValue.value;\n        this.label = axisValue.label;\n    }\n\n    public float getValue() {\n        return value;\n    }\n\n    public AxisValue setValue(float value) {\n        this.value = value;\n        return this;\n    }\n\n    @Deprecated\n    public char[] getLabel() {\n        return label;\n    }\n\n    /**\n     * Set custom label for this axis value.\n     *\n     * @param label\n     */\n    public AxisValue setLabel(String label) {\n        this.label = label.toCharArray();\n        return this;\n    }\n\n    public char[] getLabelAsChars() {\n        return label;\n    }\n\n    /**\n     * Set custom label for this axis value.\n     *\n     * @param label\n     */\n    @Deprecated\n    public AxisValue setLabel(char[] label) {\n        this.label = label;\n        return this;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n\n        AxisValue axisValue = (AxisValue) o;\n\n        if (Float.compare(axisValue.value, value) != 0) return false;\n        if (!Arrays.equals(label, axisValue.label)) return false;\n\n        return true;\n    }\n\n    @Override\n    public int hashCode() {\n        int result = (value != +0.0f ? Float.floatToIntBits(value) : 0);\n        result = 31 * result + (label != null ? Arrays.hashCode(label) : 0);\n        return result;\n    }\n}"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/model/BubbleChartData.java",
    "content": "package lecho.lib.hellocharts.model;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport lecho.lib.hellocharts.formatter.BubbleChartValueFormatter;\nimport lecho.lib.hellocharts.formatter.SimpleBubbleChartValueFormatter;\nimport lecho.lib.hellocharts.view.Chart;\n\n/**\n * Data for BubbleChart.\n */\npublic class BubbleChartData extends AbstractChartData {\n    public static final int DEFAULT_MIN_BUBBLE_RADIUS_DP = 6;\n    public static final float DEFAULT_BUBBLE_SCALE = 1f;\n    private BubbleChartValueFormatter formatter = new SimpleBubbleChartValueFormatter();\n    private boolean hasLabels = false;\n    private boolean hasLabelsOnlyForSelected = false;\n    private int minBubbleRadius = DEFAULT_MIN_BUBBLE_RADIUS_DP;\n    private float bubbleScale = DEFAULT_BUBBLE_SCALE;\n    // TODO: consider Collections.emptyList()\n    private List<BubbleValue> values = new ArrayList<BubbleValue>();\n\n    public BubbleChartData() {\n    }\n\n    public BubbleChartData(List<BubbleValue> values) {\n        setValues(values);\n    }\n\n    /**\n     * Copy constructor for deep copy.\n     */\n    public BubbleChartData(BubbleChartData data) {\n        super(data);\n        this.formatter = data.formatter;\n        this.hasLabels = data.hasLabels;\n        this.hasLabelsOnlyForSelected = data.hasLabelsOnlyForSelected;\n        this.minBubbleRadius = data.minBubbleRadius;\n        this.bubbleScale = data.bubbleScale;\n\n        for (BubbleValue bubbleValue : data.getValues()) {\n            this.values.add(new BubbleValue(bubbleValue));\n        }\n    }\n\n    public static BubbleChartData generateDummyData() {\n        final int numValues = 4;\n        BubbleChartData data = new BubbleChartData();\n        List<BubbleValue> values = new ArrayList<BubbleValue>(numValues);\n        values.add(new BubbleValue(0, 20, 15000));\n        values.add(new BubbleValue(3, 22, 20000));\n        values.add(new BubbleValue(5, 25, 5000));\n        values.add(new BubbleValue(7, 30, 30000));\n        values.add(new BubbleValue(11, 22, 10));\n        data.setValues(values);\n        return data;\n    }\n\n    @Override\n    public void update(float scale) {\n        for (BubbleValue value : values) {\n            value.update(scale);\n        }\n    }\n\n    @Override\n    public void finish() {\n        for (BubbleValue value : values) {\n            value.finish();\n        }\n    }\n\n    public List<BubbleValue> getValues() {\n        return values;\n    }\n\n    public BubbleChartData setValues(List<BubbleValue> values) {\n        if (null == values) {\n            this.values = new ArrayList<BubbleValue>();\n        } else {\n            this.values = values;\n        }\n        return this;\n    }\n\n    public boolean hasLabels() {\n        return hasLabels;\n    }\n\n    public BubbleChartData setHasLabels(boolean hasLabels) {\n        this.hasLabels = hasLabels;\n        if (hasLabels) {\n            hasLabelsOnlyForSelected = false;\n        }\n        return this;\n    }\n\n    /**\n     * @see #setHasLabelsOnlyForSelected(boolean)\n     */\n    public boolean hasLabelsOnlyForSelected() {\n        return hasLabelsOnlyForSelected;\n    }\n\n    /**\n     * Set true if you want to show value labels only for selected value, works best when chart has\n     * isValueSelectionEnabled set to true {@link Chart#setValueSelectionEnabled(boolean)}.\n     */\n    public BubbleChartData setHasLabelsOnlyForSelected(boolean hasLabelsOnlyForSelected) {\n        this.hasLabelsOnlyForSelected = hasLabelsOnlyForSelected;\n        if (hasLabelsOnlyForSelected) {\n            this.hasLabels = false;\n        }\n        return this;\n    }\n\n    /**\n     * Returns minimal bubble radius in dp.\n     *\n     * @see #setMinBubbleRadius(int)\n     */\n    public int getMinBubbleRadius() {\n        return minBubbleRadius;\n    }\n\n    /**\n     * Set minimal bubble radius in dp, helpful when you want small bubbles(bubbles with very small z values compared to\n     * other bubbles) to be visible on chart, default 6dp\n     */\n    public void setMinBubbleRadius(int minBubbleRadius) {\n        this.minBubbleRadius = minBubbleRadius;\n    }\n\n    /**\n     * Returns bubble scale which is used to adjust bubble size.\n     *\n     * @see #setBubbleScale(float)\n     */\n    public float getBubbleScale() {\n        return bubbleScale;\n    }\n\n    /**\n     * Set bubble scale which is used to adjust bubble size. If you want smaller bubbles set scale {@code <0, 1>},\n     * if you want bigger bubbles set scale greater than 1, default is 1.0f.\n     */\n    public void setBubbleScale(float bubbleScale) {\n        this.bubbleScale = bubbleScale;\n    }\n\n    public BubbleChartValueFormatter getFormatter() {\n        return formatter;\n    }\n\n    public BubbleChartData setFormatter(BubbleChartValueFormatter formatter) {\n        if (null != formatter) {\n            this.formatter = formatter;\n        }\n        return this;\n    }\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/model/BubbleValue.java",
    "content": "package lecho.lib.hellocharts.model;\n\nimport java.util.Arrays;\n\nimport lecho.lib.hellocharts.util.ChartUtils;\nimport lecho.lib.hellocharts.view.Chart;\n\n/**\n * Single value drawn as bubble on BubbleChart.\n */\npublic class BubbleValue {\n\n    /**\n     * Current X value.\n     */\n    private float x;\n    /**\n     * Current Y value.\n     */\n    private float y;\n    /**\n     * Current Z value , third bubble value interpreted as bubble area.\n     */\n    private float z;\n\n    /**\n     * Origin X value, used during value animation.\n     */\n    private float originX;\n    /**\n     * Origin Y value, used during value animation.\n     */\n    private float originY;\n    /**\n     * Origin Z value, used during value animation.\n     */\n    private float originZ;\n\n    /**\n     * Difference between originX value and target X value.\n     */\n    private float diffX;\n\n    /**\n     * Difference between originX value and target X value.\n     */\n    private float diffY;\n\n    /**\n     * Difference between originX value and target X value.\n     */\n    private float diffZ;\n    private int color = ChartUtils.DEFAULT_COLOR;\n    private int darkenColor = ChartUtils.DEFAULT_DARKEN_COLOR;\n    private ValueShape shape = ValueShape.CIRCLE;\n    private char[] label;\n\n    public BubbleValue() {\n        set(0, 0, 0);\n    }\n\n    public BubbleValue(float x, float y, float z) {\n        set(x, y, z);\n    }\n\n    public BubbleValue(float x, float y, float z, int color) {\n        set(x, y, z);\n        setColor(color);\n    }\n\n    public BubbleValue(BubbleValue bubbleValue) {\n        set(bubbleValue.x, bubbleValue.y, bubbleValue.z);\n        setColor(bubbleValue.color);\n        this.label = bubbleValue.label;\n    }\n\n    public void update(float scale) {\n        x = originX + diffX * scale;\n        y = originY + diffY * scale;\n        z = originZ + diffZ * scale;\n    }\n\n    public void finish() {\n        set(originX + diffX, originY + diffY, originZ + diffZ);\n    }\n\n    public BubbleValue set(float x, float y, float z) {\n        this.x = x;\n        this.y = y;\n        this.z = z;\n        this.originX = x;\n        this.originY = y;\n        this.originZ = z;\n        this.diffX = 0;\n        this.diffY = 0;\n        this.diffZ = 0;\n        return this;\n    }\n\n    /**\n     * Set target values that should be reached when data animation finish then call {@link Chart#startDataAnimation()}\n     */\n    public BubbleValue setTarget(float targetX, float targetY, float targetZ) {\n        set(x, y, z);\n        this.diffX = targetX - originX;\n        this.diffY = targetY - originY;\n        this.diffZ = targetZ - originZ;\n        return this;\n    }\n\n    public float getX() {\n        return this.x;\n    }\n\n    public float getY() {\n        return this.y;\n    }\n\n    public float getZ() {\n        return this.z;\n    }\n\n    public int getColor() {\n        return color;\n    }\n\n    public BubbleValue setColor(int color) {\n        this.color = color;\n        this.darkenColor = ChartUtils.darkenColor(color);\n        return this;\n    }\n\n    public int getDarkenColor() {\n        return darkenColor;\n    }\n\n    public ValueShape getShape() {\n        return shape;\n    }\n\n    public BubbleValue setShape(ValueShape shape) {\n        this.shape = shape;\n        return this;\n    }\n\n    @Deprecated\n    public char[] getLabel() {\n        return label;\n    }\n\n    public BubbleValue setLabel(String label) {\n        this.label = label.toCharArray();\n        return this;\n    }\n\n    public char[] getLabelAsChars() {\n        return label;\n    }\n\n    @Deprecated\n    public BubbleValue setLabel(char[] label) {\n        this.label = label;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"BubbleValue [x=\" + x + \", y=\" + y + \", z=\" + z + \"]\";\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n\n        BubbleValue that = (BubbleValue) o;\n\n        if (color != that.color) return false;\n        if (darkenColor != that.darkenColor) return false;\n        if (Float.compare(that.diffX, diffX) != 0) return false;\n        if (Float.compare(that.diffY, diffY) != 0) return false;\n        if (Float.compare(that.diffZ, diffZ) != 0) return false;\n        if (Float.compare(that.originX, originX) != 0) return false;\n        if (Float.compare(that.originY, originY) != 0) return false;\n        if (Float.compare(that.originZ, originZ) != 0) return false;\n        if (Float.compare(that.x, x) != 0) return false;\n        if (Float.compare(that.y, y) != 0) return false;\n        if (Float.compare(that.z, z) != 0) return false;\n        if (!Arrays.equals(label, that.label)) return false;\n        if (shape != that.shape) return false;\n\n        return true;\n    }\n\n    @Override\n    public int hashCode() {\n        int result = (x != +0.0f ? Float.floatToIntBits(x) : 0);\n        result = 31 * result + (y != +0.0f ? Float.floatToIntBits(y) : 0);\n        result = 31 * result + (z != +0.0f ? Float.floatToIntBits(z) : 0);\n        result = 31 * result + (originX != +0.0f ? Float.floatToIntBits(originX) : 0);\n        result = 31 * result + (originY != +0.0f ? Float.floatToIntBits(originY) : 0);\n        result = 31 * result + (originZ != +0.0f ? Float.floatToIntBits(originZ) : 0);\n        result = 31 * result + (diffX != +0.0f ? Float.floatToIntBits(diffX) : 0);\n        result = 31 * result + (diffY != +0.0f ? Float.floatToIntBits(diffY) : 0);\n        result = 31 * result + (diffZ != +0.0f ? Float.floatToIntBits(diffZ) : 0);\n        result = 31 * result + color;\n        result = 31 * result + darkenColor;\n        result = 31 * result + (shape != null ? shape.hashCode() : 0);\n        result = 31 * result + (label != null ? Arrays.hashCode(label) : 0);\n        return result;\n    }\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/model/ChartData.java",
    "content": "package lecho.lib.hellocharts.model;\n\nimport android.graphics.Typeface;\n\n/**\n * Base interface for all chart data models.\n */\npublic interface ChartData {\n\n    /**\n     * Updates data by scale during animation.\n     *\n     * @param scale value from 0 to 1.0\n     */\n    public void update(float scale);\n\n    /**\n     * Inform data that animation finished(data should be update with scale 1.0f).\n     */\n    public void finish();\n\n    /**\n     * @see #setAxisXBottom(Axis)\n     */\n    public Axis getAxisXBottom();\n\n    /**\n     * Set horizontal axis at the bottom of the chart. Pass null to remove that axis.\n     *\n     * @param axisX\n     */\n    public void setAxisXBottom(Axis axisX);\n\n    /**\n     * @see #setAxisYLeft(Axis)\n     */\n    public Axis getAxisYLeft();\n\n    /**\n     * Set vertical axis on the left of the chart. Pass null to remove that axis.\n     *\n     * @param axisY\n     */\n    public void setAxisYLeft(Axis axisY);\n\n    /**\n     * @see #setAxisXTop(Axis)\n     */\n    public Axis getAxisXTop();\n\n    /**\n     * Set horizontal axis at the top of the chart. Pass null to remove that axis.\n     *\n     * @param axisX\n     */\n    public void setAxisXTop(Axis axisX);\n\n    /**\n     * @see #setAxisYRight(Axis)\n     */\n    public Axis getAxisYRight();\n\n    /**\n     * Set vertical axis on the right of the chart. Pass null to remove that axis.\n     *\n     * @param axisY\n     */\n    public void setAxisYRight(Axis axisY);\n\n    /**\n     * Returns color used to draw value label text.\n     */\n    public int getValueLabelTextColor();\n\n    /**\n     * Set value label text color, by default Color.WHITE.\n     */\n    public void setValueLabelsTextColor(int labelsTextColor);\n\n    /**\n     * Returns text size for value label in SP units.\n     */\n    public int getValueLabelTextSize();\n\n    /**\n     * Set text size for value label in SP units.\n     */\n    public void setValueLabelTextSize(int labelsTextSize);\n\n    /**\n     * Returns Typeface for value labels.\n     *\n     * @return Typeface or null if Typeface is not set.\n     */\n    public Typeface getValueLabelTypeface();\n\n    /**\n     * Set Typeface for all values labels.\n     *\n     * @param typeface\n     */\n    public void setValueLabelTypeface(Typeface typeface);\n\n    /**\n     * @see #setValueLabelBackgroundEnabled(boolean)\n     */\n    public boolean isValueLabelBackgroundEnabled();\n\n    /**\n     * Set whether labels should have rectangle background. Default is true.\n     */\n    public void setValueLabelBackgroundEnabled(boolean isValueLabelBackgroundEnabled);\n\n    /**\n     * @see #setValueLabelBackgroundAuto(boolean)\n     */\n    public boolean isValueLabelBackgroundAuto();\n\n    /**\n     * Set false if you want to set custom color for all value labels. Default is true.\n     */\n    public void setValueLabelBackgroundAuto(boolean isValueLabelBackgrountAuto);\n\n    /**\n     * @see #setValueLabelBackgroundColor(int)\n     */\n    public int getValueLabelBackgroundColor();\n\n    /**\n     * Set value labels background. This value is used only if isValueLabelBackgroundAuto returns false. Default is\n     * green.\n     */\n    public void setValueLabelBackgroundColor(int valueLabelBackgroundColor);\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/model/Column.java",
    "content": "package lecho.lib.hellocharts.model;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport lecho.lib.hellocharts.formatter.ColumnChartValueFormatter;\nimport lecho.lib.hellocharts.formatter.SimpleColumnChartValueFormatter;\nimport lecho.lib.hellocharts.view.Chart;\n\n/**\n * Single column for ColumnChart. One column can be divided into multiple sub-columns(ColumnValues) especially for\n * stacked ColumnChart.\n * Note: you can set X value for columns or sub-columns, columns are by default indexed from 0 to numOfColumns-1 and\n * column index is used as column X value, so first column has X value 0, second clumn has X value 1 etc.\n * If you want to display AxisValue for given column you should initialize AxisValue with X value of that column.\n */\npublic class Column {\n    private boolean hasLabels = false;\n    private boolean hasLabelsOnlyForSelected = false;\n    private ColumnChartValueFormatter formatter = new SimpleColumnChartValueFormatter();\n    // TODO: consider Collections.emptyList()\n    private List<SubcolumnValue> values = new ArrayList<SubcolumnValue>();\n\n    public Column() {\n\n    }\n\n    public Column(List<SubcolumnValue> values) {\n        setValues(values);\n    }\n\n    public Column(Column column) {\n        this.hasLabels = column.hasLabels;\n        this.hasLabelsOnlyForSelected = column.hasLabelsOnlyForSelected;\n        this.formatter = column.formatter;\n\n        for (SubcolumnValue columnValue : column.values) {\n            this.values.add(new SubcolumnValue(columnValue));\n        }\n    }\n\n    public void update(float scale) {\n        for (SubcolumnValue value : values) {\n            value.update(scale);\n        }\n\n    }\n\n    public void finish() {\n        for (SubcolumnValue value : values) {\n            value.finish();\n        }\n    }\n\n    public List<SubcolumnValue> getValues() {\n        return values;\n    }\n\n    public Column setValues(List<SubcolumnValue> values) {\n        if (null == values) {\n            this.values = new ArrayList<SubcolumnValue>();\n        } else {\n            this.values = values;\n        }\n        return this;\n    }\n\n    public boolean hasLabels() {\n        return hasLabels;\n    }\n\n    public Column setHasLabels(boolean hasLabels) {\n        this.hasLabels = hasLabels;\n        if (hasLabels) {\n            this.hasLabelsOnlyForSelected = false;\n        }\n        return this;\n    }\n\n    /**\n     * @see #setHasLabelsOnlyForSelected(boolean)\n     */\n    public boolean hasLabelsOnlyForSelected() {\n        return hasLabelsOnlyForSelected;\n    }\n\n    /**\n     * Set true if you want to show value labels only for selected value, works best when chart has\n     * isValueSelectionEnabled set to true {@link Chart#setValueSelectionEnabled(boolean)}.\n     */\n    public Column setHasLabelsOnlyForSelected(boolean hasLabelsOnlyForSelected) {\n        this.hasLabelsOnlyForSelected = hasLabelsOnlyForSelected;\n        if (hasLabelsOnlyForSelected) {\n            this.hasLabels = false;\n        }\n        return this;\n    }\n\n    public ColumnChartValueFormatter getFormatter() {\n        return formatter;\n    }\n\n    public Column setFormatter(ColumnChartValueFormatter formatter) {\n        if (null != formatter) {\n            this.formatter = formatter;\n        }\n        return this;\n    }\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/model/ColumnChartData.java",
    "content": "package lecho.lib.hellocharts.model;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Data model for column chart. Note: you can set X value for columns or sub-columns, columns are by default indexed\n * from 0 to numOfColumns-1 and\n * column index is used as column X value, so first column has X value 0, second clumn has X value 1 etc.\n * If you want to display AxisValue for given column you should initialize AxisValue with X value of that column.\n */\npublic class ColumnChartData extends AbstractChartData {\n    public static final float DEFAULT_FILL_RATIO = 0.75f;\n    public static final float DEFAULT_BASE_VALUE = 0.0f;\n    private float fillRatio = DEFAULT_FILL_RATIO;\n    private float baseValue = DEFAULT_BASE_VALUE;\n    private List<Column> columns = new ArrayList<Column>();\n    private boolean isStacked = false;\n\n    public ColumnChartData() {\n    }\n\n    public ColumnChartData(List<Column> columns) {\n        setColumns(columns);\n    }\n\n    /**\n     * Copy constructor for deep copy.\n     */\n    public ColumnChartData(ColumnChartData data) {\n        super(data);\n        this.isStacked = data.isStacked;\n        this.fillRatio = data.fillRatio;\n\n        for (Column column : data.columns) {\n            this.columns.add(new Column(column));\n        }\n    }\n\n    public static ColumnChartData generateDummyData() {\n        final int numColumns = 4;\n        ColumnChartData data = new ColumnChartData();\n        List<Column> columns = new ArrayList<Column>(numColumns);\n        List<SubcolumnValue> values;\n        Column column;\n        for (int i = 1; i <= numColumns; ++i) {\n            values = new ArrayList<SubcolumnValue>(numColumns);\n            values.add(new SubcolumnValue(i));\n            column = new Column(values);\n            columns.add(column);\n        }\n\n        data.setColumns(columns);\n        return data;\n    }\n\n    @Override\n    public void update(float scale) {\n        for (Column column : columns) {\n            column.update(scale);\n        }\n\n    }\n\n    @Override\n    public void finish() {\n        for (Column column : columns) {\n            column.finish();\n        }\n    }\n\n    public List<Column> getColumns() {\n        return columns;\n    }\n\n    public ColumnChartData setColumns(List<Column> columns) {\n        if (null == columns) {\n            this.columns = new ArrayList<Column>();\n        } else {\n            this.columns = columns;\n        }\n        return this;\n    }\n\n    public boolean isStacked() {\n        return isStacked;\n    }\n\n    /**\n     * Set true if you want stacked column chart.\n     *\n     * @param isStacked\n     * @return\n     */\n    public ColumnChartData setStacked(boolean isStacked) {\n        this.isStacked = isStacked;\n        return this;\n    }\n\n    public float getFillRatio() {\n        return fillRatio;\n    }\n\n    /**\n     * Set fill ration for columns, value from 0 to 1, 1 means that there will be almost no free space between columns,\n     * 0 means that columns will have minimum width(2px).\n     *\n     * @param fillRatio\n     * @return\n     */\n    public ColumnChartData setFillRatio(float fillRatio) {\n        if (fillRatio < 0) {\n            fillRatio = 0;\n        }\n        if (fillRatio > 1) {\n            fillRatio = 1;\n        }\n        this.fillRatio = fillRatio;\n        return this;\n    }\n\n    /**\n     * @see #setBaseValue(float)\n     */\n    public float getBaseValue() {\n        return baseValue;\n    }\n\n    /**\n     * Set value below which values will be drawn as negative, by default 0.\n     */\n    public ColumnChartData setBaseValue(float baseValue) {\n        this.baseValue = baseValue;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/model/ComboLineColumnChartData.java",
    "content": "package lecho.lib.hellocharts.model;\n\n/**\n * Data model for combo line-column chart. It uses ColumnChartData and LineChartData internally.\n */\npublic class ComboLineColumnChartData extends AbstractChartData {\n\n    private ColumnChartData columnChartData;\n    private LineChartData lineChartData;\n\n    public ComboLineColumnChartData() {\n        this.columnChartData = new ColumnChartData();\n        this.lineChartData = new LineChartData();\n    }\n\n    public ComboLineColumnChartData(ColumnChartData columnChartData, LineChartData lineChartData) {\n        setColumnChartData(columnChartData);\n        setLineChartData(lineChartData);\n    }\n\n    public ComboLineColumnChartData(ComboLineColumnChartData data) {\n        super(data);\n\n        setColumnChartData(new ColumnChartData(data.getColumnChartData()));\n        setLineChartData(new LineChartData(data.getLineChartData()));\n    }\n\n    public static ComboLineColumnChartData generateDummyData() {\n        ComboLineColumnChartData data = new ComboLineColumnChartData();\n        data.setColumnChartData(ColumnChartData.generateDummyData());\n        data.setLineChartData(LineChartData.generateDummyData());\n        return data;\n    }\n\n    @Override\n    public void update(float scale) {\n        columnChartData.update(scale);\n        lineChartData.update(scale);\n    }\n\n    @Override\n    public void finish() {\n        columnChartData.finish();\n        lineChartData.finish();\n    }\n\n    public ColumnChartData getColumnChartData() {\n        return columnChartData;\n    }\n\n    public void setColumnChartData(ColumnChartData columnChartData) {\n        if (null == columnChartData) {\n            this.columnChartData = new ColumnChartData();\n        } else {\n            this.columnChartData = columnChartData;\n        }\n    }\n\n    public LineChartData getLineChartData() {\n        return lineChartData;\n    }\n\n    public void setLineChartData(LineChartData lineChartData) {\n        if (null == lineChartData) {\n            this.lineChartData = new LineChartData();\n        } else {\n            this.lineChartData = lineChartData;\n        }\n    }\n\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/model/Line.java",
    "content": "package lecho.lib.hellocharts.model;\n\nimport android.graphics.PathEffect;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport lecho.lib.hellocharts.formatter.LineChartValueFormatter;\nimport lecho.lib.hellocharts.formatter.SimpleLineChartValueFormatter;\nimport lecho.lib.hellocharts.util.ChartUtils;\nimport lecho.lib.hellocharts.view.Chart;\n\n/**\n * Single line for line chart.\n */\npublic class Line {\n    private static final int DEFAULT_LINE_STROKE_WIDTH_DP = 3;\n    private static final int DEFAULT_POINT_RADIUS_DP = 6;\n    private static final int DEFAULT_AREA_TRANSPARENCY = 64;\n    public static final int UNINITIALIZED = 0;\n    private int color = ChartUtils.DEFAULT_COLOR;\n    private int pointColor = UNINITIALIZED;\n    private int darkenColor = ChartUtils.DEFAULT_DARKEN_COLOR;\n    /**\n     * Transparency of area when line is filled. *\n     */\n    private int areaTransparency = DEFAULT_AREA_TRANSPARENCY;\n    private int strokeWidth = DEFAULT_LINE_STROKE_WIDTH_DP;\n    private int pointRadius = DEFAULT_POINT_RADIUS_DP;\n    private boolean hasGradientToTransparent = false;\n    private boolean hasPoints = true;\n    private boolean hasLines = true;\n    private boolean hasLabels = false;\n    private boolean hasLabelsOnlyForSelected = false;\n    private boolean isCubic = false;\n    private boolean isSquare = false;\n    private boolean isFilled = false;\n    private ValueShape shape = ValueShape.CIRCLE;\n    private PathEffect pathEffect;\n    private LineChartValueFormatter formatter = new SimpleLineChartValueFormatter();\n    private List<PointValue> values = new ArrayList<PointValue>();\n\n    public Line() {\n\n    }\n\n    public Line(List<PointValue> values) {\n        setValues(values);\n    }\n\n    public Line(Line line) {\n        this.color = line.color;\n        this.pointColor = line.pointColor;\n        this.darkenColor = line.darkenColor;\n        this.areaTransparency = line.areaTransparency;\n        this.strokeWidth = line.strokeWidth;\n        this.pointRadius = line.pointRadius;\n        this.hasGradientToTransparent = line.hasGradientToTransparent;\n        this.hasPoints = line.hasPoints;\n        this.hasLines = line.hasLines;\n        this.hasLabels = line.hasLabels;\n        this.hasLabelsOnlyForSelected = line.hasLabelsOnlyForSelected;\n        this.isSquare = line.isSquare;\n        this.isCubic = line.isCubic;\n        this.isFilled = line.isFilled;\n        this.shape = line.shape;\n        this.pathEffect = line.pathEffect;\n        this.formatter = line.formatter;\n\n        for (PointValue pointValue : line.values) {\n            this.values.add(new PointValue(pointValue));\n        }\n    }\n\n    public void update(float scale) {\n        for (PointValue value : values) {\n            value.update(scale);\n        }\n    }\n\n    public void finish() {\n        for (PointValue value : values) {\n            value.finish();\n        }\n    }\n\n    public List<PointValue> getValues() {\n        return this.values;\n    }\n\n    public void setValues(List<PointValue> values) {\n        if (null == values) {\n            this.values = new ArrayList<PointValue>();\n        } else {\n            this.values = values;\n        }\n    }\n\n    public int getColor() {\n        return color;\n    }\n\n    public Line setColor(int color) {\n        this.color = color;\n        if (pointColor == UNINITIALIZED) {\n            this.darkenColor = ChartUtils.darkenColor(color);\n        }\n        return this;\n    }\n\n    public int getPointColor() {\n        if (pointColor == UNINITIALIZED) {\n            return color;\n        } else {\n            return pointColor;\n        }\n    }\n\n    public Line setPointColor(int pointColor) {\n        this.pointColor = pointColor;\n        if (pointColor == UNINITIALIZED) {\n            this.darkenColor = ChartUtils.darkenColor(color);\n        } else {\n            this.darkenColor = ChartUtils.darkenColor(pointColor);\n        }\n        return this;\n    }\n\n    public int getDarkenColor() {\n        return darkenColor;\n    }\n\n    /**\n     * @see #setAreaTransparency(int)\n     */\n    public int getAreaTransparency() {\n        return areaTransparency;\n    }\n\n    /**\n     * Set area transparency(255 is full opacity) for filled lines\n     *\n     * @param areaTransparency\n     * @return\n     */\n    public Line setAreaTransparency(int areaTransparency) {\n        this.areaTransparency = areaTransparency;\n        return this;\n    }\n\n    public int getStrokeWidth() {\n        return strokeWidth;\n    }\n\n    public Line setStrokeWidth(int strokeWidth) {\n        this.strokeWidth = strokeWidth;\n        return this;\n    }\n\n    public boolean hasPoints() {\n        return hasPoints;\n    }\n\n    public Line setHasPoints(boolean hasPoints) {\n        this.hasPoints = hasPoints;\n        return this;\n    }\n\n    public boolean hasLines() {\n        return hasLines;\n    }\n\n    public Line setHasLines(boolean hasLines) {\n        this.hasLines = hasLines;\n        return this;\n    }\n\n    public boolean hasLabels() {\n        return hasLabels;\n    }\n\n    public Line setHasLabels(boolean hasLabels) {\n        this.hasLabels = hasLabels;\n        if (hasLabels) {\n            this.hasLabelsOnlyForSelected = false;\n        }\n        return this;\n    }\n\n    /**\n     * @see #setHasLabelsOnlyForSelected(boolean)\n     */\n    public boolean hasLabelsOnlyForSelected() {\n        return hasLabelsOnlyForSelected;\n    }\n\n    /**\n     * Set true if you want to show value labels only for selected value, works best when chart has\n     * isValueSelectionEnabled set to true {@link Chart#setValueSelectionEnabled(boolean)}.\n     */\n    public Line setHasLabelsOnlyForSelected(boolean hasLabelsOnlyForSelected) {\n        this.hasLabelsOnlyForSelected = hasLabelsOnlyForSelected;\n        if (hasLabelsOnlyForSelected) {\n            this.hasLabels = false;\n        }\n        return this;\n    }\n\n    public int getPointRadius() {\n        return pointRadius;\n    }\n\n    /**\n     * Set radius for points for this line.\n     *\n     * @param pointRadius\n     * @return\n     */\n    public Line setPointRadius(int pointRadius) {\n        this.pointRadius = pointRadius;\n        return this;\n    }\n\n    public boolean getGradientToTransparent() {\n        return hasGradientToTransparent;\n    }\n\n    public Line setHasGradientToTransparent(boolean hasGradientToTransparent) {\n        this.hasGradientToTransparent = hasGradientToTransparent;\n        return this;\n    }\n\n    public boolean isCubic() {\n        return isCubic;\n    }\n\n    public Line setCubic(boolean isCubic) {\n        this.isCubic = isCubic;\n        if (isSquare)\n            setSquare(false);\n        return this;\n    }\n\n    public boolean isSquare() {\n        return isSquare;\n    }\n\n    public Line setSquare(boolean isSquare) {\n        this.isSquare = isSquare;\n        if (isCubic)\n            setCubic(false);\n        return this;\n    }\n\n    public boolean isFilled() {\n        return isFilled;\n    }\n\n    public Line setFilled(boolean isFilled) {\n        this.isFilled = isFilled;\n        return this;\n    }\n\n    /**\n     * @see #setShape(ValueShape)\n     */\n    public ValueShape getShape() {\n        return shape;\n    }\n\n    /**\n     * Set shape for points, possible values: SQUARE, CIRCLE\n     *\n     * @param shape\n     * @return\n     */\n    public Line setShape(ValueShape shape) {\n        this.shape = shape;\n        return this;\n    }\n\n    public PathEffect getPathEffect() {\n        return pathEffect;\n    }\n\n    /**\n     * Set path effect for this line, note: it will slow down drawing, try to not use complicated effects,\n     * DashPathEffect should be safe choice.\n     *\n     * @param pathEffect\n     */\n    public void setPathEffect(PathEffect pathEffect) {\n        this.pathEffect = pathEffect;\n    }\n\n    public LineChartValueFormatter getFormatter() {\n        return formatter;\n    }\n\n    public Line setFormatter(LineChartValueFormatter formatter) {\n        if (null != formatter) {\n            this.formatter = formatter;\n        }\n        return this;\n    }\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/model/LineChartData.java",
    "content": "package lecho.lib.hellocharts.model;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Data model for LineChartView.\n */\npublic class LineChartData extends AbstractChartData {\n    public static final float DEFAULT_BASE_VALUE = 0.0f;\n\n    private List<Line> lines = new ArrayList<Line>();\n    private float baseValue = DEFAULT_BASE_VALUE;\n\n    public LineChartData() {\n\n    }\n\n    public LineChartData(List<Line> lines) {\n        setLines(lines);\n    }\n\n    /**\n     * Copy constructor to perform deep copy of chart data.\n     */\n    public LineChartData(LineChartData data) {\n        super(data);\n        this.baseValue = data.baseValue;\n\n        for (Line line : data.lines) {\n            this.lines.add(new Line(line));\n        }\n    }\n\n    public static LineChartData generateDummyData() {\n        final int numValues = 4;\n        LineChartData data = new LineChartData();\n        List<PointValue> values = new ArrayList<PointValue>(numValues);\n        values.add(new PointValue(0, 2));\n        values.add(new PointValue(1, 4));\n        values.add(new PointValue(2, 3));\n        values.add(new PointValue(3, 4));\n        Line line = new Line(values);\n        List<Line> lines = new ArrayList<Line>(1);\n        lines.add(line);\n        data.setLines(lines);\n        return data;\n    }\n\n    @Override\n    public void update(float scale) {\n        for (Line line : lines) {\n            line.update(scale);\n        }\n    }\n\n    @Override\n    public void finish() {\n        for (Line line : lines) {\n            line.finish();\n        }\n    }\n\n    public List<Line> getLines() {\n        return lines;\n    }\n\n    public LineChartData setLines(List<Line> lines) {\n        if (null == lines) {\n            this.lines = new ArrayList<Line>();\n        } else {\n            this.lines = lines;\n        }\n        return this;\n    }\n\n    /**\n     * @see #setBaseValue(float)\n     */\n    public float getBaseValue() {\n        return baseValue;\n    }\n\n    /**\n     * Set value below which values will be drawn as negative, important attribute for drawing filled area charts, by\n     * default 0.\n     */\n    public LineChartData setBaseValue(float baseValue) {\n        this.baseValue = baseValue;\n        return this;\n    }\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/model/PieChartData.java",
    "content": "package lecho.lib.hellocharts.model;\n\nimport android.graphics.Color;\nimport android.graphics.Typeface;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport lecho.lib.hellocharts.formatter.PieChartValueFormatter;\nimport lecho.lib.hellocharts.formatter.SimplePieChartValueFormatter;\nimport lecho.lib.hellocharts.view.Chart;\nimport lecho.lib.hellocharts.view.PieChartView;\n\n/**\n * Data for PieChart, by default it doesn't have axes.\n */\npublic class PieChartData extends AbstractChartData {\n    public static final int DEFAULT_CENTER_TEXT1_SIZE_SP = 42;\n    public static final int DEFAULT_CENTER_TEXT2_SIZE_SP = 16;\n    public static final float DEFAULT_CENTER_CIRCLE_SCALE = 0.6f;\n    private static final int DEFAULT_SLICE_SPACING_DP = 2;\n    private int centerText1FontSize = DEFAULT_CENTER_TEXT1_SIZE_SP;\n    private int centerText2FontSize = DEFAULT_CENTER_TEXT2_SIZE_SP;\n    private float centerCircleScale = DEFAULT_CENTER_CIRCLE_SCALE;\n    private int slicesSpacing = DEFAULT_SLICE_SPACING_DP;\n    private PieChartValueFormatter formatter = new SimplePieChartValueFormatter();\n    private boolean hasLabels = false;\n    private boolean hasLabelsOnlyForSelected = false;\n    private boolean hasLabelsOutside = false;\n    private boolean hasCenterCircle = false;\n    private int centerCircleColor = Color.TRANSPARENT;\n    private int centerText1Color = Color.BLACK;\n    private Typeface centerText1Typeface;\n    private String centerText1;\n    private int centerText2Color = Color.BLACK;\n    private Typeface centerText2Typeface;\n    private String centerText2;\n\n    private List<SliceValue> values = new ArrayList<SliceValue>();\n\n    public PieChartData() {\n        setAxisXBottom(null);\n        setAxisYLeft(null);\n    }\n\n    public PieChartData(List<SliceValue> values) {\n        setValues(values);\n        // Empty axes. Pie chart don't need axes.\n        setAxisXBottom(null);\n        setAxisYLeft(null);\n    }\n\n    public PieChartData(PieChartData data) {\n        super(data);\n        this.formatter = data.formatter;\n        this.hasLabels = data.hasLabels;\n        this.hasLabelsOnlyForSelected = data.hasLabelsOnlyForSelected;\n        this.hasLabelsOutside = data.hasLabelsOutside;\n\n        this.hasCenterCircle = data.hasCenterCircle;\n        this.centerCircleColor = data.centerCircleColor;\n        this.centerCircleScale = data.centerCircleScale;\n\n        this.centerText1Color = data.centerText1Color;\n        this.centerText1FontSize = data.centerText1FontSize;\n        this.centerText1Typeface = data.centerText1Typeface;\n        this.centerText1 = data.centerText1;\n\n        this.centerText2Color = data.centerText2Color;\n        this.centerText2FontSize = data.centerText2FontSize;\n        this.centerText2Typeface = data.centerText2Typeface;\n        this.centerText2 = data.centerText2;\n\n        for (SliceValue sliceValue : data.values) {\n            this.values.add(new SliceValue(sliceValue));\n        }\n    }\n\n    public static PieChartData generateDummyData() {\n        final int numValues = 4;\n        PieChartData data = new PieChartData();\n        List<SliceValue> values = new ArrayList<SliceValue>(numValues);\n        values.add(new SliceValue(40f));\n        values.add(new SliceValue(20f));\n        values.add(new SliceValue(30f));\n        values.add(new SliceValue(50f));\n        data.setValues(values);\n        return data;\n    }\n\n    @Override\n    public void update(float scale) {\n        for (SliceValue value : values) {\n            value.update(scale);\n        }\n    }\n\n    @Override\n    public void finish() {\n        for (SliceValue value : values) {\n            value.finish();\n        }\n    }\n\n    /**\n     * PieChart does not support axes so method call will be ignored\n     */\n    @Override\n    public void setAxisXBottom(Axis axisX) {\n        super.setAxisXBottom(null);\n    }\n\n    /**\n     * PieChart does not support axes so method call will be ignored\n     */\n    @Override\n    public void setAxisYLeft(Axis axisY) {\n        super.setAxisYLeft(null);\n    }\n\n    public List<SliceValue> getValues() {\n        return values;\n    }\n\n    public PieChartData setValues(List<SliceValue> values) {\n        if (null == values) {\n            this.values = new ArrayList<SliceValue>();\n        } else {\n            this.values = values;\n        }\n        return this;\n    }\n\n    public boolean hasLabels() {\n        return hasLabels;\n    }\n\n    public PieChartData setHasLabels(boolean hasLabels) {\n        this.hasLabels = hasLabels;\n        if (hasLabels) {\n            hasLabelsOnlyForSelected = false;\n        }\n        return this;\n    }\n\n    /**\n     * @see #setHasLabelsOnlyForSelected(boolean)\n     */\n    public boolean hasLabelsOnlyForSelected() {\n        return hasLabelsOnlyForSelected;\n    }\n\n    /**\n     * Set true if you want to show value labels only for selected value, works best when chart has\n     * isValueSelectionEnabled set to true {@link Chart#setValueSelectionEnabled(boolean)}.\n     */\n    public PieChartData setHasLabelsOnlyForSelected(boolean hasLabelsOnlyForSelected) {\n        this.hasLabelsOnlyForSelected = hasLabelsOnlyForSelected;\n        if (hasLabelsOnlyForSelected) {\n            this.hasLabels = false;\n        }\n        return this;\n    }\n\n    public boolean hasLabelsOutside() {\n        return hasLabelsOutside;\n    }\n\n    /**\n     * Set if labels should be drawn inside circle(false) or outside(true). By default false. If you set it to true you\n     * should also change chart fill ration using {@link PieChartView#setCircleFillRatio(float)}. This flag is used only\n     * if you also set hasLabels or hasLabelsOnlyForSelected flags.\n     */\n    public PieChartData setHasLabelsOutside(boolean hasLabelsOutside) {\n        this.hasLabelsOutside = hasLabelsOutside;\n        return this;\n    }\n\n    public boolean hasCenterCircle() {\n        return hasCenterCircle;\n    }\n\n    public PieChartData setHasCenterCircle(boolean hasCenterCircle) {\n        this.hasCenterCircle = hasCenterCircle;\n        return this;\n    }\n\n    public int getCenterCircleColor() {\n        return centerCircleColor;\n    }\n\n    public PieChartData setCenterCircleColor(int centerCircleColor) {\n        this.centerCircleColor = centerCircleColor;\n        return this;\n    }\n\n    public float getCenterCircleScale() {\n        return centerCircleScale;\n    }\n\n    public PieChartData setCenterCircleScale(float centerCircleScale) {\n        this.centerCircleScale = centerCircleScale;\n        return this;\n    }\n\n    public int getCenterText1Color() {\n        return centerText1Color;\n    }\n\n    public PieChartData setCenterText1Color(int centerText1Color) {\n        this.centerText1Color = centerText1Color;\n        return this;\n    }\n\n    public int getCenterText1FontSize() {\n        return centerText1FontSize;\n    }\n\n    public PieChartData setCenterText1FontSize(int centerText1FontSize) {\n        this.centerText1FontSize = centerText1FontSize;\n        return this;\n    }\n\n    public Typeface getCenterText1Typeface() {\n        return centerText1Typeface;\n    }\n\n    public PieChartData setCenterText1Typeface(Typeface text1Typeface) {\n        this.centerText1Typeface = text1Typeface;\n        return this;\n    }\n\n    public String getCenterText1() {\n        return centerText1;\n    }\n\n    public PieChartData setCenterText1(String centerText1) {\n        this.centerText1 = centerText1;\n        return this;\n    }\n\n    public String getCenterText2() {\n        return centerText2;\n    }\n\n    /**\n     * Note that centerText2 will be drawn only if centerText1 is not empty/null.\n     */\n    public PieChartData setCenterText2(String centerText2) {\n        this.centerText2 = centerText2;\n        return this;\n    }\n\n    public int getCenterText2Color() {\n        return centerText2Color;\n    }\n\n    public PieChartData setCenterText2Color(int centerText2Color) {\n        this.centerText2Color = centerText2Color;\n        return this;\n    }\n\n    public int getCenterText2FontSize() {\n        return centerText2FontSize;\n    }\n\n    public PieChartData setCenterText2FontSize(int centerText2FontSize) {\n        this.centerText2FontSize = centerText2FontSize;\n        return this;\n    }\n\n    public Typeface getCenterText2Typeface() {\n        return centerText2Typeface;\n    }\n\n    public PieChartData setCenterText2Typeface(Typeface text2Typeface) {\n        this.centerText2Typeface = text2Typeface;\n        return this;\n    }\n\n    public int getSlicesSpacing() {\n        return slicesSpacing;\n    }\n\n    public PieChartData setSlicesSpacing(int sliceSpacing) {\n        this.slicesSpacing = sliceSpacing;\n        return this;\n    }\n\n    public PieChartValueFormatter getFormatter() {\n        return formatter;\n    }\n\n    public PieChartData setFormatter(PieChartValueFormatter formatter) {\n        if (null != formatter) {\n            this.formatter = formatter;\n        }\n        return this;\n    }\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/model/PointValue.java",
    "content": "package lecho.lib.hellocharts.model;\n\nimport java.util.Arrays;\n\nimport lecho.lib.hellocharts.view.Chart;\n\n/**\n * Single point coordinates, used for LineChartData.\n */\npublic class PointValue {\n\n    private float x;\n    private float y;\n    private float originX;\n    private float originY;\n    private float diffX;\n    private float diffY;\n    private char[] label;\n\n    public PointValue() {\n        set(0, 0);\n    }\n\n    public PointValue(float x, float y) {\n        set(x, y);\n    }\n\n    public PointValue(PointValue pointValue) {\n        set(pointValue.x, pointValue.y);\n        this.label = pointValue.label;\n    }\n\n    public void update(float scale) {\n        x = originX + diffX * scale;\n        y = originY + diffY * scale;\n    }\n\n    public void finish() {\n        set(originX + diffX, originY + diffY);\n    }\n\n    public PointValue set(float x, float y) {\n        this.x = x;\n        this.y = y;\n        this.originX = x;\n        this.originY = y;\n        this.diffX = 0;\n        this.diffY = 0;\n        return this;\n    }\n\n    /**\n     * Set target values that should be reached when data animation finish then call {@link Chart#startDataAnimation()}\n     */\n    public PointValue setTarget(float targetX, float targetY) {\n        set(x, y);\n        this.diffX = targetX - originX;\n        this.diffY = targetY - originY;\n        return this;\n    }\n\n    public float getX() {\n        return this.x;\n    }\n\n    public float getY() {\n        return this.y;\n    }\n\n    @Deprecated\n    public char[] getLabel() {\n        return label;\n    }\n\n    public PointValue setLabel(String label) {\n        this.label = label.toCharArray();\n        return this;\n    }\n\n    public char[] getLabelAsChars() {\n        return label;\n    }\n\n    @Deprecated\n    public PointValue setLabel(char[] label) {\n        this.label = label;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"PointValue [x=\" + x + \", y=\" + y + \"]\";\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n\n        PointValue that = (PointValue) o;\n\n        if (Float.compare(that.diffX, diffX) != 0) return false;\n        if (Float.compare(that.diffY, diffY) != 0) return false;\n        if (Float.compare(that.originX, originX) != 0) return false;\n        if (Float.compare(that.originY, originY) != 0) return false;\n        if (Float.compare(that.x, x) != 0) return false;\n        if (Float.compare(that.y, y) != 0) return false;\n        if (!Arrays.equals(label, that.label)) return false;\n\n        return true;\n    }\n\n    @Override\n    public int hashCode() {\n        int result = (x != +0.0f ? Float.floatToIntBits(x) : 0);\n        result = 31 * result + (y != +0.0f ? Float.floatToIntBits(y) : 0);\n        result = 31 * result + (originX != +0.0f ? Float.floatToIntBits(originX) : 0);\n        result = 31 * result + (originY != +0.0f ? Float.floatToIntBits(originY) : 0);\n        result = 31 * result + (diffX != +0.0f ? Float.floatToIntBits(diffX) : 0);\n        result = 31 * result + (diffY != +0.0f ? Float.floatToIntBits(diffY) : 0);\n        result = 31 * result + (label != null ? Arrays.hashCode(label) : 0);\n        return result;\n    }\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/model/SelectedValue.java",
    "content": "package lecho.lib.hellocharts.model;\n\n/**\n * Holds selected values indexes, i.e. for LineChartModel it will be firstIndex=lineIndex; secondIndex=valueIndex.\n */\npublic class SelectedValue {\n\n    /**\n     * First index i.e for LineChart that will be line index.\n     */\n    private int firstIndex;\n\n    /**\n     * Second index i.e for LineChart that will be PointValue index.\n     */\n    private int secondIndex;\n\n    /**\n     * Used only for combo charts, in other cases should have value NONE.\n     */\n    private SelectedValueType type = SelectedValueType.NONE;\n\n    public SelectedValue() {\n        clear();\n    }\n\n    public SelectedValue(int firstIndex, int secondIndex, SelectedValueType type) {\n        set(firstIndex, secondIndex, type);\n    }\n\n    public void set(int firstIndex, int secondIndex, SelectedValueType type) {\n        this.firstIndex = firstIndex;\n        this.secondIndex = secondIndex;\n        if (null != type) {\n            this.type = type;\n        } else {\n            this.type = SelectedValueType.NONE;\n        }\n    }\n\n    public void set(SelectedValue selectedValue) {\n        this.firstIndex = selectedValue.firstIndex;\n        this.secondIndex = selectedValue.secondIndex;\n        this.type = selectedValue.type;\n    }\n\n    public void clear() {\n        set(Integer.MIN_VALUE, Integer.MIN_VALUE, SelectedValueType.NONE);\n    }\n\n    /**\n     * Return true if selected value have meaningful value.\n     */\n    public boolean isSet() {\n        if (firstIndex >= 0 && secondIndex >= 0) {\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n    /**\n     * First index i.e for LineChart that will be line index.\n     */\n    public int getFirstIndex() {\n        return firstIndex;\n    }\n\n    public void setFirstIndex(int firstIndex) {\n        this.firstIndex = firstIndex;\n    }\n\n    /**\n     * Second index i.e for LineChart that will be PointValue index.\n     */\n    public int getSecondIndex() {\n        return secondIndex;\n    }\n\n    public void setSecondIndex(int secondIndex) {\n        this.secondIndex = secondIndex;\n    }\n\n    public SelectedValueType getType() {\n        return type;\n    }\n\n    public void setType(SelectedValueType type) {\n        this.type = type;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + firstIndex;\n        result = prime * result + secondIndex;\n        result = prime * result + ((type == null) ? 0 : type.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj)\n            return true;\n        if (obj == null)\n            return false;\n        if (getClass() != obj.getClass())\n            return false;\n        SelectedValue other = (SelectedValue) obj;\n        if (firstIndex != other.firstIndex)\n            return false;\n        if (secondIndex != other.secondIndex)\n            return false;\n        if (type != other.type)\n            return false;\n        return true;\n    }\n\n    @Override\n    public String toString() {\n        return \"SelectedValue [firstIndex=\" + firstIndex + \", secondIndex=\" + secondIndex + \", type=\" + type + \"]\";\n    }\n\n    /**\n     * Used in combo chart to determine if selected value is used for line or column selection.\n     */\n    public enum SelectedValueType {\n        NONE, LINE, COLUMN\n    }\n\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/model/SliceValue.java",
    "content": "package lecho.lib.hellocharts.model;\n\nimport java.util.Arrays;\n\nimport lecho.lib.hellocharts.util.ChartUtils;\nimport lecho.lib.hellocharts.view.Chart;\n\n/**\n * Model representing single slice on PieChart.\n */\npublic class SliceValue {\n    private static final int DEFAULT_SLICE_SPACING_DP = 2;\n    @Deprecated\n    /** Spacing between this slice and its neighbors. */\n    private int sliceSpacing = DEFAULT_SLICE_SPACING_DP;\n    /**\n     * Current value of this slice.\n     */\n    private float value;\n    /**\n     * Origin value of this slice, used during value animation.\n     */\n    private float originValue;\n    /**\n     * Difference between originValue and targetValue.\n     */\n    private float diff;\n    /**\n     * Color of this slice.\n     */\n    private int color = ChartUtils.DEFAULT_COLOR;\n    /**\n     * Darken color used to draw label background and give touch feedback.\n     */\n    private int darkenColor = ChartUtils.DEFAULT_DARKEN_COLOR;\n    /**\n     * Custom label for this slice, if not set number formatting will be used.\n     */\n    private char[] label;\n\n    public SliceValue() {\n        setValue(0);\n    }\n\n    public SliceValue(float value) {\n        setValue(value);\n    }\n\n    public SliceValue(float value, int color) {\n        setValue(value);\n        setColor(color);\n    }\n\n    public SliceValue(float value, int color, int sliceSpacing) {\n        setValue(value);\n        setColor(color);\n        this.sliceSpacing = sliceSpacing;\n    }\n\n    public SliceValue(SliceValue sliceValue) {\n        setValue(sliceValue.value);\n        setColor(sliceValue.color);\n        this.sliceSpacing = sliceValue.sliceSpacing;\n        this.label = sliceValue.label;\n    }\n\n    public void update(float scale) {\n        value = originValue + diff * scale;\n    }\n\n    public void finish() {\n        setValue(originValue + diff);\n    }\n\n    public float getValue() {\n        return value;\n    }\n\n    public SliceValue setValue(float value) {\n        this.value = value;\n        this.originValue = value;\n        this.diff = 0;\n        return this;\n    }\n\n    /**\n     * Set target value that should be reached when data animation finish then call {@link Chart#startDataAnimation()}\n     *\n     * @param target\n     * @return\n     */\n    public SliceValue setTarget(float target) {\n        setValue(value);\n        this.diff = target - originValue;\n        return this;\n    }\n\n    public int getColor() {\n        return color;\n    }\n\n    public SliceValue setColor(int color) {\n        this.color = color;\n        this.darkenColor = ChartUtils.darkenColor(color);\n        return this;\n    }\n\n    public int getDarkenColor() {\n        return darkenColor;\n    }\n\n    @Deprecated\n    public int getSliceSpacing() {\n        return sliceSpacing;\n    }\n\n    @Deprecated\n    public SliceValue setSliceSpacing(int sliceSpacing) {\n        this.sliceSpacing = sliceSpacing;\n        return this;\n    }\n\n    @Deprecated\n    public char[] getLabel() {\n        return label;\n    }\n\n    @Deprecated\n    public SliceValue setLabel(char[] label) {\n        this.label = label;\n        return this;\n    }\n\n    public SliceValue setLabel(String label) {\n        this.label = label.toCharArray();\n        return this;\n    }\n\n    public char[] getLabelAsChars() {\n        return label;\n    }\n\n    @Override\n    public String toString() {\n        return \"SliceValue [value=\" + value + \"]\";\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n\n        SliceValue that = (SliceValue) o;\n\n        if (color != that.color) return false;\n        if (darkenColor != that.darkenColor) return false;\n        if (Float.compare(that.diff, diff) != 0) return false;\n        if (Float.compare(that.originValue, originValue) != 0) return false;\n        if (sliceSpacing != that.sliceSpacing) return false;\n        if (Float.compare(that.value, value) != 0) return false;\n        if (!Arrays.equals(label, that.label)) return false;\n\n        return true;\n    }\n\n    @Override\n    public int hashCode() {\n        int result = (value != +0.0f ? Float.floatToIntBits(value) : 0);\n        result = 31 * result + (originValue != +0.0f ? Float.floatToIntBits(originValue) : 0);\n        result = 31 * result + (diff != +0.0f ? Float.floatToIntBits(diff) : 0);\n        result = 31 * result + color;\n        result = 31 * result + darkenColor;\n        result = 31 * result + sliceSpacing;\n        result = 31 * result + (label != null ? Arrays.hashCode(label) : 0);\n        return result;\n    }\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/model/SubcolumnValue.java",
    "content": "package lecho.lib.hellocharts.model;\n\nimport java.util.Arrays;\n\nimport lecho.lib.hellocharts.util.ChartUtils;\nimport lecho.lib.hellocharts.view.Chart;\n\n/**\n * Single sub-column value for ColumnChart.\n */\npublic class SubcolumnValue {\n\n    private float value;\n    private float originValue;\n    private float diff;\n    private int color = ChartUtils.DEFAULT_COLOR;\n    private int darkenColor = ChartUtils.DEFAULT_DARKEN_COLOR;\n    private char[] label;\n\n    public SubcolumnValue() {\n        setValue(0);\n    }\n\n    public SubcolumnValue(float value) {\n        // point and targetPoint have to be different objects\n        setValue(value);\n    }\n\n    public SubcolumnValue(float value, int color) {\n        // point and targetPoint have to be different objects\n        setValue(value);\n        setColor(color);\n    }\n\n    public SubcolumnValue(SubcolumnValue columnValue) {\n        setValue(columnValue.value);\n        setColor(columnValue.color);\n        this.label = columnValue.label;\n    }\n\n    public void update(float scale) {\n        value = originValue + diff * scale;\n    }\n\n    public void finish() {\n        setValue(originValue + diff);\n    }\n\n    public float getValue() {\n        return value;\n    }\n\n    public SubcolumnValue setValue(float value) {\n        this.value = value;\n        this.originValue = value;\n        this.diff = 0;\n        return this;\n    }\n\n    /**\n     * Set target value that should be reached when data animation finish then call {@link Chart#startDataAnimation()}\n     *\n     * @param target\n     * @return\n     */\n    public SubcolumnValue setTarget(float target) {\n        setValue(value);\n        this.diff = target - originValue;\n        return this;\n    }\n\n    public int getColor() {\n        return color;\n    }\n\n    public SubcolumnValue setColor(int color) {\n        this.color = color;\n        this.darkenColor = ChartUtils.darkenColor(color);\n        return this;\n    }\n\n    public int getDarkenColor() {\n        return darkenColor;\n    }\n\n    @Deprecated\n    public char[] getLabel() {\n        return label;\n    }\n\n    public SubcolumnValue setLabel(String label) {\n        this.label = label.toCharArray();\n        return this;\n    }\n\n    public char[] getLabelAsChars() {\n        return label;\n    }\n\n    @Deprecated\n    public SubcolumnValue setLabel(char[] label) {\n        this.label = label;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ColumnValue [value=\" + value + \"]\";\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n\n        SubcolumnValue that = (SubcolumnValue) o;\n\n        if (color != that.color) return false;\n        if (darkenColor != that.darkenColor) return false;\n        if (Float.compare(that.diff, diff) != 0) return false;\n        if (Float.compare(that.originValue, originValue) != 0) return false;\n        if (Float.compare(that.value, value) != 0) return false;\n        if (!Arrays.equals(label, that.label)) return false;\n\n        return true;\n    }\n\n    @Override\n    public int hashCode() {\n        int result = (value != +0.0f ? Float.floatToIntBits(value) : 0);\n        result = 31 * result + (originValue != +0.0f ? Float.floatToIntBits(originValue) : 0);\n        result = 31 * result + (diff != +0.0f ? Float.floatToIntBits(diff) : 0);\n        result = 31 * result + color;\n        result = 31 * result + darkenColor;\n        result = 31 * result + (label != null ? Arrays.hashCode(label) : 0);\n        return result;\n    }\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/model/ValueShape.java",
    "content": "package lecho.lib.hellocharts.model;\n\npublic enum ValueShape {\n    CIRCLE, SQUARE, DIAMOND\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/model/Viewport.java",
    "content": "package lecho.lib.hellocharts.model;\n\nimport android.os.Parcel;\nimport android.os.Parcelable;\n\n/**\n * Partial copy of android.graphics.Rect but here the top should be greater then the bottom. Viewport holds 4 float\n * coordinates for a chart extremes. The viewport is represented by the coordinates of its 4 edges (left, top, right\n * bottom). These fields can be accessed directly. Use width() and height() to retrieve the viewport's width and height.\n * Note: most methods do not check to see that the coordinates are sorted correctly (i.e. left is less than right and\n * bottom is less than top). Viewport implements Parcerable.\n */\npublic class Viewport implements Parcelable {\n\n    public float left;\n    public float top;\n    public float right;\n    public float bottom;\n    public static final Parcelable.Creator<Viewport> CREATOR = new Parcelable.Creator<Viewport>() {\n        /**\n         * Return a new viewport from the data in the specified parcel.\n         */\n        public Viewport createFromParcel(Parcel in) {\n            Viewport v = new Viewport();\n            v.readFromParcel(in);\n            return v;\n        }\n\n        /**\n         * Return an array of viewports of the specified size.\n         */\n        public Viewport[] newArray(int size) {\n            return new Viewport[size];\n        }\n    };\n\n    /**\n     * Create a new empty Viewport. All coordinates are initialized to 0.\n     */\n    public Viewport() {\n    }\n\n    /**\n     * Create a new viewport with the specified coordinates. Note: no range checking is performed, so the caller must\n     * ensure that left is less than right and bottom is less than top.\n     *\n     * @param left   The X coordinate of the left side of the viewport\n     * @param top    The Y coordinate of the top of the viewport\n     * @param right  The X coordinate of the right side of the viewport\n     * @param bottom The Y coordinate of the bottom of the viewport\n     */\n    public Viewport(float left, float top, float right, float bottom) {\n        this.left = left;\n        this.top = top;\n        this.right = right;\n        this.bottom = bottom;\n    }\n\n    /**\n     * Create a new viewport, initialized with the values in the specified viewport (which is left unmodified).\n     *\n     * @param v The viewport whose coordinates are copied into the new viewport.\n     */\n    public Viewport(Viewport v) {\n        if (v == null) {\n            left = top = right = bottom = 0.0f;\n        } else {\n            left = v.left;\n            top = v.top;\n            right = v.right;\n            bottom = v.bottom;\n        }\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj)\n            return true;\n        if (obj == null)\n            return false;\n        if (getClass() != obj.getClass())\n            return false;\n        Viewport other = (Viewport) obj;\n        if (Float.floatToIntBits(bottom) != Float.floatToIntBits(other.bottom))\n            return false;\n        if (Float.floatToIntBits(left) != Float.floatToIntBits(other.left))\n            return false;\n        if (Float.floatToIntBits(right) != Float.floatToIntBits(other.right))\n            return false;\n        if (Float.floatToIntBits(top) != Float.floatToIntBits(other.top))\n            return false;\n        return true;\n    }\n\n    /**\n     * Returns true if the viewport is empty {@code left >= right or bottom >= top}\n     */\n    public final boolean isEmpty() {\n        return left >= right || bottom >= top;\n    }\n\n    /**\n     * Set the viewport to (0,0,0,0)\n     */\n    public void setEmpty() {\n        left = right = top = bottom = 0;\n    }\n\n    /**\n     * @return the viewport's width. This does not check for a valid viewport (i.e. {@code left <= right}) so the\n     * result may be negative.\n     */\n    public final float width() {\n        return right - left;\n    }\n\n    /**\n     * @return the viewport's height. This does not check for a valid viewport (i.e. {@code top <= bottom}) so the\n     * result may be negative.\n     */\n    public final float height() {\n        return top - bottom;\n    }\n\n    /**\n     * @return the horizontal center of the viewport. This does not check for a valid viewport (i.e. {@code left <=\n     * right})\n     */\n    public final float centerX() {\n        return (left + right) * 0.5f;\n    }\n\n    /**\n     * @return the vertical center of the viewport. This does not check for a valid viewport (i.e. {@code bottom <=\n     * top})\n     */\n    public final float centerY() {\n        return (top + bottom) * 0.5f;\n    }\n\n    /**\n     * Set the viewport's coordinates to the specified values. Note: no range checking is performed, so it is up to the\n     * caller to ensure that {@code left <= right and bottom <= top}.\n     *\n     * @param left   The X coordinate of the left side of the viewport\n     * @param top    The Y coordinate of the top of the viewport\n     * @param right  The X coordinate of the right side of the viewport\n     * @param bottom The Y coordinate of the bottom of the viewport\n     */\n    public void set(float left, float top, float right, float bottom) {\n        this.left = left;\n        this.top = top;\n        this.right = right;\n        this.bottom = bottom;\n    }\n\n    /**\n     * Copy the coordinates from src into this viewport.\n     *\n     * @param src The viewport whose coordinates are copied into this viewport.\n     */\n    public void set(Viewport src) {\n        this.left = src.left;\n        this.top = src.top;\n        this.right = src.right;\n        this.bottom = src.bottom;\n    }\n\n    /**\n     * Offset the viewport by adding dx to its left and right coordinates, and adding dy to its top and bottom\n     * coordinates.\n     *\n     * @param dx The amount to add to the viewport's left and right coordinates\n     * @param dy The amount to add to the viewport's top and bottom coordinates\n     */\n    public void offset(float dx, float dy) {\n        left += dx;\n        top += dy;\n        right += dx;\n        bottom += dy;\n    }\n\n    /**\n     * Offset the viewport to a specific (left, top) position, keeping its width and height the same.\n     *\n     * @param newLeft The new \"left\" coordinate for the viewport\n     * @param newTop  The new \"top\" coordinate for the viewport\n     */\n    public void offsetTo(float newLeft, float newTop) {\n        right += newLeft - left;\n        bottom += newTop - top;\n        left = newLeft;\n        top = newTop;\n    }\n\n    /**\n     * Inset the viewport by (dx,dy). If dx is positive, then the sides are moved inwards, making the viewport narrower.\n     * If dx is negative, then the sides are moved outwards, making the viewport wider. The same holds true for dy and\n     * the top and bottom.\n     *\n     * @param dx The amount to add(subtract) from the viewport's left(right)\n     * @param dy The amount to add(subtract) from the viewport's top(bottom)\n     */\n    public void inset(float dx, float dy) {\n        left += dx;\n        top -= dy;\n        right -= dx;\n        bottom += dy;\n    }\n\n    /**\n     * Returns true if (x,y) is inside the viewport. The left and top are considered to be inside, while the right and\n     * bottom are not. This means that for a x,y to be contained: {@code left <= x < right and bottom <= y < top}. An\n     * empty viewport never contains any point.\n     *\n     * @param x The X coordinate of the point being tested for containment\n     * @param y The Y coordinate of the point being tested for containment\n     * @return true iff (x,y) are contained by the viewport, where containment means {@code left <= x < right and top <=\n     * y < bottom}\n     */\n    public boolean contains(float x, float y) {\n        return left < right && bottom < top // check for empty first\n                && x >= left && x < right && y >= bottom && y < top;\n    }\n\n    /**\n     * Returns true iff the 4 specified sides of a viewport are inside or equal to this viewport. i.e. is this viewport\n     * a superset of the specified viewport. An empty viewport never contains another viewport.\n     *\n     * @param left   The left side of the viewport being tested for containment\n     * @param top    The top of the viewport being tested for containment\n     * @param right  The right side of the viewport being tested for containment\n     * @param bottom The bottom of the viewport being tested for containment\n     * @return true iff the the 4 specified sides of a viewport are inside or equal to this viewport\n     */\n    public boolean contains(float left, float top, float right, float bottom) {\n        // check for empty first\n        return this.left < this.right && this.bottom < this.top\n                // now check for containment\n                && this.left <= left && this.top >= top && this.right >= right && this.bottom <= bottom;\n    }\n\n    /**\n     * Returns true iff the specified viewport r is inside or equal to this viewport. An empty viewport never contains\n     * another viewport.\n     *\n     * @param v The viewport being tested for containment.\n     * @return true iff the specified viewport r is inside or equal to this viewport\n     */\n    public boolean contains(Viewport v) {\n        // check for empty first\n        return this.left < this.right && this.bottom < this.top\n                // now check for containment\n                && left <= v.left && top >= v.top && right >= v.right && bottom <= v.bottom;\n    }\n\n    /**\n     * Update this Viewport to enclose itself and the specified viewport. If the specified viewport is empty, nothing is\n     * done. If this viewport is empty it is set to the specified viewport.\n     *\n     * @param left   The left edge being unioned with this viewport\n     * @param top    The top edge being unioned with this viewport\n     * @param right  The right edge being unioned with this viewport\n     * @param bottom The bottom edge being unioned with this viewport\n     */\n    public void union(float left, float top, float right, float bottom) {\n        if ((left < right) && (bottom < top)) {\n            if ((this.left < this.right) && (this.bottom < this.top)) {\n                if (this.left > left)\n                    this.left = left;\n                if (this.top < top)\n                    this.top = top;\n                if (this.right < right)\n                    this.right = right;\n                if (this.bottom > bottom)\n                    this.bottom = bottom;\n            } else {\n                this.left = left;\n                this.top = top;\n                this.right = right;\n                this.bottom = bottom;\n            }\n        }\n    }\n\n    /**\n     * Update this Viewport to enclose itself and the specified viewport. If the specified viewport is empty, nothing is\n     * done. If this viewport is empty it is set to the specified viewport.\n     *\n     * @param v The viewport being unioned with this viewport\n     */\n    public void union(Viewport v) {\n        union(v.left, v.top, v.right, v.bottom);\n    }\n\n    /**\n     * If the viewport specified by left,top,right,bottom intersects this viewport, return true and set this viewport to\n     * that intersection, otherwise return false and do not change this viewport. No check is performed to see if either\n     * viewport is empty. Note: To just test for intersection, use intersects()\n     *\n     * @param left   The left side of the viewport being intersected with this viewport\n     * @param top    The top of the viewport being intersected with this viewport\n     * @param right  The right side of the viewport being intersected with this viewport.\n     * @param bottom The bottom of the viewport being intersected with this viewport.\n     * @return true if the specified viewport and this viewport intersect (and this viewport is then set to that\n     * intersection) else return false and do not change this viewport.\n     */\n    public boolean intersect(float left, float top, float right, float bottom) {\n        if (this.left < right && left < this.right && this.bottom < top && bottom < this.top) {\n            if (this.left < left) {\n                this.left = left;\n            }\n            if (this.top > top) {\n                this.top = top;\n            }\n            if (this.right > right) {\n                this.right = right;\n            }\n            if (this.bottom < bottom) {\n                this.bottom = bottom;\n            }\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * If the specified viewport intersects this viewport, return true and set this viewport to that intersection,\n     * otherwise return false and do not change this viewport. No check is performed to see if either viewport is empty.\n     * To just test for intersection, use intersects()\n     *\n     * @param v The viewport being intersected with this viewport.\n     * @return true if the specified viewport and this viewport intersect (and this viewport is then set to that\n     * intersection) else return false and do not change this viewport.\n     */\n    public boolean intersect(Viewport v) {\n        return intersect(v.left, v.top, v.right, v.bottom);\n    }\n\n    @Override\n    public String toString() {\n        return \"Viewport [left=\" + left + \", top=\" + top + \", right=\" + right + \", bottom=\" + bottom + \"]\";\n    }\n\n    // ** PARCERABLE **\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + Float.floatToIntBits(bottom);\n        result = prime * result + Float.floatToIntBits(left);\n        result = prime * result + Float.floatToIntBits(right);\n        result = prime * result + Float.floatToIntBits(top);\n        return result;\n    }\n\n    /**\n     * Parcelable interface methods\n     */\n    public int describeContents() {\n        return 0;\n    }\n\n    /**\n     * Write this viewport to the specified parcel. To restore a viewport from a parcel, use readFromParcel()\n     *\n     * @param out The parcel to write the viewport's coordinates into\n     */\n    public void writeToParcel(Parcel out, int flags) {\n        out.writeFloat(left);\n        out.writeFloat(top);\n        out.writeFloat(right);\n        out.writeFloat(bottom);\n    }\n\n    /**\n     * Set the viewport's coordinates from the data stored in the specified parcel. To write a viewport to a parcel,\n     * call writeToParcel().\n     *\n     * @param in The parcel to read the viewport's coordinates from\n     */\n    public void readFromParcel(Parcel in) {\n        left = in.readFloat();\n        top = in.readFloat();\n        right = in.readFloat();\n        bottom = in.readFloat();\n    }\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/provider/BubbleChartDataProvider.java",
    "content": "package lecho.lib.hellocharts.provider;\n\nimport lecho.lib.hellocharts.model.BubbleChartData;\n\npublic interface BubbleChartDataProvider {\n\n    public BubbleChartData getBubbleChartData();\n\n    public void setBubbleChartData(BubbleChartData data);\n\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/provider/ColumnChartDataProvider.java",
    "content": "package lecho.lib.hellocharts.provider;\n\nimport lecho.lib.hellocharts.model.ColumnChartData;\n\npublic interface ColumnChartDataProvider {\n\n    public ColumnChartData getColumnChartData();\n\n    public void setColumnChartData(ColumnChartData data);\n\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/provider/ComboLineColumnChartDataProvider.java",
    "content": "package lecho.lib.hellocharts.provider;\n\nimport lecho.lib.hellocharts.model.ComboLineColumnChartData;\n\npublic interface ComboLineColumnChartDataProvider {\n\n    public ComboLineColumnChartData getComboLineColumnChartData();\n\n    public void setComboLineColumnChartData(ComboLineColumnChartData data);\n\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/provider/LineChartDataProvider.java",
    "content": "package lecho.lib.hellocharts.provider;\n\nimport lecho.lib.hellocharts.model.LineChartData;\n\npublic interface LineChartDataProvider {\n\n    public LineChartData getLineChartData();\n\n    public void setLineChartData(LineChartData data);\n\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/provider/PieChartDataProvider.java",
    "content": "package lecho.lib.hellocharts.provider;\n\nimport lecho.lib.hellocharts.model.PieChartData;\n\npublic interface PieChartDataProvider {\n\n    public PieChartData getPieChartData();\n\n    public void setPieChartData(PieChartData data);\n\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/renderer/AbstractChartRenderer.java",
    "content": "package lecho.lib.hellocharts.renderer;\n\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Paint;\nimport android.graphics.Paint.Align;\nimport android.graphics.Paint.FontMetricsInt;\nimport android.graphics.RectF;\nimport android.graphics.Typeface;\n\nimport lecho.lib.hellocharts.computator.ChartComputator;\nimport lecho.lib.hellocharts.model.ChartData;\nimport lecho.lib.hellocharts.model.SelectedValue;\nimport lecho.lib.hellocharts.model.Viewport;\nimport lecho.lib.hellocharts.util.ChartUtils;\nimport lecho.lib.hellocharts.view.Chart;\n\n/**\n * Abstract renderer implementation, every chart renderer extends this class(although it is not required it helps).\n */\npublic abstract class AbstractChartRenderer implements ChartRenderer {\n    public int DEFAULT_LABEL_MARGIN_DP = 4;\n    protected Chart chart;\n    protected ChartComputator computator;\n    /**\n     * Paint for value labels.\n     */\n    protected Paint labelPaint = new Paint();\n    /**\n     * Paint for labels background.\n     */\n    protected Paint labelBackgroundPaint = new Paint();\n    /**\n     * Holds coordinates for label background rect.\n     */\n    protected RectF labelBackgroundRect = new RectF();\n    /**\n     * Font metrics for label paint, used to determine text height.\n     */\n    protected FontMetricsInt fontMetrics = new FontMetricsInt();\n    /**\n     * If true maximum and current viewport will be calculated when chart data change or during data animations.\n     */\n    protected boolean isViewportCalculationEnabled = true;\n    protected float density;\n    protected float scaledDensity;\n    protected SelectedValue selectedValue = new SelectedValue();\n    protected char[] labelBuffer = new char[64];\n    protected int labelOffset;\n    protected int labelMargin;\n    protected boolean isValueLabelBackgroundEnabled;\n    protected boolean isValueLabelBackgroundAuto;\n\n    public AbstractChartRenderer(Context context, Chart chart) {\n        this.density = context.getResources().getDisplayMetrics().density;\n        this.scaledDensity = context.getResources().getDisplayMetrics().scaledDensity;\n        this.chart = chart;\n        this.computator = chart.getChartComputator();\n\n        labelMargin = ChartUtils.dp2px(density, DEFAULT_LABEL_MARGIN_DP);\n        labelOffset = labelMargin;\n\n        labelPaint.setAntiAlias(true);\n        labelPaint.setStyle(Paint.Style.FILL);\n        labelPaint.setTextAlign(Align.LEFT);\n        labelPaint.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));\n        labelPaint.setColor(Color.WHITE);\n\n        labelBackgroundPaint.setAntiAlias(true);\n        labelBackgroundPaint.setStyle(Paint.Style.FILL);\n    }\n\n    @Override\n    public void resetRenderer() {\n        this.computator = chart.getChartComputator();\n    }\n\n    @Override\n    public void onChartDataChanged() {\n        final ChartData data = chart.getChartData();\n\n        Typeface typeface = chart.getChartData().getValueLabelTypeface();\n        if (null != typeface) {\n            labelPaint.setTypeface(typeface);\n        }\n\n        labelPaint.setColor(data.getValueLabelTextColor());\n        labelPaint.setTextSize(ChartUtils.sp2px(scaledDensity, data.getValueLabelTextSize()));\n        labelPaint.getFontMetricsInt(fontMetrics);\n\n        this.isValueLabelBackgroundEnabled = data.isValueLabelBackgroundEnabled();\n        this.isValueLabelBackgroundAuto = data.isValueLabelBackgroundAuto();\n        this.labelBackgroundPaint.setColor(data.getValueLabelBackgroundColor());\n\n        // Important - clear selection when data changed.\n        selectedValue.clear();\n\n    }\n\n    /**\n     * Draws label text and label background if isValueLabelBackgroundEnabled is true.\n     */\n    protected void drawLabelTextAndBackground(Canvas canvas, char[] labelBuffer, int startIndex, int numChars,\n                                              int autoBackgroundColor) {\n        final float textX;\n        final float textY;\n\n        if (isValueLabelBackgroundEnabled) {\n\n            if (isValueLabelBackgroundAuto) {\n                labelBackgroundPaint.setColor(autoBackgroundColor);\n            }\n\n            canvas.drawRect(labelBackgroundRect, labelBackgroundPaint);\n\n            textX = labelBackgroundRect.left + labelMargin;\n            textY = labelBackgroundRect.bottom - labelMargin;\n        } else {\n            textX = labelBackgroundRect.left;\n            textY = labelBackgroundRect.bottom;\n        }\n\n        canvas.drawText(labelBuffer, startIndex, numChars, textX, textY, labelPaint);\n    }\n\n    @Override\n    public boolean isTouched() {\n        return selectedValue.isSet();\n    }\n\n    @Override\n    public void clearTouch() {\n        selectedValue.clear();\n    }\n\n    @Override\n    public Viewport getMaximumViewport() {\n        return computator.getMaximumViewport();\n    }\n\n    @Override\n    public void setMaximumViewport(Viewport maxViewport) {\n        if (null != maxViewport) {\n            computator.setMaxViewport(maxViewport);\n        }\n    }\n\n    @Override\n    public Viewport getCurrentViewport() {\n        return computator.getCurrentViewport();\n    }\n\n    @Override\n    public void setCurrentViewport(Viewport viewport) {\n        if (null != viewport) {\n            computator.setCurrentViewport(viewport);\n        }\n    }\n\n    @Override\n    public boolean isViewportCalculationEnabled() {\n        return isViewportCalculationEnabled;\n    }\n\n    @Override\n    public void setViewportCalculationEnabled(boolean isEnabled) {\n        this.isViewportCalculationEnabled = isEnabled;\n    }\n\n    @Override\n    public void selectValue(SelectedValue selectedValue) {\n        this.selectedValue.set(selectedValue);\n    }\n\n    @Override\n    public SelectedValue getSelectedValue() {\n        return selectedValue;\n    }\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/renderer/AxesRenderer.java",
    "content": "package lecho.lib.hellocharts.renderer;\n\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.graphics.Paint;\nimport android.graphics.Paint.Align;\nimport android.graphics.Paint.FontMetricsInt;\nimport android.graphics.Rect;\nimport android.graphics.Typeface;\nimport android.text.TextUtils;\n\nimport lecho.lib.hellocharts.computator.ChartComputator;\nimport lecho.lib.hellocharts.model.Axis;\nimport lecho.lib.hellocharts.model.AxisValue;\nimport lecho.lib.hellocharts.model.Viewport;\nimport lecho.lib.hellocharts.util.AxisAutoValues;\nimport lecho.lib.hellocharts.util.ChartUtils;\nimport lecho.lib.hellocharts.util.FloatUtils;\nimport lecho.lib.hellocharts.view.Chart;\n\n/**\n * Default axes renderer. Can draw maximum four axes - two horizontal(top/bottom) and two vertical(left/right).\n */\npublic class AxesRenderer {\n    private static final int DEFAULT_AXIS_MARGIN_DP = 2;\n\n    /**\n     * Axis positions indexes, used for indexing tabs that holds axes parameters, see below.\n     */\n    private static final int TOP = 0;\n    private static final int LEFT = 1;\n    private static final int RIGHT = 2;\n    private static final int BOTTOM = 3;\n\n    /**\n     * Used to measure label width. If label has mas 5 characters only 5 first characters of this array are used to\n     * measure text width.\n     */\n    private static final char[] labelWidthChars = new char[]{\n            '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',\n            '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',\n            '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',\n            '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0'};\n\n    private Chart chart;\n    private ChartComputator computator;\n    private int axisMargin;\n    private float density;\n    private float scaledDensity;\n    private Paint[] labelPaintTab = new Paint[]{new Paint(), new Paint(), new Paint(), new Paint()};\n    private Paint[] namePaintTab = new Paint[]{new Paint(), new Paint(), new Paint(), new Paint()};\n    private Paint[] linePaintTab = new Paint[]{new Paint(), new Paint(), new Paint(), new Paint()};\n    private float[] nameBaselineTab = new float[4];\n    private float[] labelBaselineTab = new float[4];\n    private float[] separationLineTab = new float[4];\n    private int[] labelWidthTab = new int[4];\n    private int[] labelTextAscentTab = new int[4];\n    private int[] labelTextDescentTab = new int[4];\n    private int[] labelDimensionForMarginsTab = new int[4];\n    private int[] labelDimensionForStepsTab = new int[4];\n    private int[] tiltedLabelXTranslation = new int[4];\n    private int[] tiltedLabelYTranslation = new int[4];\n    private FontMetricsInt[] fontMetricsTab = new FontMetricsInt[]{new FontMetricsInt(), new FontMetricsInt(),\n            new FontMetricsInt(), new FontMetricsInt()};\n    /**\n     * Holds formatted axis value label.\n     */\n    private char[] labelBuffer = new char[64];\n\n    /**\n     * Holds number of values that should be drown for each axis.\n     */\n    private int[] valuesToDrawNumTab = new int[4];\n\n    /**\n     * Holds raw values to draw for each axis.\n     */\n    private float[][] rawValuesTab = new float[4][0];\n\n    /**\n     * Holds auto-generated values that should be drawn, i.e if axis is inside not all auto-generated values should be\n     * drawn to avoid overdrawing. Used only for auto axes.\n     */\n    private float[][] autoValuesToDrawTab = new float[4][0];\n\n    /**\n     * Holds custom values that should be drawn, used only for custom axes.\n     */\n    private AxisValue[][] valuesToDrawTab = new AxisValue[4][0];\n\n    /**\n     * Buffers for axes lines coordinates(to draw grid in the background).\n     */\n    private float[][] linesDrawBufferTab = new float[4][0];\n\n    /**\n     * Buffers for auto-generated values for each axis, used only if there are auto axes.\n     */\n    private AxisAutoValues[] autoValuesBufferTab = new AxisAutoValues[]{new AxisAutoValues(),\n            new AxisAutoValues(), new AxisAutoValues(), new AxisAutoValues()};\n\n    public AxesRenderer(Context context, Chart chart) {\n        this.chart = chart;\n        computator = chart.getChartComputator();\n        density = context.getResources().getDisplayMetrics().density;\n        scaledDensity = context.getResources().getDisplayMetrics().scaledDensity;\n        axisMargin = ChartUtils.dp2px(density, DEFAULT_AXIS_MARGIN_DP);\n        for (int position = 0; position < 4; ++position) {\n            labelPaintTab[position].setStyle(Paint.Style.FILL);\n            labelPaintTab[position].setAntiAlias(true);\n            namePaintTab[position].setStyle(Paint.Style.FILL);\n            namePaintTab[position].setAntiAlias(true);\n            linePaintTab[position].setStyle(Paint.Style.STROKE);\n            linePaintTab[position].setAntiAlias(true);\n        }\n    }\n\n    public void onChartSizeChanged() {\n        onChartDataOrSizeChanged();\n    }\n\n    public void onChartDataChanged() {\n        onChartDataOrSizeChanged();\n    }\n\n    private void onChartDataOrSizeChanged() {\n        initAxis(chart.getChartData().getAxisXTop(), TOP);\n        initAxis(chart.getChartData().getAxisXBottom(), BOTTOM);\n        initAxis(chart.getChartData().getAxisYLeft(), LEFT);\n        initAxis(chart.getChartData().getAxisYRight(), RIGHT);\n    }\n\n    public void resetRenderer() {\n        this.computator = chart.getChartComputator();\n    }\n\n    /**\n     * Initialize attributes and measurement for axes(left, right, top, bottom);\n     */\n    private void initAxis(Axis axis, int position) {\n        if (null == axis) {\n            return;\n        }\n        initAxisAttributes(axis, position);\n        initAxisMargin(axis, position);\n        initAxisMeasurements(axis, position);\n    }\n\n    private void initAxisAttributes(Axis axis, int position) {\n        initAxisPaints(axis, position);\n        initAxisTextAlignment(axis, position);\n        if (axis.hasTiltedLabels()) {\n            initAxisDimensionForTiltedLabels(position);\n            intiTiltedLabelsTranslation(axis, position);\n        } else {\n            initAxisDimension(position);\n        }\n    }\n\n    private void initAxisPaints(Axis axis, int position) {\n        Typeface typeface = axis.getTypeface();\n        if (null != typeface) {\n            labelPaintTab[position].setTypeface(typeface);\n            namePaintTab[position].setTypeface(typeface);\n        }\n        labelPaintTab[position].setColor(axis.getTextColor());\n        labelPaintTab[position].setTextSize(ChartUtils.sp2px(scaledDensity, axis.getTextSize()));\n        labelPaintTab[position].getFontMetricsInt(fontMetricsTab[position]);\n        namePaintTab[position].setColor(axis.getTextColor());\n        namePaintTab[position].setTextSize(ChartUtils.sp2px(scaledDensity, axis.getTextSize()));\n        linePaintTab[position].setColor(axis.getLineColor());\n\n        labelTextAscentTab[position] = Math.abs(fontMetricsTab[position].ascent);\n        labelTextDescentTab[position] = Math.abs(fontMetricsTab[position].descent);\n        labelWidthTab[position] = (int) labelPaintTab[position].measureText(labelWidthChars, 0,\n                axis.getMaxLabelChars());\n    }\n\n    private void initAxisTextAlignment(Axis axis, int position) {\n        namePaintTab[position].setTextAlign(Align.CENTER);\n        if (TOP == position || BOTTOM == position) {\n            labelPaintTab[position].setTextAlign(Align.CENTER);\n        } else if (LEFT == position) {\n            if (axis.isInside()) {\n                labelPaintTab[position].setTextAlign(Align.LEFT);\n            } else {\n                labelPaintTab[position].setTextAlign(Align.RIGHT);\n            }\n        } else if (RIGHT == position) {\n            if (axis.isInside()) {\n                labelPaintTab[position].setTextAlign(Align.RIGHT);\n            } else {\n                labelPaintTab[position].setTextAlign(Align.LEFT);\n            }\n        }\n    }\n\n    private void initAxisDimensionForTiltedLabels(int position) {\n        int pythagoreanFromLabelWidth = (int) Math.sqrt(Math.pow(labelWidthTab[position], 2) / 2);\n        int pythagoreanFromAscent = (int) Math.sqrt(Math.pow(labelTextAscentTab[position], 2) / 2);\n        labelDimensionForMarginsTab[position] = pythagoreanFromAscent + pythagoreanFromLabelWidth;\n        labelDimensionForStepsTab[position] = Math.round(labelDimensionForMarginsTab[position] * 0.75f);\n    }\n\n    private void initAxisDimension(int position) {\n        if (LEFT == position || RIGHT == position) {\n            labelDimensionForMarginsTab[position] = labelWidthTab[position];\n            labelDimensionForStepsTab[position] = labelTextAscentTab[position];\n        } else if (TOP == position || BOTTOM == position) {\n            labelDimensionForMarginsTab[position] = labelTextAscentTab[position] +\n                    labelTextDescentTab[position];\n            labelDimensionForStepsTab[position] = labelWidthTab[position];\n        }\n    }\n\n    private void intiTiltedLabelsTranslation(Axis axis, int position) {\n        int pythagoreanFromLabelWidth = (int) Math.sqrt(Math.pow(labelWidthTab[position], 2) / 2);\n        int pythagoreanFromAscent = (int) Math.sqrt(Math.pow(labelTextAscentTab[position], 2) / 2);\n        int dx = 0;\n        int dy = 0;\n        if (axis.isInside()) {\n            if (LEFT == position) {\n                dx = pythagoreanFromAscent;\n            } else if (RIGHT == position) {\n                dy = -pythagoreanFromLabelWidth / 2;\n            } else if (TOP == position) {\n                dy = (pythagoreanFromAscent + pythagoreanFromLabelWidth / 2) - labelTextAscentTab[position];\n            } else if (BOTTOM == position) {\n                dy = -pythagoreanFromLabelWidth / 2;\n            }\n        } else {\n            if (LEFT == position) {\n                dy = -pythagoreanFromLabelWidth / 2;\n            } else if (RIGHT == position) {\n                dx = pythagoreanFromAscent;\n            } else if (TOP == position) {\n                dy = -pythagoreanFromLabelWidth / 2;\n            } else if (BOTTOM == position) {\n                dy = (pythagoreanFromAscent + pythagoreanFromLabelWidth / 2) - labelTextAscentTab[position];\n            }\n        }\n        tiltedLabelXTranslation[position] = dx;\n        tiltedLabelYTranslation[position] = dy;\n    }\n\n    private void initAxisMargin(Axis axis, int position) {\n        int margin = 0;\n        if (!axis.isInside() && (axis.isAutoGenerated() || !axis.getValues().isEmpty())) {\n            margin += axisMargin + labelDimensionForMarginsTab[position];\n        }\n        margin += getAxisNameMargin(axis, position);\n        insetContentRectWithAxesMargins(margin, position);\n    }\n\n    private int getAxisNameMargin(Axis axis, int position) {\n        int margin = 0;\n        if (!TextUtils.isEmpty(axis.getName())) {\n            margin += labelTextAscentTab[position];\n            margin += labelTextDescentTab[position];\n            margin += axisMargin;\n        }\n        return margin;\n    }\n\n    private void insetContentRectWithAxesMargins(int axisMargin, int position) {\n        if (LEFT == position) {\n            chart.getChartComputator().insetContentRect(axisMargin, 0, 0, 0);\n        } else if (RIGHT == position) {\n            chart.getChartComputator().insetContentRect(0, 0, axisMargin, 0);\n        } else if (TOP == position) {\n            chart.getChartComputator().insetContentRect(0, axisMargin, 0, 0);\n        } else if (BOTTOM == position) {\n            chart.getChartComputator().insetContentRect(0, 0, 0, axisMargin);\n        }\n    }\n\n    private void initAxisMeasurements(Axis axis, int position) {\n        if (LEFT == position) {\n            if (axis.isInside()) {\n                labelBaselineTab[position] = computator.getContentRectMinusAllMargins().left + axisMargin;\n                nameBaselineTab[position] = computator.getContentRectMinusAxesMargins().left - axisMargin\n                        - labelTextDescentTab[position];\n            } else {\n                labelBaselineTab[position] = computator.getContentRectMinusAxesMargins().left - axisMargin;\n                nameBaselineTab[position] = labelBaselineTab[position] - axisMargin\n                        - labelTextDescentTab[position] - labelDimensionForMarginsTab[position];\n            }\n            separationLineTab[position] = computator.getContentRectMinusAllMargins().left;\n        } else if (RIGHT == position) {\n            if (axis.isInside()) {\n                labelBaselineTab[position] = computator.getContentRectMinusAllMargins().right - axisMargin;\n                nameBaselineTab[position] = computator.getContentRectMinusAxesMargins().right + axisMargin\n                        + labelTextAscentTab[position];\n            } else {\n                labelBaselineTab[position] = computator.getContentRectMinusAxesMargins().right + axisMargin;\n                nameBaselineTab[position] = labelBaselineTab[position] + axisMargin\n                        + labelTextAscentTab[position] + labelDimensionForMarginsTab[position];\n            }\n            separationLineTab[position] = computator.getContentRectMinusAllMargins().right;\n        } else if (BOTTOM == position) {\n            if (axis.isInside()) {\n                labelBaselineTab[position] = computator.getContentRectMinusAllMargins().bottom - axisMargin\n                        - labelTextDescentTab[position];\n                nameBaselineTab[position] = computator.getContentRectMinusAxesMargins().bottom + axisMargin\n                        + labelTextAscentTab[position];\n            } else {\n                labelBaselineTab[position] = computator.getContentRectMinusAxesMargins().bottom + axisMargin\n                        + labelTextAscentTab[position];\n                nameBaselineTab[position] = labelBaselineTab[position] + axisMargin +\n                        labelDimensionForMarginsTab[position];\n            }\n            separationLineTab[position] = computator.getContentRectMinusAllMargins().bottom;\n        } else if (TOP == position) {\n            if (axis.isInside()) {\n                labelBaselineTab[position] = computator.getContentRectMinusAllMargins().top + axisMargin\n                        + labelTextAscentTab[position];\n                nameBaselineTab[position] = computator.getContentRectMinusAxesMargins().top - axisMargin\n                        - labelTextDescentTab[position];\n            } else {\n                labelBaselineTab[position] = computator.getContentRectMinusAxesMargins().top - axisMargin\n                        - labelTextDescentTab[position];\n                nameBaselineTab[position] = labelBaselineTab[position] - axisMargin -\n                        labelDimensionForMarginsTab[position];\n            }\n            separationLineTab[position] = computator.getContentRectMinusAllMargins().top;\n        } else {\n            throw new IllegalArgumentException(\"Invalid axis position: \" + position);\n        }\n    }\n\n    /**\n     * Prepare axes coordinates and draw axes lines(if enabled) in the background.\n     *\n     * @param canvas\n     */\n    public void drawInBackground(Canvas canvas) {\n        Axis axis = chart.getChartData().getAxisYLeft();\n        if (null != axis) {\n            prepareAxisToDraw(axis, LEFT);\n            drawAxisLines(canvas, axis, LEFT);\n        }\n\n        axis = chart.getChartData().getAxisYRight();\n        if (null != axis) {\n            prepareAxisToDraw(axis, RIGHT);\n            drawAxisLines(canvas, axis, RIGHT);\n        }\n\n        axis = chart.getChartData().getAxisXBottom();\n        if (null != axis) {\n            prepareAxisToDraw(axis, BOTTOM);\n            drawAxisLines(canvas, axis, BOTTOM);\n        }\n\n        axis = chart.getChartData().getAxisXTop();\n        if (null != axis) {\n            prepareAxisToDraw(axis, TOP);\n            drawAxisLines(canvas, axis, TOP);\n        }\n    }\n\n    private void prepareAxisToDraw(Axis axis, int position) {\n        if (axis.isAutoGenerated()) {\n            prepareAutoGeneratedAxis(axis, position);\n        } else {\n            prepareCustomAxis(axis, position);\n        }\n    }\n\n    /**\n     * Draw axes labels and names in the foreground.\n     *\n     * @param canvas\n     */\n    public void drawInForeground(Canvas canvas) {\n        Axis axis = chart.getChartData().getAxisYLeft();\n        if (null != axis) {\n            drawAxisLabelsAndName(canvas, axis, LEFT);\n        }\n\n        axis = chart.getChartData().getAxisYRight();\n        if (null != axis) {\n            drawAxisLabelsAndName(canvas, axis, RIGHT);\n        }\n\n        axis = chart.getChartData().getAxisXBottom();\n        if (null != axis) {\n            drawAxisLabelsAndName(canvas, axis, BOTTOM);\n        }\n\n        axis = chart.getChartData().getAxisXTop();\n        if (null != axis) {\n            drawAxisLabelsAndName(canvas, axis, TOP);\n        }\n    }\n\n    private void prepareCustomAxis(Axis axis, int position) {\n        final Viewport maxViewport = computator.getMaximumViewport();\n        final Viewport visibleViewport = computator.getVisibleViewport();\n        final Rect contentRect = computator.getContentRectMinusAllMargins();\n        boolean isAxisVertical = isAxisVertical(position);\n        float viewportMin, viewportMax;\n        float scale = 1;\n        if (isAxisVertical) {\n            if (maxViewport.height() > 0 && visibleViewport.height() > 0) {\n                scale = contentRect.height() * (maxViewport.height() / visibleViewport.height());\n            }\n            viewportMin = visibleViewport.bottom;\n            viewportMax = visibleViewport.top;\n        } else {\n            if (maxViewport.width() > 0 && visibleViewport.width() > 0) {\n                scale = contentRect.width() * (maxViewport.width() / visibleViewport.width());\n            }\n            viewportMin = visibleViewport.left;\n            viewportMax = visibleViewport.right;\n        }\n        if (scale == 0) {\n            scale = 1;\n        }\n        int module = (int) Math.max(1,\n                Math.ceil((axis.getValues().size() * labelDimensionForStepsTab[position] * 1.5) / scale));\n        //Reinitialize tab to hold lines coordinates.\n        if (axis.hasLines() && (linesDrawBufferTab[position].length < axis.getValues().size() * 4)) {\n            linesDrawBufferTab[position] = new float[axis.getValues().size() * 4];\n        }\n        //Reinitialize tabs to hold all raw values to draw.\n        if (rawValuesTab[position].length < axis.getValues().size()) {\n            rawValuesTab[position] = new float[axis.getValues().size()];\n        }\n        //Reinitialize tabs to hold all raw values to draw.\n        if (valuesToDrawTab[position].length < axis.getValues().size()) {\n            valuesToDrawTab[position] = new AxisValue[axis.getValues().size()];\n        }\n\n        float rawValue;\n        int valueIndex = 0;\n        int valueToDrawIndex = 0;\n        for (AxisValue axisValue : axis.getValues()) {\n            // Draw axis values that are within visible viewport.\n            final float value = axisValue.getValue();\n            if (value >= viewportMin && value <= viewportMax) {\n                // Draw axis values that have 0 module value, this will hide some labels if there is no place for them.\n                if (0 == valueIndex % module) {\n                    if (isAxisVertical) {\n                        rawValue = computator.computeRawY(value);\n                    } else {\n                        rawValue = computator.computeRawX(value);\n                    }\n                    if (checkRawValue(contentRect, rawValue, axis.isInside(), position, isAxisVertical)) {\n                        rawValuesTab[position][valueToDrawIndex] = rawValue;\n                        valuesToDrawTab[position][valueToDrawIndex] = axisValue;\n                        ++valueToDrawIndex;\n                    }\n                }\n                // If within viewport - increment valueIndex;\n                ++valueIndex;\n            }\n        }\n        valuesToDrawNumTab[position] = valueToDrawIndex;\n    }\n\n    private void prepareAutoGeneratedAxis(Axis axis, int position) {\n        final Viewport visibleViewport = computator.getVisibleViewport();\n        final Rect contentRect = computator.getContentRectMinusAllMargins();\n        boolean isAxisVertical = isAxisVertical(position);\n        float start, stop;\n        int contentRectDimension;\n        if (isAxisVertical) {\n            start = visibleViewport.bottom;\n            stop = visibleViewport.top;\n            contentRectDimension = contentRect.height();\n        } else {\n            start = visibleViewport.left;\n            stop = visibleViewport.right;\n            contentRectDimension = contentRect.width();\n        }\n        FloatUtils.computeAutoGeneratedAxisValues(start, stop, Math.abs(contentRectDimension) /\n                labelDimensionForStepsTab[position] / 2, autoValuesBufferTab[position]);\n        //Reinitialize tab to hold lines coordinates.\n        if (axis.hasLines()\n                && (linesDrawBufferTab[position].length < autoValuesBufferTab[position].valuesNumber * 4)) {\n            linesDrawBufferTab[position] = new float[autoValuesBufferTab[position].valuesNumber * 4];\n        }\n        //Reinitialize tabs to hold all raw and auto values.\n        if (rawValuesTab[position].length < autoValuesBufferTab[position].valuesNumber) {\n            rawValuesTab[position] = new float[autoValuesBufferTab[position].valuesNumber];\n        }\n        if (autoValuesToDrawTab[position].length < autoValuesBufferTab[position].valuesNumber) {\n            autoValuesToDrawTab[position] = new float[autoValuesBufferTab[position].valuesNumber];\n        }\n\n        float rawValue;\n        int valueToDrawIndex = 0;\n        for (int i = 0; i < autoValuesBufferTab[position].valuesNumber; ++i) {\n            if (isAxisVertical) {\n                rawValue = computator.computeRawY(autoValuesBufferTab[position].values[i]);\n            } else {\n                rawValue = computator.computeRawX(autoValuesBufferTab[position].values[i]);\n            }\n            if (checkRawValue(contentRect, rawValue, axis.isInside(), position, isAxisVertical)) {\n                rawValuesTab[position][valueToDrawIndex] = rawValue;\n                autoValuesToDrawTab[position][valueToDrawIndex] = autoValuesBufferTab[position].values[i];\n                ++valueToDrawIndex;\n            }\n        }\n        valuesToDrawNumTab[position] = valueToDrawIndex;\n    }\n\n    private boolean checkRawValue(Rect rect, float rawValue, boolean axisInside, int position, boolean isVertical) {\n        if (axisInside) {\n            if (isVertical) {\n                float marginBottom = labelTextAscentTab[BOTTOM] + axisMargin;\n                float marginTop = labelTextAscentTab[TOP] + axisMargin;\n                if (rawValue <= rect.bottom - marginBottom && rawValue >= rect.top + marginTop) {\n                    return true;\n                } else {\n                    return false;\n                }\n            } else {\n                float margin = labelWidthTab[position] / 2;\n                if (rawValue >= rect.left + margin && rawValue <= rect.right - margin) {\n                    return true;\n                } else {\n                    return false;\n                }\n            }\n        }\n        return true;\n    }\n\n    private void drawAxisLines(Canvas canvas, Axis axis, int position) {\n        final Rect contentRectMargins = computator.getContentRectMinusAxesMargins();\n        float separationX1, separationY1, separationX2, separationY2;\n        separationX1 = separationY1 = separationX2 = separationY2 = 0;\n        float lineX1, lineY1, lineX2, lineY2;\n        lineX1 = lineY1 = lineX2 = lineY2 = 0;\n        boolean isAxisVertical = isAxisVertical(position);\n        if (LEFT == position || RIGHT == position) {\n            separationX1 = separationX2 = separationLineTab[position];\n            separationY1 = contentRectMargins.bottom;\n            separationY2 = contentRectMargins.top;\n            lineX1 = contentRectMargins.left;\n            lineX2 = contentRectMargins.right;\n        } else if (TOP == position || BOTTOM == position) {\n            separationX1 = contentRectMargins.left;\n            separationX2 = contentRectMargins.right;\n            separationY1 = separationY2 = separationLineTab[position];\n            lineY1 = contentRectMargins.top;\n            lineY2 = contentRectMargins.bottom;\n        }\n        // Draw separation line with the same color as axis labels and name.\n        if (axis.hasSeparationLine()) {\n            canvas.drawLine(separationX1, separationY1, separationX2, separationY2, labelPaintTab[position]);\n        }\n\n        if (axis.hasLines()) {\n            int valueToDrawIndex = 0;\n            for (; valueToDrawIndex < valuesToDrawNumTab[position]; ++valueToDrawIndex) {\n                if (isAxisVertical) {\n                    lineY1 = lineY2 = rawValuesTab[position][valueToDrawIndex];\n                } else {\n                    lineX1 = lineX2 = rawValuesTab[position][valueToDrawIndex];\n                }\n                linesDrawBufferTab[position][valueToDrawIndex * 4 + 0] = lineX1;\n                linesDrawBufferTab[position][valueToDrawIndex * 4 + 1] = lineY1;\n                linesDrawBufferTab[position][valueToDrawIndex * 4 + 2] = lineX2;\n                linesDrawBufferTab[position][valueToDrawIndex * 4 + 3] = lineY2;\n            }\n            canvas.drawLines(linesDrawBufferTab[position], 0, valueToDrawIndex * 4, linePaintTab[position]);\n        }\n    }\n\n    private void drawAxisLabelsAndName(Canvas canvas, Axis axis, int position) {\n        float labelX, labelY;\n        labelX = labelY = 0;\n        boolean isAxisVertical = isAxisVertical(position);\n        if (LEFT == position || RIGHT == position) {\n            labelX = labelBaselineTab[position];\n        } else if (TOP == position || BOTTOM == position) {\n            labelY = labelBaselineTab[position];\n        }\n\n        for (int valueToDrawIndex = 0; valueToDrawIndex < valuesToDrawNumTab[position]; ++valueToDrawIndex) {\n            int charsNumber = 0;\n            if (axis.isAutoGenerated()) {\n                final float value = autoValuesToDrawTab[position][valueToDrawIndex];\n                charsNumber = axis.getFormatter().formatValueForAutoGeneratedAxis(labelBuffer, value,\n                        autoValuesBufferTab[position].decimals);\n            } else {\n                AxisValue axisValue = valuesToDrawTab[position][valueToDrawIndex];\n                charsNumber = axis.getFormatter().formatValueForManualAxis(labelBuffer, axisValue);\n            }\n\n            if (isAxisVertical) {\n                labelY = rawValuesTab[position][valueToDrawIndex];\n            } else {\n                labelX = rawValuesTab[position][valueToDrawIndex];\n            }\n\n            if (axis.hasTiltedLabels()) {\n                canvas.save();\n                canvas.translate(tiltedLabelXTranslation[position], tiltedLabelYTranslation[position]);\n                canvas.rotate(-45, labelX, labelY);\n                canvas.drawText(labelBuffer, labelBuffer.length - charsNumber, charsNumber, labelX, labelY,\n                        labelPaintTab[position]);\n                canvas.restore();\n            } else {\n                canvas.drawText(labelBuffer, labelBuffer.length - charsNumber, charsNumber, labelX, labelY,\n                        labelPaintTab[position]);\n            }\n        }\n\n        // Drawing axis name\n        final Rect contentRectMargins = computator.getContentRectMinusAxesMargins();\n        if (!TextUtils.isEmpty(axis.getName())) {\n            if (isAxisVertical) {\n                canvas.save();\n                canvas.rotate(-90, contentRectMargins.centerY(), contentRectMargins.centerY());\n                canvas.drawText(axis.getName(), contentRectMargins.centerY(), nameBaselineTab[position],\n                        namePaintTab[position]);\n                canvas.restore();\n            } else {\n                canvas.drawText(axis.getName(), contentRectMargins.centerX(), nameBaselineTab[position],\n                        namePaintTab[position]);\n            }\n        }\n    }\n\n    private boolean isAxisVertical(int position) {\n        if (LEFT == position || RIGHT == position) {\n            return true;\n        } else if (TOP == position || BOTTOM == position) {\n            return false;\n        } else {\n            throw new IllegalArgumentException(\"Invalid axis position \" + position);\n        }\n    }\n\n}"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/renderer/BubbleChartRenderer.java",
    "content": "package lecho.lib.hellocharts.renderer;\n\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.graphics.Paint;\nimport android.graphics.PointF;\nimport android.graphics.Rect;\nimport android.graphics.RectF;\n\nimport lecho.lib.hellocharts.computator.ChartComputator;\nimport lecho.lib.hellocharts.formatter.BubbleChartValueFormatter;\nimport lecho.lib.hellocharts.model.BubbleChartData;\nimport lecho.lib.hellocharts.model.BubbleValue;\nimport lecho.lib.hellocharts.model.SelectedValue.SelectedValueType;\nimport lecho.lib.hellocharts.model.ValueShape;\nimport lecho.lib.hellocharts.model.Viewport;\nimport lecho.lib.hellocharts.provider.BubbleChartDataProvider;\nimport lecho.lib.hellocharts.util.ChartUtils;\nimport lecho.lib.hellocharts.view.Chart;\n\npublic class BubbleChartRenderer extends AbstractChartRenderer {\n    private static final int DEFAULT_TOUCH_ADDITIONAL_DP = 4;\n    private static final int MODE_DRAW = 0;\n    private static final int MODE_HIGHLIGHT = 1;\n\n    private BubbleChartDataProvider dataProvider;\n\n    /**\n     * Additional value added to bubble radius when drawing highlighted bubble, used to give tauch feedback.\n     */\n    private int touchAdditional;\n\n    /**\n     * Scales for bubble radius value, only one is used depending on screen orientation;\n     */\n    private float bubbleScaleX;\n    private float bubbleScaleY;\n\n    /**\n     * True if bubbleScale = bubbleScaleX so the renderer should used {@link ChartComputator#computeRawDistanceX(float)}\n     * , if false bubbleScale = bubbleScaleY and renderer should use\n     * {@link ChartComputator#computeRawDistanceY(float)}.\n     */\n    private boolean isBubbleScaledByX = true;\n\n    /**\n     * Maximum bubble radius.\n     */\n    private float maxRadius;\n\n    /**\n     * Minimal bubble radius in pixels.\n     */\n    private float minRawRadius;\n    private PointF bubbleCenter = new PointF();\n    private Paint bubblePaint = new Paint();\n\n    /**\n     * Rect used for drawing bubbles with SHAPE_SQUARE.\n     */\n    private RectF bubbleRect = new RectF();\n\n    private boolean hasLabels;\n    private boolean hasLabelsOnlyForSelected;\n    private BubbleChartValueFormatter valueFormatter;\n    private Viewport tempMaximumViewport = new Viewport();\n\n    public BubbleChartRenderer(Context context, Chart chart, BubbleChartDataProvider dataProvider) {\n        super(context, chart);\n        this.dataProvider = dataProvider;\n\n        touchAdditional = ChartUtils.dp2px(density, DEFAULT_TOUCH_ADDITIONAL_DP);\n\n        bubblePaint.setAntiAlias(true);\n        bubblePaint.setStyle(Paint.Style.FILL);\n\n    }\n\n    @Override\n    public void onChartSizeChanged() {\n        final ChartComputator computator = chart.getChartComputator();\n        Rect contentRect = computator.getContentRectMinusAllMargins();\n        if (contentRect.width() < contentRect.height()) {\n            isBubbleScaledByX = true;\n        } else {\n            isBubbleScaledByX = false;\n        }\n    }\n\n    @Override\n    public void onChartDataChanged() {\n        super.onChartDataChanged();\n        BubbleChartData data = dataProvider.getBubbleChartData();\n        this.hasLabels = data.hasLabels();\n        this.hasLabelsOnlyForSelected = data.hasLabelsOnlyForSelected();\n        this.valueFormatter = data.getFormatter();\n\n        onChartViewportChanged();\n    }\n\n    @Override\n    public void onChartViewportChanged() {\n        if (isViewportCalculationEnabled) {\n            calculateMaxViewport();\n            computator.setMaxViewport(tempMaximumViewport);\n            computator.setCurrentViewport(computator.getMaximumViewport());\n        }\n    }\n\n    @Override\n    public void draw(Canvas canvas) {\n        drawBubbles(canvas);\n        if (isTouched()) {\n            highlightBubbles(canvas);\n        }\n    }\n\n    @Override\n    public void drawUnclipped(Canvas canvas) {\n    }\n\n    @Override\n    public boolean checkTouch(float touchX, float touchY) {\n        selectedValue.clear();\n        final BubbleChartData data = dataProvider.getBubbleChartData();\n        int valueIndex = 0;\n        for (BubbleValue bubbleValue : data.getValues()) {\n            float rawRadius = processBubble(bubbleValue, bubbleCenter);\n\n            if (ValueShape.SQUARE.equals(bubbleValue.getShape())) {\n                if (bubbleRect.contains(touchX, touchY)) {\n                    selectedValue.set(valueIndex, valueIndex, SelectedValueType.NONE);\n                }\n            } else if (ValueShape.CIRCLE.equals(bubbleValue.getShape())) {\n                final float diffX = touchX - bubbleCenter.x;\n                final float diffY = touchY - bubbleCenter.y;\n                final float touchDistance = (float) Math.sqrt((diffX * diffX) + (diffY * diffY));\n\n                if (touchDistance <= rawRadius) {\n                    selectedValue.set(valueIndex, valueIndex, SelectedValueType.NONE);\n                }\n            } else {\n                throw new IllegalArgumentException(\"Invalid bubble shape: \" + bubbleValue.getShape());\n            }\n\n            ++valueIndex;\n        }\n\n        return isTouched();\n    }\n\n    /**\n     * Removes empty spaces on sides of chart(left-right for landscape, top-bottom for portrait). *This method should be\n     * called after layout had been drawn*. Because most often chart is drawn as rectangle with proportions other than\n     * 1:1 and bubbles have to be drawn as circles not ellipses I am unable to calculate correct margins based on chart\n     * data only. I need to know chart dimension to remove extra empty spaces, that bad because viewport depends a\n     * little on contentRectMinusAllMargins.\n     */\n    public void removeMargins() {\n        final Rect contentRect = computator.getContentRectMinusAllMargins();\n        if (contentRect.height() == 0 || contentRect.width() == 0) {\n            // View probably not yet measured, skip removing margins.\n            return;\n        }\n        final float pxX = computator.computeRawDistanceX(maxRadius * bubbleScaleX);\n        final float pxY = computator.computeRawDistanceY(maxRadius * bubbleScaleY);\n        final float scaleX = computator.getMaximumViewport().width() / contentRect.width();\n        final float scaleY = computator.getMaximumViewport().height() / contentRect.height();\n        float dx = 0;\n        float dy = 0;\n        if (isBubbleScaledByX) {\n            dy = (pxY - pxX) * scaleY * 0.75f;\n        } else {\n            dx = (pxX - pxY) * scaleX * 0.75f;\n        }\n\n        Viewport maxViewport = computator.getMaximumViewport();\n        maxViewport.inset(dx, dy);\n        Viewport currentViewport = computator.getCurrentViewport();\n        currentViewport.inset(dx, dy);\n        computator.setMaxViewport(maxViewport);\n        computator.setCurrentViewport(currentViewport);\n    }\n\n    private void drawBubbles(Canvas canvas) {\n        final BubbleChartData data = dataProvider.getBubbleChartData();\n        for (BubbleValue bubbleValue : data.getValues()) {\n            drawBubble(canvas, bubbleValue);\n        }\n    }\n\n    private void drawBubble(Canvas canvas, BubbleValue bubbleValue) {\n        float rawRadius = processBubble(bubbleValue, bubbleCenter);\n        // Not touched bubbles are a little smaller than touched to give user touch feedback.\n        rawRadius -= touchAdditional;\n        bubbleRect.inset(touchAdditional, touchAdditional);\n        bubblePaint.setColor(bubbleValue.getColor());\n        drawBubbleShapeAndLabel(canvas, bubbleValue, rawRadius, MODE_DRAW);\n\n    }\n\n    private void drawBubbleShapeAndLabel(Canvas canvas, BubbleValue bubbleValue, float rawRadius, int mode) {\n        if (ValueShape.SQUARE.equals(bubbleValue.getShape())) {\n            canvas.drawRect(bubbleRect, bubblePaint);\n        } else if (ValueShape.CIRCLE.equals(bubbleValue.getShape())) {\n            canvas.drawCircle(bubbleCenter.x, bubbleCenter.y, rawRadius, bubblePaint);\n        } else {\n            throw new IllegalArgumentException(\"Invalid bubble shape: \" + bubbleValue.getShape());\n        }\n\n        if (MODE_HIGHLIGHT == mode) {\n            if (hasLabels || hasLabelsOnlyForSelected) {\n                drawLabel(canvas, bubbleValue, bubbleCenter.x, bubbleCenter.y);\n            }\n        } else if (MODE_DRAW == mode) {\n            if (hasLabels) {\n                drawLabel(canvas, bubbleValue, bubbleCenter.x, bubbleCenter.y);\n            }\n        } else {\n            throw new IllegalStateException(\"Cannot process bubble in mode: \" + mode);\n        }\n    }\n\n    private void highlightBubbles(Canvas canvas) {\n        final BubbleChartData data = dataProvider.getBubbleChartData();\n        BubbleValue bubbleValue = data.getValues().get(selectedValue.getFirstIndex());\n        highlightBubble(canvas, bubbleValue);\n    }\n\n    private void highlightBubble(Canvas canvas, BubbleValue bubbleValue) {\n        float rawRadius = processBubble(bubbleValue, bubbleCenter);\n        bubblePaint.setColor(bubbleValue.getDarkenColor());\n        drawBubbleShapeAndLabel(canvas, bubbleValue, rawRadius, MODE_HIGHLIGHT);\n    }\n\n    /**\n     * Calculate bubble radius and center x and y coordinates. Center x and x will be stored in point parameter, radius\n     * will be returned as float value.\n     */\n    private float processBubble(BubbleValue bubbleValue, PointF point) {\n        final float rawX = computator.computeRawX(bubbleValue.getX());\n        final float rawY = computator.computeRawY(bubbleValue.getY());\n        float radius = (float) Math.sqrt(Math.abs(bubbleValue.getZ()) / Math.PI);\n        float rawRadius;\n        if (isBubbleScaledByX) {\n            radius *= bubbleScaleX;\n            rawRadius = computator.computeRawDistanceX(radius);\n        } else {\n            radius *= bubbleScaleY;\n            rawRadius = computator.computeRawDistanceY(radius);\n        }\n\n        if (rawRadius < minRawRadius + touchAdditional) {\n            rawRadius = minRawRadius + touchAdditional;\n        }\n\n        bubbleCenter.set(rawX, rawY);\n        if (ValueShape.SQUARE.equals(bubbleValue.getShape())) {\n            bubbleRect.set(rawX - rawRadius, rawY - rawRadius, rawX + rawRadius, rawY + rawRadius);\n        }\n        return rawRadius;\n    }\n\n    private void drawLabel(Canvas canvas, BubbleValue bubbleValue, float rawX, float rawY) {\n        final Rect contentRect = computator.getContentRectMinusAllMargins();\n        final int numChars = valueFormatter.formatChartValue(labelBuffer, bubbleValue);\n\n        if (numChars == 0) {\n            // No need to draw empty label\n            return;\n        }\n\n        final float labelWidth = labelPaint.measureText(labelBuffer, labelBuffer.length - numChars, numChars);\n        final int labelHeight = Math.abs(fontMetrics.ascent);\n        float left = rawX - labelWidth / 2 - labelMargin;\n        float right = rawX + labelWidth / 2 + labelMargin;\n        float top = rawY - labelHeight / 2 - labelMargin;\n        float bottom = rawY + labelHeight / 2 + labelMargin;\n\n        if (top < contentRect.top) {\n            top = rawY;\n            bottom = rawY + labelHeight + labelMargin * 2;\n        }\n        if (bottom > contentRect.bottom) {\n            top = rawY - labelHeight - labelMargin * 2;\n            bottom = rawY;\n        }\n        if (left < contentRect.left) {\n            left = rawX;\n            right = rawX + labelWidth + labelMargin * 2;\n        }\n        if (right > contentRect.right) {\n            left = rawX - labelWidth - labelMargin * 2;\n            right = rawX;\n        }\n\n        labelBackgroundRect.set(left, top, right, bottom);\n        drawLabelTextAndBackground(canvas, labelBuffer, labelBuffer.length - numChars, numChars,\n                bubbleValue.getDarkenColor());\n\n    }\n\n    private void calculateMaxViewport() {\n        float maxZ = Float.MIN_VALUE;\n        tempMaximumViewport.set(Float.MAX_VALUE, Float.MIN_VALUE, Float.MIN_VALUE, Float.MAX_VALUE);\n        BubbleChartData data = dataProvider.getBubbleChartData();\n        // TODO: Optimize.\n        for (BubbleValue bubbleValue : data.getValues()) {\n            if (Math.abs(bubbleValue.getZ()) > maxZ) {\n                maxZ = Math.abs(bubbleValue.getZ());\n            }\n            if (bubbleValue.getX() < tempMaximumViewport.left) {\n                tempMaximumViewport.left = bubbleValue.getX();\n            }\n            if (bubbleValue.getX() > tempMaximumViewport.right) {\n                tempMaximumViewport.right = bubbleValue.getX();\n            }\n            if (bubbleValue.getY() < tempMaximumViewport.bottom) {\n                tempMaximumViewport.bottom = bubbleValue.getY();\n            }\n            if (bubbleValue.getY() > tempMaximumViewport.top) {\n                tempMaximumViewport.top = bubbleValue.getY();\n            }\n        }\n\n        maxRadius = (float) Math.sqrt(maxZ / Math.PI);\n\n        // Number 4 is determined by trials and errors method, no magic behind it:).\n        bubbleScaleX = tempMaximumViewport.width() / (maxRadius * 4);\n        if (bubbleScaleX == 0) {\n            // case for 0 viewport width.\n            bubbleScaleX = 1;\n        }\n\n        bubbleScaleY = tempMaximumViewport.height() / (maxRadius * 4);\n        if (bubbleScaleY == 0) {\n            // case for 0 viewport height.\n            bubbleScaleY = 1;\n        }\n\n        // For cases when user sets different than 1 bubble scale in BubbleChartData.\n        bubbleScaleX *= data.getBubbleScale();\n        bubbleScaleY *= data.getBubbleScale();\n\n        // Prevent cutting of bubbles on the edges of chart area.\n        tempMaximumViewport.inset(-maxRadius * bubbleScaleX, -maxRadius * bubbleScaleY);\n\n        minRawRadius = ChartUtils.dp2px(density, dataProvider.getBubbleChartData().getMinBubbleRadius());\n    }\n\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/renderer/ChartRenderer.java",
    "content": "package lecho.lib.hellocharts.renderer;\n\nimport android.graphics.Canvas;\n\nimport lecho.lib.hellocharts.model.SelectedValue;\nimport lecho.lib.hellocharts.model.Viewport;\n\n/**\n * Interface for all chart renderer.\n */\npublic interface ChartRenderer {\n\n    public void onChartSizeChanged();\n\n    public void onChartDataChanged();\n\n    public void onChartViewportChanged();\n\n    public void resetRenderer();\n\n    /**\n     * Draw chart data.\n     */\n    public void draw(Canvas canvas);\n\n    /**\n     * Draw chart data that should not be clipped to contentRect area.\n     */\n    public void drawUnclipped(Canvas canvas);\n\n    /**\n     * Checks if given pixel coordinates corresponds to any chart value. If yes return true and set selectedValue, if\n     * not selectedValue should be *cleared* and method should return false.\n     */\n    public boolean checkTouch(float touchX, float touchY);\n\n    /**\n     * Returns true if there is value selected.\n     */\n    public boolean isTouched();\n\n    /**\n     * Clear value selection.\n     */\n    public void clearTouch();\n\n    public Viewport getMaximumViewport();\n\n    public void setMaximumViewport(Viewport maxViewport);\n\n    public Viewport getCurrentViewport();\n\n    public void setCurrentViewport(Viewport viewport);\n\n    public boolean isViewportCalculationEnabled();\n\n    public void setViewportCalculationEnabled(boolean isEnabled);\n\n    public void selectValue(SelectedValue selectedValue);\n\n    public SelectedValue getSelectedValue();\n\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/renderer/ColumnChartRenderer.java",
    "content": "package lecho.lib.hellocharts.renderer;\n\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.graphics.Paint;\nimport android.graphics.Paint.Cap;\nimport android.graphics.PointF;\nimport android.graphics.RectF;\n\nimport lecho.lib.hellocharts.model.Column;\nimport lecho.lib.hellocharts.model.ColumnChartData;\nimport lecho.lib.hellocharts.model.SelectedValue.SelectedValueType;\nimport lecho.lib.hellocharts.model.SubcolumnValue;\nimport lecho.lib.hellocharts.model.Viewport;\nimport lecho.lib.hellocharts.provider.ColumnChartDataProvider;\nimport lecho.lib.hellocharts.util.ChartUtils;\nimport lecho.lib.hellocharts.view.Chart;\n\n/**\n * Magic renderer for ColumnChart.\n */\npublic class ColumnChartRenderer extends AbstractChartRenderer {\n    public static final int DEFAULT_SUBCOLUMN_SPACING_DP = 1;\n    public static final int DEFAULT_COLUMN_TOUCH_ADDITIONAL_WIDTH_DP = 4;\n\n    private static final int MODE_DRAW = 0;\n    private static final int MODE_CHECK_TOUCH = 1;\n    private static final int MODE_HIGHLIGHT = 2;\n\n    private ColumnChartDataProvider dataProvider;\n\n    /**\n     * Additional width for hightlighted column, used to give tauch feedback.\n     */\n    private int touchAdditionalWidth;\n\n    /**\n     * Spacing between sub-columns.\n     */\n    private int subcolumnSpacing;\n\n    /**\n     * Paint used to draw every column.\n     */\n    private Paint columnPaint = new Paint();\n\n    /**\n     * Holds coordinates for currently processed column/sub-column.\n     */\n    private RectF drawRect = new RectF();\n\n    /**\n     * Coordinated of user tauch.\n     */\n    private PointF touchedPoint = new PointF();\n\n    private float fillRatio;\n\n    private float baseValue;\n\n    private Viewport tempMaximumViewport = new Viewport();\n\n    public ColumnChartRenderer(Context context, Chart chart, ColumnChartDataProvider dataProvider) {\n        super(context, chart);\n        this.dataProvider = dataProvider;\n        subcolumnSpacing = ChartUtils.dp2px(density, DEFAULT_SUBCOLUMN_SPACING_DP);\n        touchAdditionalWidth = ChartUtils.dp2px(density, DEFAULT_COLUMN_TOUCH_ADDITIONAL_WIDTH_DP);\n\n        columnPaint.setAntiAlias(true);\n        columnPaint.setStyle(Paint.Style.FILL);\n        columnPaint.setStrokeCap(Cap.SQUARE);\n    }\n\n    @Override\n    public void onChartSizeChanged() {\n    }\n\n    @Override\n    public void onChartDataChanged() {\n        super.onChartDataChanged();\n        ColumnChartData data = dataProvider.getColumnChartData();\n        fillRatio = data.getFillRatio();\n        baseValue = data.getBaseValue();\n\n        onChartViewportChanged();\n    }\n\n    @Override\n    public void onChartViewportChanged() {\n        if (isViewportCalculationEnabled) {\n            calculateMaxViewport();\n            computator.setMaxViewport(tempMaximumViewport);\n            computator.setCurrentViewport(computator.getMaximumViewport());\n        }\n    }\n\n    public void draw(Canvas canvas) {\n        final ColumnChartData data = dataProvider.getColumnChartData();\n        if (data.isStacked()) {\n            drawColumnForStacked(canvas);\n            if (isTouched()) {\n                highlightColumnForStacked(canvas);\n            }\n        } else {\n            drawColumnsForSubcolumns(canvas);\n            if (isTouched()) {\n                highlightColumnsForSubcolumns(canvas);\n            }\n        }\n    }\n\n    @Override\n    public void drawUnclipped(Canvas canvas) {\n        // Do nothing, for this kind of chart there is nothing to draw beyond clipped area\n    }\n\n    public boolean checkTouch(float touchX, float touchY) {\n        selectedValue.clear();\n        final ColumnChartData data = dataProvider.getColumnChartData();\n        if (data.isStacked()) {\n            checkTouchForStacked(touchX, touchY);\n        } else {\n            checkTouchForSubcolumns(touchX, touchY);\n        }\n        return isTouched();\n    }\n\n    private void calculateMaxViewport() {\n        final ColumnChartData data = dataProvider.getColumnChartData();\n        // Column chart always has X values from 0 to numColumns-1, to add some margin on the left and right I added\n        // extra 0.5 to the each side, that margins will be negative scaled according to number of columns, so for more\n        // columns there will be less margin.\n        tempMaximumViewport.set(-0.5f, baseValue, data.getColumns().size() - 0.5f, baseValue);\n        if (data.isStacked()) {\n            calculateMaxViewportForStacked(data);\n        } else {\n            calculateMaxViewportForSubcolumns(data);\n        }\n    }\n\n    private void calculateMaxViewportForSubcolumns(ColumnChartData data) {\n        for (Column column : data.getColumns()) {\n            for (SubcolumnValue columnValue : column.getValues()) {\n                if (columnValue.getValue() >= baseValue && columnValue.getValue() > tempMaximumViewport.top) {\n                    tempMaximumViewport.top = columnValue.getValue();\n                }\n                if (columnValue.getValue() < baseValue && columnValue.getValue() < tempMaximumViewport.bottom) {\n                    tempMaximumViewport.bottom = columnValue.getValue();\n                }\n            }\n        }\n    }\n\n    private void calculateMaxViewportForStacked(ColumnChartData data) {\n        for (Column column : data.getColumns()) {\n            float sumPositive = baseValue;\n            float sumNegative = baseValue;\n            for (SubcolumnValue columnValue : column.getValues()) {\n                if (columnValue.getValue() >= baseValue) {\n                    sumPositive += columnValue.getValue();\n                } else {\n                    sumNegative += columnValue.getValue();\n                }\n            }\n            if (sumPositive > tempMaximumViewport.top) {\n                tempMaximumViewport.top = sumPositive;\n            }\n            if (sumNegative < tempMaximumViewport.bottom) {\n                tempMaximumViewport.bottom = sumNegative;\n            }\n        }\n    }\n\n    private void drawColumnsForSubcolumns(Canvas canvas) {\n        final ColumnChartData data = dataProvider.getColumnChartData();\n        final float columnWidth = calculateColumnWidth();\n        int columnIndex = 0;\n        for (Column column : data.getColumns()) {\n            processColumnForSubcolumns(canvas, column, columnWidth, columnIndex, MODE_DRAW);\n            ++columnIndex;\n        }\n    }\n\n    private void highlightColumnsForSubcolumns(Canvas canvas) {\n        final ColumnChartData data = dataProvider.getColumnChartData();\n        final float columnWidth = calculateColumnWidth();\n        Column column = data.getColumns().get(selectedValue.getFirstIndex());\n        processColumnForSubcolumns(canvas, column, columnWidth, selectedValue.getFirstIndex(), MODE_HIGHLIGHT);\n    }\n\n    private void checkTouchForSubcolumns(float touchX, float touchY) {\n        // Using member variable to hold touch point to avoid too much parameters in methods.\n        touchedPoint.x = touchX;\n        touchedPoint.y = touchY;\n        final ColumnChartData data = dataProvider.getColumnChartData();\n        final float columnWidth = calculateColumnWidth();\n        int columnIndex = 0;\n        for (Column column : data.getColumns()) {\n            // canvas is not needed for checking touch\n            processColumnForSubcolumns(null, column, columnWidth, columnIndex, MODE_CHECK_TOUCH);\n            ++columnIndex;\n        }\n    }\n\n    private void processColumnForSubcolumns(Canvas canvas, Column column, float columnWidth, int columnIndex,\n                                            int mode) {\n        // For n subcolumns there will be n-1 spacing and there will be one\n        // subcolumn for every columnValue\n        float subcolumnWidth = (columnWidth - (subcolumnSpacing * (column.getValues().size() - 1)))\n                / column.getValues().size();\n        if (subcolumnWidth < 1) {\n            subcolumnWidth = 1;\n        }\n        // Columns are indexes from 0 to n, column index is also column X value\n        final float rawX = computator.computeRawX(columnIndex);\n        final float halfColumnWidth = columnWidth / 2;\n        final float baseRawY = computator.computeRawY(baseValue);\n        // First subcolumn will starts at the left edge of current column,\n        // rawValueX is horizontal center of that column\n        float subcolumnRawX = rawX - halfColumnWidth;\n        int valueIndex = 0;\n        for (SubcolumnValue columnValue : column.getValues()) {\n            columnPaint.setColor(columnValue.getColor());\n            if (subcolumnRawX > rawX + halfColumnWidth) {\n                break;\n            }\n            final float rawY = computator.computeRawY(columnValue.getValue());\n            calculateRectToDraw(columnValue, subcolumnRawX, subcolumnRawX + subcolumnWidth, baseRawY, rawY);\n            switch (mode) {\n                case MODE_DRAW:\n                    drawSubcolumn(canvas, column, columnValue, false);\n                    break;\n                case MODE_HIGHLIGHT:\n                    highlightSubcolumn(canvas, column, columnValue, valueIndex, false);\n                    break;\n                case MODE_CHECK_TOUCH:\n                    checkRectToDraw(columnIndex, valueIndex);\n                    break;\n                default:\n                    // There no else, every case should be handled or exception will\n                    // be thrown\n                    throw new IllegalStateException(\"Cannot process column in mode: \" + mode);\n            }\n            subcolumnRawX += subcolumnWidth + subcolumnSpacing;\n            ++valueIndex;\n        }\n    }\n\n    private void drawColumnForStacked(Canvas canvas) {\n        final ColumnChartData data = dataProvider.getColumnChartData();\n        final float columnWidth = calculateColumnWidth();\n        // Columns are indexes from 0 to n, column index is also column X value\n        int columnIndex = 0;\n        for (Column column : data.getColumns()) {\n            processColumnForStacked(canvas, column, columnWidth, columnIndex, MODE_DRAW);\n            ++columnIndex;\n        }\n    }\n\n    private void highlightColumnForStacked(Canvas canvas) {\n        final ColumnChartData data = dataProvider.getColumnChartData();\n        final float columnWidth = calculateColumnWidth();\n        // Columns are indexes from 0 to n, column index is also column X value\n        Column column = data.getColumns().get(selectedValue.getFirstIndex());\n        processColumnForStacked(canvas, column, columnWidth, selectedValue.getFirstIndex(), MODE_HIGHLIGHT);\n    }\n\n    private void checkTouchForStacked(float touchX, float touchY) {\n        touchedPoint.x = touchX;\n        touchedPoint.y = touchY;\n        final ColumnChartData data = dataProvider.getColumnChartData();\n        final float columnWidth = calculateColumnWidth();\n        int columnIndex = 0;\n        for (Column column : data.getColumns()) {\n            // canvas is not needed for checking touch\n            processColumnForStacked(null, column, columnWidth, columnIndex, MODE_CHECK_TOUCH);\n            ++columnIndex;\n        }\n    }\n\n    private void processColumnForStacked(Canvas canvas, Column column, float columnWidth, int columnIndex, int mode) {\n        final float rawX = computator.computeRawX(columnIndex);\n        final float halfColumnWidth = columnWidth / 2;\n        float mostPositiveValue = baseValue;\n        float mostNegativeValue = baseValue;\n        float subcolumnBaseValue = baseValue;\n        int valueIndex = 0;\n        for (SubcolumnValue columnValue : column.getValues()) {\n            columnPaint.setColor(columnValue.getColor());\n            if (columnValue.getValue() >= baseValue) {\n                // Using values instead of raw pixels make code easier to\n                // understand(for me)\n                subcolumnBaseValue = mostPositiveValue;\n                mostPositiveValue += columnValue.getValue();\n            } else {\n                subcolumnBaseValue = mostNegativeValue;\n                mostNegativeValue += columnValue.getValue();\n            }\n            final float rawBaseY = computator.computeRawY(subcolumnBaseValue);\n            final float rawY = computator.computeRawY(subcolumnBaseValue + columnValue.getValue());\n            calculateRectToDraw(columnValue, rawX - halfColumnWidth, rawX + halfColumnWidth, rawBaseY, rawY);\n            switch (mode) {\n                case MODE_DRAW:\n                    drawSubcolumn(canvas, column, columnValue, true);\n                    break;\n                case MODE_HIGHLIGHT:\n                    highlightSubcolumn(canvas, column, columnValue, valueIndex, true);\n                    break;\n                case MODE_CHECK_TOUCH:\n                    checkRectToDraw(columnIndex, valueIndex);\n                    break;\n                default:\n                    // There no else, every case should be handled or exception will\n                    // be thrown\n                    throw new IllegalStateException(\"Cannot process column in mode: \" + mode);\n            }\n            ++valueIndex;\n        }\n    }\n\n    private void drawSubcolumn(Canvas canvas, Column column, SubcolumnValue columnValue, boolean isStacked) {\n        canvas.drawRect(drawRect, columnPaint);\n        if (column.hasLabels()) {\n            drawLabel(canvas, column, columnValue, isStacked, labelOffset);\n        }\n    }\n\n    private void highlightSubcolumn(Canvas canvas, Column column, SubcolumnValue columnValue, int valueIndex,\n                                    boolean isStacked) {\n        if (selectedValue.getSecondIndex() == valueIndex) {\n            columnPaint.setColor(columnValue.getDarkenColor());\n            canvas.drawRect(drawRect.left - touchAdditionalWidth, drawRect.top, drawRect.right + touchAdditionalWidth,\n                    drawRect.bottom, columnPaint);\n            if (column.hasLabels() || column.hasLabelsOnlyForSelected()) {\n                drawLabel(canvas, column, columnValue, isStacked, labelOffset);\n            }\n        }\n    }\n\n    private void checkRectToDraw(int columnIndex, int valueIndex) {\n        if (drawRect.contains(touchedPoint.x, touchedPoint.y)) {\n            selectedValue.set(columnIndex, valueIndex, SelectedValueType.COLUMN);\n        }\n    }\n\n    private float calculateColumnWidth() {\n        // columnWidht should be at least 2 px\n        float columnWidth = fillRatio * computator.getContentRectMinusAllMargins().width() / computator\n                .getVisibleViewport().width();\n        if (columnWidth < 2) {\n            columnWidth = 2;\n        }\n        return columnWidth;\n    }\n\n    private void calculateRectToDraw(SubcolumnValue columnValue, float left, float right, float rawBaseY, float rawY) {\n        // Calculate rect that will be drawn as column, subcolumn or label background.\n        drawRect.left = left;\n        drawRect.right = right;\n        if (columnValue.getValue() >= baseValue) {\n            drawRect.top = rawY;\n            drawRect.bottom = rawBaseY - subcolumnSpacing;\n        } else {\n            drawRect.bottom = rawY;\n            drawRect.top = rawBaseY + subcolumnSpacing;\n        }\n    }\n\n    private void drawLabel(Canvas canvas, Column column, SubcolumnValue columnValue, boolean isStacked, float offset) {\n        final int numChars = column.getFormatter().formatChartValue(labelBuffer, columnValue);\n\n        if (numChars == 0) {\n            // No need to draw empty label\n            return;\n        }\n\n        final float labelWidth = labelPaint.measureText(labelBuffer, labelBuffer.length - numChars, numChars);\n        final int labelHeight = Math.abs(fontMetrics.ascent);\n        float left = drawRect.centerX() - labelWidth / 2 - labelMargin;\n        float right = drawRect.centerX() + labelWidth / 2 + labelMargin;\n        float top;\n        float bottom;\n        if (isStacked && labelHeight < drawRect.height() - (2 * labelMargin)) {\n            // For stacked columns draw label only if label height is less than subcolumn height - (2 * labelMargin).\n            if (columnValue.getValue() >= baseValue) {\n                top = drawRect.top;\n                bottom = drawRect.top + labelHeight + labelMargin * 2;\n            } else {\n                top = drawRect.bottom - labelHeight - labelMargin * 2;\n                bottom = drawRect.bottom;\n            }\n        } else if (!isStacked) {\n            // For not stacked draw label at the top for positive and at the bottom for negative values\n            if (columnValue.getValue() >= baseValue) {\n                top = drawRect.top - offset - labelHeight - labelMargin * 2;\n                if (top < computator.getContentRectMinusAllMargins().top) {\n                    top = drawRect.top + offset;\n                    bottom = drawRect.top + offset + labelHeight + labelMargin * 2;\n                } else {\n                    bottom = drawRect.top - offset;\n                }\n            } else {\n                bottom = drawRect.bottom + offset + labelHeight + labelMargin * 2;\n                if (bottom > computator.getContentRectMinusAllMargins().bottom) {\n                    top = drawRect.bottom - offset - labelHeight - labelMargin * 2;\n                    bottom = drawRect.bottom - offset;\n                } else {\n                    top = drawRect.bottom + offset;\n                }\n            }\n        } else {\n            // Draw nothing.\n            return;\n        }\n\n        labelBackgroundRect.set(left, top, right, bottom);\n        drawLabelTextAndBackground(canvas, labelBuffer, labelBuffer.length - numChars, numChars,\n                columnValue.getDarkenColor());\n\n    }\n\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/renderer/ComboChartRenderer.java",
    "content": "package lecho.lib.hellocharts.renderer;\n\nimport android.content.Context;\nimport android.graphics.Canvas;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport lecho.lib.hellocharts.model.Viewport;\nimport lecho.lib.hellocharts.view.Chart;\n\npublic class ComboChartRenderer extends AbstractChartRenderer {\n\n    protected List<ChartRenderer> renderers;\n    protected Viewport unionViewport = new Viewport();\n\n    public ComboChartRenderer(Context context, Chart chart) {\n        super(context, chart);\n        this.renderers = new ArrayList<>();\n    }\n\n    @Override\n    public void onChartSizeChanged() {\n        for (ChartRenderer renderer : renderers) {\n            renderer.onChartSizeChanged();\n        }\n    }\n\n    @Override\n    public void onChartDataChanged() {\n        super.onChartDataChanged();\n        for (ChartRenderer renderer : renderers) {\n            renderer.onChartDataChanged();\n        }\n        onChartViewportChanged();\n    }\n\n    @Override\n    public void onChartViewportChanged() {\n        if (isViewportCalculationEnabled) {\n            int rendererIndex = 0;\n            for (ChartRenderer renderer : renderers) {\n                renderer.onChartViewportChanged();\n                if (rendererIndex == 0) {\n                    unionViewport.set(renderer.getMaximumViewport());\n                } else {\n                    unionViewport.union(renderer.getMaximumViewport());\n                }\n                ++rendererIndex;\n            }\n            computator.setMaxViewport(unionViewport);\n            computator.setCurrentViewport(unionViewport);\n        }\n\n\n    }\n\n    public void draw(Canvas canvas) {\n        for (ChartRenderer renderer : renderers) {\n            renderer.draw(canvas);\n        }\n    }\n\n    @Override\n    public void drawUnclipped(Canvas canvas) {\n        for (ChartRenderer renderer : renderers) {\n            renderer.drawUnclipped(canvas);\n        }\n    }\n\n    public boolean checkTouch(float touchX, float touchY) {\n        selectedValue.clear();\n        int rendererIndex = renderers.size() - 1;\n        for (; rendererIndex >= 0; rendererIndex--) {\n            ChartRenderer renderer = renderers.get(rendererIndex);\n            if (renderer.checkTouch(touchX, touchY)) {\n                selectedValue.set(renderer.getSelectedValue());\n                break;\n            }\n        }\n\n        //clear the rest of renderers if value was selected, if value was not selected this loop\n        // will not be executed.\n        for (rendererIndex--; rendererIndex >= 0; rendererIndex--) {\n            ChartRenderer renderer = renderers.get(rendererIndex);\n            renderer.clearTouch();\n        }\n\n        return isTouched();\n    }\n\n    @Override\n    public void clearTouch() {\n        for (ChartRenderer renderer : renderers) {\n            renderer.clearTouch();\n        }\n        selectedValue.clear();\n    }\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/renderer/ComboLineColumnChartRenderer.java",
    "content": "package lecho.lib.hellocharts.renderer;\n\nimport android.content.Context;\n\nimport lecho.lib.hellocharts.provider.ColumnChartDataProvider;\nimport lecho.lib.hellocharts.provider.LineChartDataProvider;\nimport lecho.lib.hellocharts.view.Chart;\n\npublic class ComboLineColumnChartRenderer extends ComboChartRenderer {\n\n    private ColumnChartRenderer columnChartRenderer;\n    private LineChartRenderer lineChartRenderer;\n\n    public ComboLineColumnChartRenderer(Context context, Chart chart, ColumnChartDataProvider columnChartDataProvider,\n                                        LineChartDataProvider lineChartDataProvider) {\n        this(context, chart, new ColumnChartRenderer(context, chart, columnChartDataProvider),\n                new LineChartRenderer(context, chart, lineChartDataProvider));\n    }\n\n    public ComboLineColumnChartRenderer(Context context, Chart chart, ColumnChartRenderer columnChartRenderer,\n                                        LineChartDataProvider lineChartDataProvider) {\n        this(context, chart, columnChartRenderer, new LineChartRenderer(context, chart, lineChartDataProvider));\n    }\n\n    public ComboLineColumnChartRenderer(Context context, Chart chart, ColumnChartDataProvider columnChartDataProvider,\n                                        LineChartRenderer lineChartRenderer) {\n        this(context, chart, new ColumnChartRenderer(context, chart, columnChartDataProvider), lineChartRenderer);\n    }\n\n    public ComboLineColumnChartRenderer(Context context, Chart chart, ColumnChartRenderer columnChartRenderer,\n                                        LineChartRenderer lineChartRenderer) {\n        super(context, chart);\n\n        this.columnChartRenderer = columnChartRenderer;\n        this.lineChartRenderer = lineChartRenderer;\n\n        renderers.add(this.columnChartRenderer);\n        renderers.add(this.lineChartRenderer);\n    }\n}"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/renderer/LineChartRenderer.java",
    "content": "package lecho.lib.hellocharts.renderer;\n\nimport android.content.Context;\nimport android.graphics.Bitmap;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.LinearGradient;\nimport android.graphics.Paint;\nimport android.graphics.Paint.Cap;\nimport android.graphics.Path;\nimport android.graphics.PorterDuff.Mode;\nimport android.graphics.Rect;\nimport android.graphics.Shader;\n\nimport lecho.lib.hellocharts.model.Line;\nimport lecho.lib.hellocharts.model.LineChartData;\nimport lecho.lib.hellocharts.model.PointValue;\nimport lecho.lib.hellocharts.model.SelectedValue.SelectedValueType;\nimport lecho.lib.hellocharts.model.ValueShape;\nimport lecho.lib.hellocharts.model.Viewport;\nimport lecho.lib.hellocharts.provider.LineChartDataProvider;\nimport lecho.lib.hellocharts.util.ChartUtils;\nimport lecho.lib.hellocharts.view.Chart;\n\n/**\n * Renderer for line chart. Can draw lines, cubic lines, filled area chart and scattered chart.\n */\npublic class LineChartRenderer extends AbstractChartRenderer {\n    private static final float LINE_SMOOTHNESS = 0.16f;\n    private static final int DEFAULT_LINE_STROKE_WIDTH_DP = 3;\n    private static final int DEFAULT_TOUCH_TOLERANCE_MARGIN_DP = 4;\n\n    private static final int MODE_DRAW = 0;\n    private static final int MODE_HIGHLIGHT = 1;\n\n    private LineChartDataProvider dataProvider;\n\n    private int checkPrecision;\n\n    private float baseValue;\n\n    private int touchToleranceMargin;\n    private Path path = new Path();\n    private Paint linePaint = new Paint();\n    private Paint pointPaint = new Paint();\n\n    private Bitmap softwareBitmap;\n    private Canvas softwareCanvas = new Canvas();\n    private Viewport tempMaximumViewport = new Viewport();\n\n    public LineChartRenderer(Context context, Chart chart, LineChartDataProvider dataProvider) {\n        super(context, chart);\n        this.dataProvider = dataProvider;\n\n        touchToleranceMargin = ChartUtils.dp2px(density, DEFAULT_TOUCH_TOLERANCE_MARGIN_DP);\n\n        linePaint.setAntiAlias(true);\n        linePaint.setStyle(Paint.Style.STROKE);\n        linePaint.setStrokeCap(Cap.ROUND);\n        linePaint.setStrokeWidth(ChartUtils.dp2px(density, DEFAULT_LINE_STROKE_WIDTH_DP));\n\n        pointPaint.setAntiAlias(true);\n        pointPaint.setStyle(Paint.Style.FILL);\n\n        checkPrecision = ChartUtils.dp2px(density, 2);\n\n    }\n\n    public void onChartSizeChanged() {\n        final int internalMargin = calculateContentRectInternalMargin();\n        computator.insetContentRectByInternalMargins(internalMargin, internalMargin,\n                internalMargin, internalMargin);\n        if (computator.getChartWidth() > 0 && computator.getChartHeight() > 0) {\n            softwareBitmap = Bitmap.createBitmap(computator.getChartWidth(), computator.getChartHeight(),\n                    Bitmap.Config.ARGB_8888);\n            softwareCanvas.setBitmap(softwareBitmap);\n        }\n    }\n\n    @Override\n    public void onChartDataChanged() {\n        super.onChartDataChanged();\n        final int internalMargin = calculateContentRectInternalMargin();\n        computator.insetContentRectByInternalMargins(internalMargin, internalMargin,\n                internalMargin, internalMargin);\n        baseValue = dataProvider.getLineChartData().getBaseValue();\n\n        onChartViewportChanged();\n    }\n\n    @Override\n    public void onChartViewportChanged() {\n        if (isViewportCalculationEnabled) {\n            calculateMaxViewport();\n            computator.setMaxViewport(tempMaximumViewport);\n            computator.setCurrentViewport(computator.getMaximumViewport());\n        }\n    }\n\n    @Override\n    public void draw(Canvas canvas) {\n        final LineChartData data = dataProvider.getLineChartData();\n\n        final Canvas drawCanvas;\n\n        // softwareBitmap can be null if chart is rendered in layout editor. In that case use default canvas and not\n        // softwareCanvas.\n        if (null != softwareBitmap) {\n            drawCanvas = softwareCanvas;\n            drawCanvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);\n        } else {\n            drawCanvas = canvas;\n        }\n\n        for (Line line : data.getLines()) {\n            if (line.hasLines()) {\n                if (line.isCubic()) {\n                    drawSmoothPath(drawCanvas, line);\n                } else if (line.isSquare()) {\n                    drawSquarePath(drawCanvas, line);\n                } else {\n                    drawPath(drawCanvas, line);\n                }\n            }\n        }\n\n        if (null != softwareBitmap) {\n            canvas.drawBitmap(softwareBitmap, 0, 0, null);\n        }\n    }\n\n    @Override\n    public void drawUnclipped(Canvas canvas) {\n        final LineChartData data = dataProvider.getLineChartData();\n        int lineIndex = 0;\n        for (Line line : data.getLines()) {\n            if (checkIfShouldDrawPoints(line)) {\n                drawPoints(canvas, line, lineIndex, MODE_DRAW);\n            }\n            ++lineIndex;\n        }\n        if (isTouched()) {\n            // Redraw touched point to bring it to the front\n            highlightPoints(canvas);\n        }\n    }\n\n    private boolean checkIfShouldDrawPoints(Line line) {\n        return line.hasPoints() || line.getValues().size() == 1;\n    }\n\n    @Override\n    public boolean checkTouch(float touchX, float touchY) {\n        selectedValue.clear();\n        final LineChartData data = dataProvider.getLineChartData();\n        int lineIndex = 0;\n        for (Line line : data.getLines()) {\n            if (checkIfShouldDrawPoints(line)) {\n                int pointRadius = ChartUtils.dp2px(density, line.getPointRadius());\n                int valueIndex = 0;\n                for (PointValue pointValue : line.getValues()) {\n                    final float rawValueX = computator.computeRawX(pointValue.getX());\n                    final float rawValueY = computator.computeRawY(pointValue.getY());\n                    if (isInArea(rawValueX, rawValueY, touchX, touchY, pointRadius + touchToleranceMargin)) {\n                        selectedValue.set(lineIndex, valueIndex, SelectedValueType.LINE);\n                    }\n                    ++valueIndex;\n                }\n            }\n            ++lineIndex;\n        }\n        return isTouched();\n    }\n\n    private void calculateMaxViewport() {\n        tempMaximumViewport.set(Float.MAX_VALUE, Float.MIN_VALUE, Float.MIN_VALUE, Float.MAX_VALUE);\n        LineChartData data = dataProvider.getLineChartData();\n\n        for (Line line : data.getLines()) {\n            // Calculate max and min for viewport.\n            for (PointValue pointValue : line.getValues()) {\n                if (pointValue.getX() < tempMaximumViewport.left) {\n                    tempMaximumViewport.left = pointValue.getX();\n                }\n                if (pointValue.getX() > tempMaximumViewport.right) {\n                    tempMaximumViewport.right = pointValue.getX();\n                }\n                if (pointValue.getY() < tempMaximumViewport.bottom) {\n                    tempMaximumViewport.bottom = pointValue.getY();\n                }\n                if (pointValue.getY() > tempMaximumViewport.top) {\n                    tempMaximumViewport.top = pointValue.getY();\n                }\n\n            }\n        }\n    }\n\n    private int calculateContentRectInternalMargin() {\n        int contentAreaMargin = 0;\n        final LineChartData data = dataProvider.getLineChartData();\n        for (Line line : data.getLines()) {\n            if (checkIfShouldDrawPoints(line)) {\n                int margin = line.getPointRadius() + DEFAULT_TOUCH_TOLERANCE_MARGIN_DP;\n                if (margin > contentAreaMargin) {\n                    contentAreaMargin = margin;\n                }\n            }\n        }\n        return ChartUtils.dp2px(density, contentAreaMargin);\n    }\n\n    /**\n     * Draws lines, uses path for drawing filled area on software canvas. Line is drawn with canvas.drawLines() method.\n     */\n    private void drawPath(Canvas canvas, final Line line) {\n        prepareLinePaint(line);\n\n        int valueIndex = 0;\n        for (PointValue pointValue : line.getValues()) {\n\n            final float rawX = computator.computeRawX(pointValue.getX());\n            final float rawY = computator.computeRawY(pointValue.getY());\n\n            if (valueIndex == 0) {\n                path.moveTo(rawX, rawY);\n            } else {\n                path.lineTo(rawX, rawY);\n            }\n\n            ++valueIndex;\n\n        }\n\n        canvas.drawPath(path, linePaint);\n\n        if (line.isFilled()) {\n            drawArea(canvas, line);\n        }\n\n        path.reset();\n    }\n\n    private void drawSquarePath(Canvas canvas, final Line line) {\n        prepareLinePaint(line);\n\n        int valueIndex = 0;\n        float previousRawY = 0;\n        for (PointValue pointValue : line.getValues()) {\n\n            final float rawX = computator.computeRawX(pointValue.getX());\n            final float rawY = computator.computeRawY(pointValue.getY());\n\n            if (valueIndex == 0) {\n                path.moveTo(rawX, rawY);\n            } else {\n                path.lineTo(rawX, previousRawY);\n                path.lineTo(rawX, rawY);\n            }\n\n            previousRawY = rawY;\n\n            ++valueIndex;\n\n        }\n\n        canvas.drawPath(path, linePaint);\n\n        if (line.isFilled()) {\n            drawArea(canvas, line);\n        }\n\n        path.reset();\n    }\n\n    private void drawSmoothPath(Canvas canvas, final Line line) {\n        prepareLinePaint(line);\n\n        final int lineSize = line.getValues().size();\n        float prePreviousPointX = Float.NaN;\n        float prePreviousPointY = Float.NaN;\n        float previousPointX = Float.NaN;\n        float previousPointY = Float.NaN;\n        float currentPointX = Float.NaN;\n        float currentPointY = Float.NaN;\n        float nextPointX = Float.NaN;\n        float nextPointY = Float.NaN;\n\n        for (int valueIndex = 0; valueIndex < lineSize; ++valueIndex) {\n            if (Float.isNaN(currentPointX)) {\n                PointValue linePoint = line.getValues().get(valueIndex);\n                currentPointX = computator.computeRawX(linePoint.getX());\n                currentPointY = computator.computeRawY(linePoint.getY());\n            }\n            if (Float.isNaN(previousPointX)) {\n                if (valueIndex > 0) {\n                    PointValue linePoint = line.getValues().get(valueIndex - 1);\n                    previousPointX = computator.computeRawX(linePoint.getX());\n                    previousPointY = computator.computeRawY(linePoint.getY());\n                } else {\n                    previousPointX = currentPointX;\n                    previousPointY = currentPointY;\n                }\n            }\n\n            if (Float.isNaN(prePreviousPointX)) {\n                if (valueIndex > 1) {\n                    PointValue linePoint = line.getValues().get(valueIndex - 2);\n                    prePreviousPointX = computator.computeRawX(linePoint.getX());\n                    prePreviousPointY = computator.computeRawY(linePoint.getY());\n                } else {\n                    prePreviousPointX = previousPointX;\n                    prePreviousPointY = previousPointY;\n                }\n            }\n\n            // nextPoint is always new one or it is equal currentPoint.\n            if (valueIndex < lineSize - 1) {\n                PointValue linePoint = line.getValues().get(valueIndex + 1);\n                nextPointX = computator.computeRawX(linePoint.getX());\n                nextPointY = computator.computeRawY(linePoint.getY());\n            } else {\n                nextPointX = currentPointX;\n                nextPointY = currentPointY;\n            }\n\n            if (valueIndex == 0) {\n                // Move to start point.\n                path.moveTo(currentPointX, currentPointY);\n            } else {\n                // Calculate control points.\n                final float firstDiffX = (currentPointX - prePreviousPointX);\n                final float firstDiffY = (currentPointY - prePreviousPointY);\n                final float secondDiffX = (nextPointX - previousPointX);\n                final float secondDiffY = (nextPointY - previousPointY);\n                final float firstControlPointX = previousPointX + (LINE_SMOOTHNESS * firstDiffX);\n                final float firstControlPointY = previousPointY + (LINE_SMOOTHNESS * firstDiffY);\n                final float secondControlPointX = currentPointX - (LINE_SMOOTHNESS * secondDiffX);\n                final float secondControlPointY = currentPointY - (LINE_SMOOTHNESS * secondDiffY);\n                path.cubicTo(firstControlPointX, firstControlPointY, secondControlPointX, secondControlPointY,\n                        currentPointX, currentPointY);\n            }\n\n            // Shift values by one back to prevent recalculation of values that have\n            // been already calculated.\n            prePreviousPointX = previousPointX;\n            prePreviousPointY = previousPointY;\n            previousPointX = currentPointX;\n            previousPointY = currentPointY;\n            currentPointX = nextPointX;\n            currentPointY = nextPointY;\n        }\n\n        canvas.drawPath(path, linePaint);\n        if (line.isFilled()) {\n            drawArea(canvas, line);\n        }\n        path.reset();\n    }\n\n    private void prepareLinePaint(final Line line) {\n        linePaint.setStrokeWidth(ChartUtils.dp2px(density, line.getStrokeWidth()));\n        linePaint.setColor(line.getColor());\n        linePaint.setPathEffect(line.getPathEffect());\n        linePaint.setShader(null);\n    }\n\n    // TODO Drawing points can be done in the same loop as drawing lines but it\n    // may cause problems in the future with\n    // implementing point styles.\n    private void drawPoints(Canvas canvas, Line line, int lineIndex, int mode) {\n        pointPaint.setColor(line.getPointColor());\n        int valueIndex = 0;\n        for (PointValue pointValue : line.getValues()) {\n            int pointRadius = ChartUtils.dp2px(density, line.getPointRadius());\n            final float rawX = computator.computeRawX(pointValue.getX());\n            final float rawY = computator.computeRawY(pointValue.getY());\n            if (computator.isWithinContentRect(rawX, rawY, checkPrecision)) {\n                // Draw points only if they are within contentRectMinusAllMargins, using contentRectMinusAllMargins\n                // instead of viewport to avoid some\n                // float rounding problems.\n                if (MODE_DRAW == mode) {\n                    drawPoint(canvas, line, pointValue, rawX, rawY, pointRadius);\n                    if (line.hasLabels()) {\n                        drawLabel(canvas, line, pointValue, rawX, rawY, pointRadius + labelOffset);\n                    }\n                } else if (MODE_HIGHLIGHT == mode) {\n                    highlightPoint(canvas, line, pointValue, rawX, rawY, lineIndex, valueIndex);\n                } else {\n                    throw new IllegalStateException(\"Cannot process points in mode: \" + mode);\n                }\n            }\n            ++valueIndex;\n        }\n    }\n\n    private void drawPoint(Canvas canvas, Line line, PointValue pointValue, float rawX, float rawY,\n                           float pointRadius) {\n        if (ValueShape.SQUARE.equals(line.getShape())) {\n            canvas.drawRect(rawX - pointRadius, rawY - pointRadius, rawX + pointRadius, rawY + pointRadius,\n                    pointPaint);\n        } else if (ValueShape.CIRCLE.equals(line.getShape())) {\n            canvas.drawCircle(rawX, rawY, pointRadius, pointPaint);\n        } else if (ValueShape.DIAMOND.equals(line.getShape())) {\n            canvas.save();\n            canvas.rotate(45, rawX, rawY);\n            canvas.drawRect(rawX - pointRadius, rawY - pointRadius, rawX + pointRadius, rawY + pointRadius,\n                    pointPaint);\n            canvas.restore();\n        } else {\n            throw new IllegalArgumentException(\"Invalid point shape: \" + line.getShape());\n        }\n    }\n\n    private void highlightPoints(Canvas canvas) {\n        int lineIndex = selectedValue.getFirstIndex();\n        Line line = dataProvider.getLineChartData().getLines().get(lineIndex);\n        drawPoints(canvas, line, lineIndex, MODE_HIGHLIGHT);\n    }\n\n    private void highlightPoint(Canvas canvas, Line line, PointValue pointValue, float rawX, float rawY, int lineIndex,\n                                int valueIndex) {\n        if (selectedValue.getFirstIndex() == lineIndex && selectedValue.getSecondIndex() == valueIndex) {\n            int pointRadius = ChartUtils.dp2px(density, line.getPointRadius());\n            pointPaint.setColor(line.getDarkenColor());\n            drawPoint(canvas, line, pointValue, rawX, rawY, pointRadius + touchToleranceMargin);\n            if (line.hasLabels() || line.hasLabelsOnlyForSelected()) {\n                drawLabel(canvas, line, pointValue, rawX, rawY, pointRadius + labelOffset);\n            }\n        }\n    }\n\n    private void drawLabel(Canvas canvas, Line line, PointValue pointValue, float rawX, float rawY, float offset) {\n        final Rect contentRect = computator.getContentRectMinusAllMargins();\n        final int numChars = line.getFormatter().formatChartValue(labelBuffer, pointValue);\n        if (numChars == 0) {\n            // No need to draw empty label\n            return;\n        }\n\n        final float labelWidth = labelPaint.measureText(labelBuffer, labelBuffer.length - numChars, numChars);\n        final int labelHeight = Math.abs(fontMetrics.ascent);\n        float left = rawX - labelWidth / 2 - labelMargin;\n        float right = rawX + labelWidth / 2 + labelMargin;\n\n        float top;\n        float bottom;\n\n        if (pointValue.getY() >= baseValue) {\n            top = rawY - offset - labelHeight - labelMargin * 2;\n            bottom = rawY - offset;\n        } else {\n            top = rawY + offset;\n            bottom = rawY + offset + labelHeight + labelMargin * 2;\n        }\n\n        if (top < contentRect.top) {\n            top = rawY + offset;\n            bottom = rawY + offset + labelHeight + labelMargin * 2;\n        }\n        if (bottom > contentRect.bottom) {\n            top = rawY - offset - labelHeight - labelMargin * 2;\n            bottom = rawY - offset;\n        }\n        if (left < contentRect.left) {\n            left = rawX;\n            right = rawX + labelWidth + labelMargin * 2;\n        }\n        if (right > contentRect.right) {\n            left = rawX - labelWidth - labelMargin * 2;\n            right = rawX;\n        }\n\n        labelBackgroundRect.set(left, top, right, bottom);\n        drawLabelTextAndBackground(canvas, labelBuffer, labelBuffer.length - numChars, numChars,\n                line.getDarkenColor());\n    }\n\n    private void drawArea(Canvas canvas, Line line) {\n        final int lineSize = line.getValues().size();\n        if (lineSize < 2) {\n            //No point to draw area for one point or empty line.\n            return;\n        }\n\n        final Rect contentRect = computator.getContentRectMinusAllMargins();\n        final float baseRawValue = Math.min(contentRect.bottom, Math.max(computator.computeRawY(baseValue),\n                contentRect.top));\n        //That checks works only if the last point is the right most one.\n        final float left = Math.max(computator.computeRawX(line.getValues().get(0).getX()), contentRect.left);\n        final float right = Math.min(computator.computeRawX(line.getValues().get(lineSize - 1).getX()),\n                contentRect.right);\n\n        path.lineTo(right, baseRawValue);\n        path.lineTo(left, baseRawValue);\n        path.close();\n\n        linePaint.setStyle(Paint.Style.FILL);\n        linePaint.setAlpha(line.getAreaTransparency());\n        linePaint.setShader(line.getGradientToTransparent() ?\n                new LinearGradient(0, 0, 0, canvas.getHeight(), line.getColor(),\n                        line.getColor() & 0x00ffffff, Shader.TileMode.MIRROR) :\n                null);\n        canvas.drawPath(path, linePaint);\n        linePaint.setStyle(Paint.Style.STROKE);\n    }\n\n    private boolean isInArea(float x, float y, float touchX, float touchY, float radius) {\n        float diffX = touchX - x;\n        float diffY = touchY - y;\n        return Math.pow(diffX, 2) + Math.pow(diffY, 2) <= 2 * Math.pow(radius, 2);\n    }\n\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/renderer/PieChartRenderer.java",
    "content": "package lecho.lib.hellocharts.renderer;\n\nimport android.content.Context;\nimport android.graphics.Bitmap;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Paint;\nimport android.graphics.Paint.Align;\nimport android.graphics.Paint.FontMetricsInt;\nimport android.graphics.PointF;\nimport android.graphics.PorterDuff;\nimport android.graphics.PorterDuffXfermode;\nimport android.graphics.Rect;\nimport android.graphics.RectF;\nimport android.text.TextUtils;\n\nimport lecho.lib.hellocharts.formatter.PieChartValueFormatter;\nimport lecho.lib.hellocharts.model.PieChartData;\nimport lecho.lib.hellocharts.model.SelectedValue;\nimport lecho.lib.hellocharts.model.SelectedValue.SelectedValueType;\nimport lecho.lib.hellocharts.model.SliceValue;\nimport lecho.lib.hellocharts.model.Viewport;\nimport lecho.lib.hellocharts.provider.PieChartDataProvider;\nimport lecho.lib.hellocharts.util.ChartUtils;\nimport lecho.lib.hellocharts.view.Chart;\n\n/**\n * Default renderer for PieChart. PieChart doesn't use viewport concept so it a little different than others chart\n * types.\n */\npublic class PieChartRenderer extends AbstractChartRenderer {\n    private static final float MAX_WIDTH_HEIGHT = 100f;\n    private static final int DEFAULT_START_ROTATION = 45;\n    private static final float DEFAULT_LABEL_INSIDE_RADIUS_FACTOR = 0.7f;\n    private static final float DEFAULT_LABEL_OUTSIDE_RADIUS_FACTOR = 1.0f;\n    private static final int DEFAULT_TOUCH_ADDITIONAL_DP = 8;\n    private static final int MODE_DRAW = 0;\n    private static final int MODE_HIGHLIGHT = 1;\n    private int rotation = DEFAULT_START_ROTATION;\n    private PieChartDataProvider dataProvider;\n    private Paint slicePaint = new Paint();\n    private float maxSum;\n    private RectF originCircleOval = new RectF();\n    private RectF drawCircleOval = new RectF();\n    private PointF sliceVector = new PointF();\n    private int touchAdditional;\n    private float circleFillRatio = 1.0f;\n\n    // Center circle related attributes\n    private boolean hasCenterCircle;\n    private float centerCircleScale;\n    private Paint centerCirclePaint = new Paint();\n    // Text1\n    private Paint centerCircleText1Paint = new Paint();\n    private FontMetricsInt centerCircleText1FontMetrics = new FontMetricsInt();\n    // Text2\n    private Paint centerCircleText2Paint = new Paint();\n    private FontMetricsInt centerCircleText2FontMetrics = new FontMetricsInt();\n    // Separation lines\n    private Paint separationLinesPaint = new Paint();\n\n    private boolean hasLabelsOutside;\n    private boolean hasLabels;\n    private boolean hasLabelsOnlyForSelected;\n    private PieChartValueFormatter valueFormatter;\n    private Viewport tempMaximumViewport = new Viewport();\n\n    private Bitmap softwareBitmap;\n    private Canvas softwareCanvas = new Canvas();\n\n    public PieChartRenderer(Context context, Chart chart, PieChartDataProvider dataProvider) {\n        super(context, chart);\n        this.dataProvider = dataProvider;\n        touchAdditional = ChartUtils.dp2px(density, DEFAULT_TOUCH_ADDITIONAL_DP);\n\n        slicePaint.setAntiAlias(true);\n        slicePaint.setStyle(Paint.Style.FILL);\n\n        centerCirclePaint.setAntiAlias(true);\n        centerCirclePaint.setStyle(Paint.Style.FILL);\n        centerCirclePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));\n\n        centerCircleText1Paint.setAntiAlias(true);\n        centerCircleText1Paint.setTextAlign(Align.CENTER);\n\n        centerCircleText2Paint.setAntiAlias(true);\n        centerCircleText2Paint.setTextAlign(Align.CENTER);\n\n        separationLinesPaint.setAntiAlias(true);\n        separationLinesPaint.setStyle(Paint.Style.STROKE);\n        separationLinesPaint.setStrokeCap(Paint.Cap.ROUND);\n        separationLinesPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));\n        separationLinesPaint.setColor(Color.TRANSPARENT);\n    }\n\n    @Override\n    public void onChartSizeChanged() {\n        calculateCircleOval();\n\n        if (computator.getChartWidth() > 0 && computator.getChartHeight() > 0) {\n            softwareBitmap = Bitmap.createBitmap(computator.getChartWidth(), computator.getChartHeight(),\n                    Bitmap.Config.ARGB_8888);\n            softwareCanvas.setBitmap(softwareBitmap);\n        }\n    }\n\n    @Override\n    public void onChartDataChanged() {\n        super.onChartDataChanged();\n        final PieChartData data = dataProvider.getPieChartData();\n        hasLabelsOutside = data.hasLabelsOutside();\n        hasLabels = data.hasLabels();\n        hasLabelsOnlyForSelected = data.hasLabelsOnlyForSelected();\n        valueFormatter = data.getFormatter();\n        hasCenterCircle = data.hasCenterCircle();\n        centerCircleScale = data.getCenterCircleScale();\n        centerCirclePaint.setColor(data.getCenterCircleColor());\n        if (null != data.getCenterText1Typeface()) {\n            centerCircleText1Paint.setTypeface(data.getCenterText1Typeface());\n        }\n        centerCircleText1Paint.setTextSize(ChartUtils.sp2px(scaledDensity, data.getCenterText1FontSize()));\n        centerCircleText1Paint.setColor(data.getCenterText1Color());\n        centerCircleText1Paint.getFontMetricsInt(centerCircleText1FontMetrics);\n        if (null != data.getCenterText2Typeface()) {\n            centerCircleText2Paint.setTypeface(data.getCenterText2Typeface());\n        }\n        centerCircleText2Paint.setTextSize(ChartUtils.sp2px(scaledDensity, data.getCenterText2FontSize()));\n        centerCircleText2Paint.setColor(data.getCenterText2Color());\n        centerCircleText2Paint.getFontMetricsInt(centerCircleText2FontMetrics);\n\n        onChartViewportChanged();\n    }\n\n    @Override\n    public void onChartViewportChanged() {\n        if (isViewportCalculationEnabled) {\n            calculateMaxViewport();\n            computator.setMaxViewport(tempMaximumViewport);\n            computator.setCurrentViewport(computator.getMaximumViewport());\n        }\n    }\n\n    @Override\n    public void draw(Canvas canvas) {\n        // softwareBitmap can be null if chart is rendered in layout editor. In that case use default canvas and not\n        // softwareCanvas.\n        final Canvas drawCanvas;\n        if (null != softwareBitmap) {\n            drawCanvas = softwareCanvas;\n            drawCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);\n        } else {\n            drawCanvas = canvas;\n        }\n\n        drawSlices(drawCanvas);\n        drawSeparationLines(drawCanvas);\n        if (hasCenterCircle) {\n            drawCenterCircle(drawCanvas);\n        }\n        drawLabels(drawCanvas);\n\n        if (null != softwareBitmap) {\n            canvas.drawBitmap(softwareBitmap, 0, 0, null);\n        }\n    }\n\n    @Override\n    public void drawUnclipped(Canvas canvas) {\n    }\n\n    @Override\n    public boolean checkTouch(float touchX, float touchY) {\n        selectedValue.clear();\n        final PieChartData data = dataProvider.getPieChartData();\n        final float centerX = originCircleOval.centerX();\n        final float centerY = originCircleOval.centerY();\n        final float circleRadius = originCircleOval.width() / 2f;\n\n        sliceVector.set(touchX - centerX, touchY - centerY);\n        // Check if touch is on circle area, if not return false;\n        if (sliceVector.length() > circleRadius + touchAdditional) {\n            return false;\n        }\n        // Check if touch is not in center circle, if yes return false;\n        if (data.hasCenterCircle() && sliceVector.length() < circleRadius * data.getCenterCircleScale()) {\n            return false;\n        }\n\n        // Get touchAngle and align touch 0 degrees with chart 0 degrees, that why I subtracting start angle,\n        // adding 360\n        // and modulo 360 translates i.e -20 degrees to 340 degrees.\n        final float touchAngle = (pointToAngle(touchX, touchY, centerX, centerY) - rotation + 360f) % 360f;\n        final float sliceScale = 360f / maxSum;\n        float lastAngle = 0f; // No start angle here, see above\n        int sliceIndex = 0;\n        for (SliceValue sliceValue : data.getValues()) {\n            final float angle = Math.abs(sliceValue.getValue()) * sliceScale;\n            if (touchAngle >= lastAngle) {\n                selectedValue.set(sliceIndex, sliceIndex, SelectedValueType.NONE);\n            }\n            lastAngle += angle;\n            ++sliceIndex;\n        }\n        return isTouched();\n    }\n\n    /**\n     * Draw center circle with text if {@link PieChartData#hasCenterCircle()} is set true.\n     */\n    private void drawCenterCircle(Canvas canvas) {\n        final PieChartData data = dataProvider.getPieChartData();\n        final float circleRadius = originCircleOval.width() / 2f;\n        final float centerRadius = circleRadius * data.getCenterCircleScale();\n        final float centerX = originCircleOval.centerX();\n        final float centerY = originCircleOval.centerY();\n\n        canvas.drawCircle(centerX, centerY, centerRadius, centerCirclePaint);\n\n        // Draw center text1 and text2 if not empty.\n        if (!TextUtils.isEmpty(data.getCenterText1())) {\n\n            final int text1Height = Math.abs(centerCircleText1FontMetrics.ascent);\n\n            if (!TextUtils.isEmpty(data.getCenterText2())) {\n                // Draw text 2 only if text 1 is not empty.\n                final int text2Height = Math.abs(centerCircleText2FontMetrics.ascent);\n                canvas.drawText(data.getCenterText1(), centerX, centerY - text1Height * 0.2f, centerCircleText1Paint);\n                canvas.drawText(data.getCenterText2(), centerX, centerY + text2Height, centerCircleText2Paint);\n            } else {\n                canvas.drawText(data.getCenterText1(), centerX, centerY + text1Height / 4, centerCircleText1Paint);\n            }\n        }\n    }\n\n    /**\n     * Draw all slices for this PieChart, if mode == {@link #MODE_HIGHLIGHT} currently selected slices will be redrawn\n     * and\n     * highlighted.\n     *\n     * @param canvas\n     */\n    private void drawSlices(Canvas canvas) {\n        final PieChartData data = dataProvider.getPieChartData();\n        final float sliceScale = 360f / maxSum;\n        float lastAngle = rotation;\n        int sliceIndex = 0;\n        for (SliceValue sliceValue : data.getValues()) {\n            final float angle = Math.abs(sliceValue.getValue()) * sliceScale;\n            if (isTouched() && selectedValue.getFirstIndex() == sliceIndex) {\n                drawSlice(canvas, sliceValue, lastAngle, angle, MODE_HIGHLIGHT);\n            } else {\n                drawSlice(canvas, sliceValue, lastAngle, angle, MODE_DRAW);\n            }\n            lastAngle += angle;\n            ++sliceIndex;\n        }\n    }\n\n    private void drawSeparationLines(Canvas canvas) {\n        final PieChartData data = dataProvider.getPieChartData();\n        if (data.getValues().size() < 2) {\n            //No need for separation lines for 0 or 1 slices.\n            return;\n        }\n        final int sliceSpacing = ChartUtils.dp2px(density, data.getSlicesSpacing());\n        if (sliceSpacing < 1) {\n            //No need for separation lines\n            return;\n        }\n        final float sliceScale = 360f / maxSum;\n        float lastAngle = rotation;\n        final float circleRadius = originCircleOval.width() / 2f;\n        separationLinesPaint.setStrokeWidth(sliceSpacing);\n        for (SliceValue sliceValue : data.getValues()) {\n            final float angle = Math.abs(sliceValue.getValue()) * sliceScale;\n\n            sliceVector.set((float) (Math.cos(Math.toRadians(lastAngle))),\n                    (float) (Math.sin(Math.toRadians(lastAngle))));\n            normalizeVector(sliceVector);\n\n            float x1 = sliceVector.x * (circleRadius + touchAdditional) + originCircleOval.centerX();\n            float y1 = sliceVector.y * (circleRadius + touchAdditional) + originCircleOval.centerY();\n\n            canvas.drawLine(originCircleOval.centerX(), originCircleOval.centerY(), x1, y1, separationLinesPaint);\n\n            lastAngle += angle;\n        }\n    }\n\n    public void drawLabels(Canvas canvas) {\n        final PieChartData data = dataProvider.getPieChartData();\n        final float sliceScale = 360f / maxSum;\n        float lastAngle = rotation;\n        int sliceIndex = 0;\n        for (SliceValue sliceValue : data.getValues()) {\n            final float angle = Math.abs(sliceValue.getValue()) * sliceScale;\n            if (isTouched()) {\n                if (hasLabels) {\n                    drawLabel(canvas, sliceValue, lastAngle, angle);\n                } else if (hasLabelsOnlyForSelected && selectedValue.getFirstIndex() == sliceIndex) {\n                    drawLabel(canvas, sliceValue, lastAngle, angle);\n                }\n            } else {\n                if (hasLabels) {\n                    drawLabel(canvas, sliceValue, lastAngle, angle);\n                }\n            }\n            lastAngle += angle;\n            ++sliceIndex;\n        }\n    }\n\n    /**\n     * Method draws single slice from lastAngle to lastAngle+angle, if mode = {@link #MODE_HIGHLIGHT} slice will be\n     * darken\n     * and will have bigger radius.\n     */\n    private void drawSlice(Canvas canvas, SliceValue sliceValue, float lastAngle, float angle, int mode) {\n        sliceVector.set((float) (Math.cos(Math.toRadians(lastAngle + angle / 2))),\n                (float) (Math.sin(Math.toRadians(lastAngle + angle / 2))));\n        normalizeVector(sliceVector);\n        drawCircleOval.set(originCircleOval);\n        if (MODE_HIGHLIGHT == mode) {\n            // Add additional touch feedback by setting bigger radius for that slice and darken color.\n            drawCircleOval.inset(-touchAdditional, -touchAdditional);\n            slicePaint.setColor(sliceValue.getDarkenColor());\n            canvas.drawArc(drawCircleOval, lastAngle, angle, true, slicePaint);\n        } else {\n            slicePaint.setColor(sliceValue.getColor());\n            canvas.drawArc(drawCircleOval, lastAngle, angle, true, slicePaint);\n        }\n    }\n\n    private void drawLabel(Canvas canvas, SliceValue sliceValue, float lastAngle, float angle) {\n        sliceVector.set((float) (Math.cos(Math.toRadians(lastAngle + angle / 2))),\n                (float) (Math.sin(Math.toRadians(lastAngle + angle / 2))));\n        normalizeVector(sliceVector);\n\n        final int numChars = valueFormatter.formatChartValue(labelBuffer, sliceValue);\n\n        if (numChars == 0) {\n            // No need to draw empty label\n            return;\n        }\n\n        final float labelWidth = labelPaint.measureText(labelBuffer, labelBuffer.length - numChars, numChars);\n        final int labelHeight = Math.abs(fontMetrics.ascent);\n\n        final float centerX = originCircleOval.centerX();\n        final float centerY = originCircleOval.centerY();\n        final float circleRadius = originCircleOval.width() / 2f;\n        final float labelRadius;\n\n        if (hasLabelsOutside) {\n            labelRadius = circleRadius * DEFAULT_LABEL_OUTSIDE_RADIUS_FACTOR;\n        } else {\n            if (hasCenterCircle) {\n                labelRadius = circleRadius - (circleRadius - (circleRadius * centerCircleScale)) / 2;\n            } else {\n                labelRadius = circleRadius * DEFAULT_LABEL_INSIDE_RADIUS_FACTOR;\n            }\n        }\n\n        final float rawX = labelRadius * sliceVector.x + centerX;\n        final float rawY = labelRadius * sliceVector.y + centerY;\n\n        float left;\n        float right;\n        float top;\n        float bottom;\n\n        if (hasLabelsOutside) {\n            if (rawX > centerX) {\n                // Right half.\n                left = rawX + labelMargin;\n                right = rawX + labelWidth + labelMargin * 3;\n            } else {\n                left = rawX - labelWidth - labelMargin * 3;\n                right = rawX - labelMargin;\n            }\n\n            if (rawY > centerY) {\n                // Lower half.\n                top = rawY + labelMargin;\n                bottom = rawY + labelHeight + labelMargin * 3;\n            } else {\n                top = rawY - labelHeight - labelMargin * 3;\n                bottom = rawY - labelMargin;\n            }\n        } else {\n            left = rawX - labelWidth / 2 - labelMargin;\n            right = rawX + labelWidth / 2 + labelMargin;\n            top = rawY - labelHeight / 2 - labelMargin;\n            bottom = rawY + labelHeight / 2 + labelMargin;\n        }\n\n        labelBackgroundRect.set(left, top, right, bottom);\n        drawLabelTextAndBackground(canvas, labelBuffer, labelBuffer.length - numChars, numChars,\n                sliceValue.getDarkenColor());\n    }\n\n    private void normalizeVector(PointF point) {\n        final float abs = point.length();\n        point.set(point.x / abs, point.y / abs);\n    }\n\n    /**\n     * Calculates angle of touched point.\n     */\n    private float pointToAngle(float x, float y, float centerX, float centerY) {\n        double diffX = x - centerX;\n        double diffY = y - centerY;\n        // Pass -diffX to get clockwise degrees order.\n        double radian = Math.atan2(-diffX, diffY);\n\n        float angle = ((float) Math.toDegrees(radian) + 360) % 360;\n        // Add 90 because atan2 returns 0 degrees at 6 o'clock.\n        angle += 90f;\n        return angle;\n    }\n\n    /**\n     * Calculates rectangle(square) that will constraint chart circle.\n     */\n    private void calculateCircleOval() {\n        Rect contentRect = computator.getContentRectMinusAllMargins();\n        final float circleRadius = Math.min(contentRect.width() / 2f, contentRect.height() / 2f);\n        final float centerX = contentRect.centerX();\n        final float centerY = contentRect.centerY();\n        final float left = centerX - circleRadius + touchAdditional;\n        final float top = centerY - circleRadius + touchAdditional;\n        final float right = centerX + circleRadius - touchAdditional;\n        final float bottom = centerY + circleRadius - touchAdditional;\n        originCircleOval.set(left, top, right, bottom);\n        final float inest = 0.5f * originCircleOval.width() * (1.0f - circleFillRatio);\n        originCircleOval.inset(inest, inest);\n    }\n\n    /**\n     * Viewport is not really important for PieChart, this kind of chart doesn't relay on viewport but uses pixels\n     * coordinates instead. This method also calculates sum of all SliceValues.\n     */\n    private void calculateMaxViewport() {\n        tempMaximumViewport.set(0, MAX_WIDTH_HEIGHT, MAX_WIDTH_HEIGHT, 0);\n        maxSum = 0.0f;\n        for (SliceValue sliceValue : dataProvider.getPieChartData().getValues()) {\n            maxSum += Math.abs(sliceValue.getValue());\n        }\n    }\n\n    public RectF getCircleOval() {\n        return originCircleOval;\n    }\n\n    public void setCircleOval(RectF orginCircleOval) {\n        this.originCircleOval = orginCircleOval;\n    }\n\n    public int getChartRotation() {\n        return rotation;\n    }\n\n    public void setChartRotation(int rotation) {\n        rotation = (rotation % 360 + 360) % 360;\n        this.rotation = rotation;\n    }\n\n    /**\n     * Returns SliceValue that is under given angle, selectedValue (if not null) will be hold slice index.\n     */\n    public SliceValue getValueForAngle(int angle, SelectedValue selectedValue) {\n        final PieChartData data = dataProvider.getPieChartData();\n        final float touchAngle = (angle - rotation + 360f) % 360f;\n        final float sliceScale = 360f / maxSum;\n        float lastAngle = 0f;\n        int sliceIndex = 0;\n        for (SliceValue sliceValue : data.getValues()) {\n            final float tempAngle = Math.abs(sliceValue.getValue()) * sliceScale;\n            if (touchAngle >= lastAngle) {\n                if (null != selectedValue) {\n                    selectedValue.set(sliceIndex, sliceIndex, SelectedValueType.NONE);\n                }\n                return sliceValue;\n            }\n            lastAngle += tempAngle;\n            ++sliceIndex;\n        }\n        return null;\n    }\n\n    /**\n     * @see #setCircleFillRatio(float)\n     */\n    public float getCircleFillRatio() {\n        return this.circleFillRatio;\n    }\n\n    /**\n     * Set how much of view area should be taken by chart circle. Value should be between 0 and 1. Default is 1 so\n     * circle will have radius equals min(View.width, View.height).\n     */\n    public void setCircleFillRatio(float fillRatio) {\n        if (fillRatio < 0) {\n            fillRatio = 0;\n        } else if (fillRatio > 1) {\n            fillRatio = 1;\n        }\n\n        this.circleFillRatio = fillRatio;\n        calculateCircleOval();\n    }\n\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/renderer/PreviewColumnChartRenderer.java",
    "content": "package lecho.lib.hellocharts.renderer;\n\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Paint;\n\nimport lecho.lib.hellocharts.model.Viewport;\nimport lecho.lib.hellocharts.provider.ColumnChartDataProvider;\nimport lecho.lib.hellocharts.util.ChartUtils;\nimport lecho.lib.hellocharts.view.Chart;\n\n/**\n * Renderer for preview chart based on ColumnChart. In addition to drawing chart data it also draw current viewport as\n * preview area.\n */\npublic class PreviewColumnChartRenderer extends ColumnChartRenderer {\n    private static final int DEFAULT_PREVIEW_TRANSPARENCY = 64;\n    private static final int FULL_ALPHA = 255;\n    private static final int DEFAULT_PREVIEW_STROKE_WIDTH_DP = 2;\n\n    private Paint previewPaint = new Paint();\n\n    public PreviewColumnChartRenderer(Context context, Chart chart, ColumnChartDataProvider dataProvider) {\n        super(context, chart, dataProvider);\n        previewPaint.setAntiAlias(true);\n        previewPaint.setColor(Color.LTGRAY);\n        previewPaint.setStrokeWidth(ChartUtils.dp2px(density, DEFAULT_PREVIEW_STROKE_WIDTH_DP));\n    }\n\n    @Override\n    public void drawUnclipped(Canvas canvas) {\n        super.drawUnclipped(canvas);\n        final Viewport currentViewport = computator.getCurrentViewport();\n        final float left = computator.computeRawX(currentViewport.left);\n        final float top = computator.computeRawY(currentViewport.top);\n        final float right = computator.computeRawX(currentViewport.right);\n        final float bottom = computator.computeRawY(currentViewport.bottom);\n        previewPaint.setAlpha(DEFAULT_PREVIEW_TRANSPARENCY);\n        previewPaint.setStyle(Paint.Style.FILL);\n        canvas.drawRect(left, top, right, bottom, previewPaint);\n        previewPaint.setStyle(Paint.Style.STROKE);\n        previewPaint.setAlpha(FULL_ALPHA);\n        canvas.drawRect(left, top, right, bottom, previewPaint);\n    }\n\n    public int getPreviewColor() {\n        return previewPaint.getColor();\n    }\n\n    public void setPreviewColor(int color) {\n        previewPaint.setColor(color);\n    }\n\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/renderer/PreviewLineChartRenderer.java",
    "content": "package lecho.lib.hellocharts.renderer;\n\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Paint;\n\nimport lecho.lib.hellocharts.model.Viewport;\nimport lecho.lib.hellocharts.provider.LineChartDataProvider;\nimport lecho.lib.hellocharts.util.ChartUtils;\nimport lecho.lib.hellocharts.view.Chart;\n\n/**\n * Renderer for preview chart based on LineChart. In addition to drawing chart data it also draw current viewport as\n * preview area.\n */\npublic class PreviewLineChartRenderer extends LineChartRenderer {\n    private static final int DEFAULT_PREVIEW_TRANSPARENCY = 64;\n    private static final int FULL_ALPHA = 255;\n    private static final int DEFAULT_PREVIEW_STROKE_WIDTH_DP = 2;\n\n    private Paint previewPaint = new Paint();\n\n    public PreviewLineChartRenderer(Context context, Chart chart, LineChartDataProvider dataProvider) {\n        super(context, chart, dataProvider);\n        previewPaint.setAntiAlias(true);\n        previewPaint.setColor(Color.LTGRAY);\n        previewPaint.setStrokeWidth(ChartUtils.dp2px(density, DEFAULT_PREVIEW_STROKE_WIDTH_DP));\n    }\n\n    @Override\n    public void drawUnclipped(Canvas canvas) {\n        super.drawUnclipped(canvas);\n        final Viewport currentViewport = computator.getCurrentViewport();\n        final float left = computator.computeRawX(currentViewport.left);\n        final float top = computator.computeRawY(currentViewport.top);\n        final float right = computator.computeRawX(currentViewport.right);\n        final float bottom = computator.computeRawY(currentViewport.bottom);\n        previewPaint.setAlpha(DEFAULT_PREVIEW_TRANSPARENCY);\n        previewPaint.setStyle(Paint.Style.FILL);\n        canvas.drawRect(left, top, right, bottom, previewPaint);\n        previewPaint.setStyle(Paint.Style.STROKE);\n        previewPaint.setAlpha(FULL_ALPHA);\n        canvas.drawRect(left, top, right, bottom, previewPaint);\n    }\n\n    public int getPreviewColor() {\n        return previewPaint.getColor();\n    }\n\n    public void setPreviewColor(int color) {\n        previewPaint.setColor(color);\n    }\n}"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/util/AxisAutoValues.java",
    "content": "package lecho.lib.hellocharts.util;\n\n/**\n * A simple class representing axis label values used only for auto generated axes.\n */\npublic class AxisAutoValues {\n    public float[] values = new float[]{};\n    public int valuesNumber;\n    public int decimals;\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/util/ChartUtils.java",
    "content": "package lecho.lib.hellocharts.util;\n\nimport android.content.Context;\nimport android.graphics.Color;\nimport android.util.TypedValue;\n\npublic abstract class ChartUtils {\n\n    public static final int DEFAULT_COLOR = Color.parseColor(\"#DFDFDF\");\n    public static final int DEFAULT_DARKEN_COLOR = Color.parseColor(\"#DDDDDD\");\n    public static final int COLOR_BLUE = Color.parseColor(\"#33B5E5\");\n    public static final int COLOR_VIOLET = Color.parseColor(\"#AA66CC\");\n    public static final int COLOR_GREEN = Color.parseColor(\"#99CC00\");\n    public static final int COLOR_ORANGE = Color.parseColor(\"#FFBB33\");\n    public static final int COLOR_RED = Color.parseColor(\"#FF4444\");\n    public static final int[] COLORS = new int[]{COLOR_BLUE, COLOR_VIOLET, COLOR_GREEN, COLOR_ORANGE, COLOR_RED};\n    private static final float DARKEN_SATURATION = 1.1f;\n    private static final float DARKEN_INTENSITY = 0.9f;\n    private static int COLOR_INDEX = 0;\n\n    public static final int pickColor() {\n        return COLORS[(int) Math.round(Math.random() * (COLORS.length - 1))];\n    }\n\n    public static final int nextColor() {\n        if (COLOR_INDEX >= COLORS.length) {\n            COLOR_INDEX = 0;\n        }\n        return COLORS[COLOR_INDEX++];\n    }\n\n    public static int dp2px(float density, int dp) {\n        if (dp == 0) {\n            return 0;\n        }\n        return (int) (dp * density + 0.5f);\n\n    }\n\n    public static int px2dp(float density, int px) {\n        return (int) Math.ceil(px / density);\n    }\n\n    public static int sp2px(float scaledDensity, int sp) {\n        if (sp == 0) {\n            return 0;\n        }\n        return (int) (sp * scaledDensity + 0.5f);\n    }\n\n    public static int px2sp(float scaledDensity, int px) {\n        return (int) Math.ceil(px / scaledDensity);\n    }\n\n    public static int mm2px(Context context, int mm) {\n        return (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, mm, context.getResources()\n                .getDisplayMetrics()) + 0.5f);\n    }\n\n    public static int darkenColor(int color) {\n        float[] hsv = new float[3];\n        int alpha = Color.alpha(color);\n        Color.colorToHSV(color, hsv);\n        hsv[1] = Math.min(hsv[1] * DARKEN_SATURATION, 1.0f);\n        hsv[2] = hsv[2] * DARKEN_INTENSITY;\n        int tempColor = Color.HSVToColor(hsv);\n        return Color.argb(alpha, Color.red(tempColor), Color.green(tempColor), Color.blue(tempColor));\n    }\n\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/util/FloatUtils.java",
    "content": "package lecho.lib.hellocharts.util;\n\npublic class FloatUtils {\n    public static final int POW10[] = {1, 10, 100, 1000, 10000, 100000, 1000000};\n\n    /**\n     * Returns next bigger float value considering precision of the argument.\n     */\n    public static float nextUpF(float f) {\n        if (Float.isNaN(f) || f == Float.POSITIVE_INFINITY) {\n            return f;\n        } else {\n            f += 0.0f;\n            return Float.intBitsToFloat(Float.floatToRawIntBits(f) + ((f >= 0.0f) ? +1 : -1));\n        }\n    }\n\n    /**\n     * Returns next smaller float value considering precision of the argument.\n     */\n    public static float nextDownF(float f) {\n        if (Float.isNaN(f) || f == Float.NEGATIVE_INFINITY) {\n            return f;\n        } else {\n            if (f == 0.0f) {\n                return -Float.MIN_VALUE;\n            } else {\n                return Float.intBitsToFloat(Float.floatToRawIntBits(f) + ((f > 0.0f) ? -1 : +1));\n            }\n        }\n    }\n\n    /**\n     * Returns next bigger double value considering precision of the argument.\n     */\n    public static double nextUp(double d) {\n        if (Double.isNaN(d) || d == Double.POSITIVE_INFINITY) {\n            return d;\n        } else {\n            d += 0.0;\n            return Double.longBitsToDouble(Double.doubleToRawLongBits(d) + ((d >= 0.0) ? +1 : -1));\n        }\n    }\n\n    /**\n     * Returns next smaller float value considering precision of the argument.\n     */\n    public static double nextDown(double d) {\n        if (Double.isNaN(d) || d == Double.NEGATIVE_INFINITY) {\n            return d;\n        } else {\n            if (d == 0.0f) {\n                return -Float.MIN_VALUE;\n            } else {\n                return Double.longBitsToDouble(Double.doubleToRawLongBits(d) + ((d > 0.0f) ? -1 : +1));\n            }\n        }\n    }\n\n    /**\n     * Method checks if two float numbers are similar.\n     */\n    public static boolean almostEqual(float a, float b, float absoluteDiff, float relativeDiff) {\n        float diff = Math.abs(a - b);\n        if (diff <= absoluteDiff) {\n            return true;\n        }\n\n        a = Math.abs(a);\n        b = Math.abs(b);\n        float largest = (a > b) ? a : b;\n\n        if (diff <= largest * relativeDiff) {\n            return true;\n        }\n\n        return false;\n    }\n\n    /**\n     * Rounds the given number to the given number of significant digits. Based on an answer on <a\n     * href=\"http://stackoverflow.com/questions/202302\">Stack Overflow</a>.\n     */\n    public static float roundToOneSignificantFigure(double num) {\n        final float d = (float) Math.ceil((float) Math.log10(num < 0 ? -num : num));\n        final int power = 1 - (int) d;\n        final float magnitude = (float) Math.pow(10, power);\n        final long shifted = Math.round(num * magnitude);\n        return shifted / magnitude;\n    }\n\n    /**\n     * Formats a float value to the given number of decimals. Returns the length of the string. The string begins at\n     * [endIndex] - [return value] and ends at [endIndex]. It's up to you to check indexes correctness.\n     * Parameter [endIndex] can be helpful when you want to append some text to formatted value.\n     *\n     * @return number of characters of formatted value\n     */\n    public static int formatFloat(final char[] formattedValue, float value, int endIndex, int digits, char separator) {\n        if (digits >= POW10.length) {\n            formattedValue[endIndex - 1] = '.';\n            return 1;\n        }\n        boolean negative = false;\n        if (value == 0) {\n            formattedValue[endIndex - 1] = '0';\n            return 1;\n        }\n        if (value < 0) {\n            negative = true;\n            value = -value;\n        }\n        if (digits > POW10.length) {\n            digits = POW10.length - 1;\n        }\n        value *= POW10[digits];\n        long lval = Math.round(value);\n        int index = endIndex - 1;\n        int charsNumber = 0;\n        while (lval != 0 || charsNumber < (digits + 1)) {\n            int digit = (int) (lval % 10);\n            lval = lval / 10;\n            formattedValue[index--] = (char) (digit + '0');\n            charsNumber++;\n            if (charsNumber == digits) {\n                formattedValue[index--] = separator;\n                charsNumber++;\n            }\n        }\n        if (formattedValue[index + 1] == separator) {\n            formattedValue[index--] = '0';\n            charsNumber++;\n        }\n        if (negative) {\n            formattedValue[index--] = '-';\n            charsNumber++;\n        }\n        return charsNumber;\n    }\n\n    /**\n     * Computes the set of axis labels to show given start and stop boundaries and an ideal number of stops between\n     * these boundaries.\n     *\n     * @param start     The minimum extreme (e.g. the left edge) for the axis.\n     * @param stop      The maximum extreme (e.g. the right edge) for the axis.\n     * @param steps     The ideal number of stops to create. This should be based on available screen space; the more\n     *                  space\n     *                  there is, the more stops should be shown.\n     * @param outValues The destination {@link AxisAutoValues} object to populate.\n     */\n    public static void computeAutoGeneratedAxisValues(float start, float stop, int steps, AxisAutoValues outValues) {\n        double range = stop - start;\n        if (steps == 0 || range <= 0) {\n            outValues.values = new float[]{};\n            outValues.valuesNumber = 0;\n            return;\n        }\n\n        double rawInterval = range / steps;\n        double interval = roundToOneSignificantFigure(rawInterval);\n        double intervalMagnitude = Math.pow(10, (int) Math.log10(interval));\n        int intervalSigDigit = (int) (interval / intervalMagnitude);\n        if (intervalSigDigit > 5) {\n            // Use one order of magnitude higher, to avoid intervals like 0.9 or 90\n            interval = Math.floor(10 * intervalMagnitude);\n        }\n\n        double first = Math.ceil(start / interval) * interval;\n        double last = nextUp(Math.floor(stop / interval) * interval);\n\n        double intervalValue;\n        int valueIndex;\n        int valuesNum = 0;\n        for (intervalValue = first; intervalValue <= last; intervalValue += interval) {\n            ++valuesNum;\n        }\n\n        outValues.valuesNumber = valuesNum;\n\n        if (outValues.values.length < valuesNum) {\n            // Ensure stops contains at least numStops elements.\n            outValues.values = new float[valuesNum];\n        }\n\n        for (intervalValue = first, valueIndex = 0; valueIndex < valuesNum; intervalValue += interval, ++valueIndex) {\n            outValues.values[valueIndex] = (float) intervalValue;\n        }\n\n        if (interval < 1) {\n            outValues.decimals = (int) Math.ceil(-Math.log10(interval));\n        } else {\n            outValues.decimals = 0;\n        }\n    }\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/view/AbstractChartView.java",
    "content": "package lecho.lib.hellocharts.view;\n\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.os.Build;\nimport android.support.v4.view.ViewCompat;\nimport android.util.AttributeSet;\nimport android.view.MotionEvent;\nimport android.view.View;\n\nimport lecho.lib.hellocharts.animation.ChartAnimationListener;\nimport lecho.lib.hellocharts.animation.ChartDataAnimator;\nimport lecho.lib.hellocharts.animation.ChartDataAnimatorV14;\nimport lecho.lib.hellocharts.animation.ChartDataAnimatorV8;\nimport lecho.lib.hellocharts.animation.ChartViewportAnimator;\nimport lecho.lib.hellocharts.animation.ChartViewportAnimatorV14;\nimport lecho.lib.hellocharts.animation.ChartViewportAnimatorV8;\nimport lecho.lib.hellocharts.computator.ChartComputator;\nimport lecho.lib.hellocharts.gesture.ChartTouchHandler;\nimport lecho.lib.hellocharts.gesture.ContainerScrollType;\nimport lecho.lib.hellocharts.gesture.ZoomType;\nimport lecho.lib.hellocharts.listener.ViewportChangeListener;\nimport lecho.lib.hellocharts.model.SelectedValue;\nimport lecho.lib.hellocharts.model.Viewport;\nimport lecho.lib.hellocharts.renderer.AxesRenderer;\nimport lecho.lib.hellocharts.renderer.ChartRenderer;\nimport lecho.lib.hellocharts.util.ChartUtils;\n\n/**\n * Abstract class for charts views.\n *\n * @author Leszek Wach\n */\npublic abstract class AbstractChartView extends View implements Chart {\n    protected ChartComputator chartComputator;\n    protected AxesRenderer axesRenderer;\n    protected ChartTouchHandler touchHandler;\n    protected ChartRenderer chartRenderer;\n    protected ChartDataAnimator dataAnimator;\n    protected ChartViewportAnimator viewportAnimator;\n    protected boolean isInteractive = true;\n    protected boolean isContainerScrollEnabled = false;\n    protected ContainerScrollType containerScrollType;\n\n    public AbstractChartView(Context context) {\n        this(context, null, 0);\n    }\n\n    public AbstractChartView(Context context, AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public AbstractChartView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        chartComputator = new ChartComputator();\n        touchHandler = new ChartTouchHandler(context, this);\n        axesRenderer = new AxesRenderer(context, this);\n\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {\n            this.dataAnimator = new ChartDataAnimatorV8(this);\n            this.viewportAnimator = new ChartViewportAnimatorV8(this);\n        } else {\n            this.viewportAnimator = new ChartViewportAnimatorV14(this);\n            this.dataAnimator = new ChartDataAnimatorV14(this);\n        }\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n    }\n\n    @Override\n    protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {\n        super.onSizeChanged(width, height, oldWidth, oldHeight);\n        chartComputator.setContentRect(getWidth(), getHeight(), getPaddingLeft(), getPaddingTop(), getPaddingRight(),\n                getPaddingBottom());\n        chartRenderer.onChartSizeChanged();\n        axesRenderer.onChartSizeChanged();\n    }\n\n    @Override\n    protected void onDraw(Canvas canvas) {\n        super.onDraw(canvas);\n\n        if (isEnabled()) {\n            axesRenderer.drawInBackground(canvas);\n            int clipRestoreCount = canvas.save();\n            canvas.clipRect(chartComputator.getContentRectMinusAllMargins());\n            chartRenderer.draw(canvas);\n            canvas.restoreToCount(clipRestoreCount);\n            chartRenderer.drawUnclipped(canvas);\n            axesRenderer.drawInForeground(canvas);\n        } else {\n            canvas.drawColor(ChartUtils.DEFAULT_COLOR);\n        }\n    }\n\n    @Override\n    public boolean onTouchEvent(MotionEvent event) {\n        super.onTouchEvent(event);\n\n        if (isInteractive) {\n\n            boolean needInvalidate;\n\n            if (isContainerScrollEnabled) {\n                needInvalidate = touchHandler.handleTouchEvent(event, getParent(), containerScrollType);\n            } else {\n                needInvalidate = touchHandler.handleTouchEvent(event);\n            }\n\n            if (needInvalidate) {\n                ViewCompat.postInvalidateOnAnimation(this);\n            }\n\n            return true;\n        } else {\n\n            return false;\n        }\n    }\n\n    @Override\n    public void computeScroll() {\n        super.computeScroll();\n        if (isInteractive) {\n            if (touchHandler.computeScroll()) {\n                ViewCompat.postInvalidateOnAnimation(this);\n            }\n        }\n    }\n\n    @Override\n    public void startDataAnimation() {\n        dataAnimator.startAnimation(Long.MIN_VALUE);\n    }\n\n    @Override\n    public void startDataAnimation(long duration) {\n        dataAnimator.startAnimation(duration);\n    }\n\n    @Override\n    public void cancelDataAnimation() {\n        dataAnimator.cancelAnimation();\n    }\n\n    @Override\n    public void animationDataUpdate(float scale) {\n        getChartData().update(scale);\n        chartRenderer.onChartViewportChanged();\n        ViewCompat.postInvalidateOnAnimation(this);\n    }\n\n    @Override\n    public void animationDataFinished() {\n        getChartData().finish();\n        chartRenderer.onChartViewportChanged();\n        ViewCompat.postInvalidateOnAnimation(this);\n    }\n\n    @Override\n    public void setDataAnimationListener(ChartAnimationListener animationListener) {\n        dataAnimator.setChartAnimationListener(animationListener);\n    }\n\n    @Override\n    public void setViewportAnimationListener(ChartAnimationListener animationListener) {\n        viewportAnimator.setChartAnimationListener(animationListener);\n    }\n\n    @Override\n    public void setViewportChangeListener(ViewportChangeListener viewportChangeListener) {\n        chartComputator.setViewportChangeListener(viewportChangeListener);\n    }\n\n    @Override\n    public ChartRenderer getChartRenderer() {\n        return chartRenderer;\n    }\n\n    @Override\n    public void setChartRenderer(ChartRenderer renderer) {\n        chartRenderer = renderer;\n        resetRendererAndTouchHandler();\n        ViewCompat.postInvalidateOnAnimation(this);\n    }\n\n    @Override\n    public AxesRenderer getAxesRenderer() {\n        return axesRenderer;\n    }\n\n    @Override\n    public ChartComputator getChartComputator() {\n        return chartComputator;\n    }\n\n    @Override\n    public ChartTouchHandler getTouchHandler() {\n        return touchHandler;\n    }\n\n    @Override\n    public boolean isInteractive() {\n        return isInteractive;\n    }\n\n    @Override\n    public void setInteractive(boolean isInteractive) {\n        this.isInteractive = isInteractive;\n    }\n\n    @Override\n    public boolean isZoomEnabled() {\n        return touchHandler.isZoomEnabled();\n    }\n\n    @Override\n    public void setZoomEnabled(boolean isZoomEnabled) {\n        touchHandler.setZoomEnabled(isZoomEnabled);\n    }\n\n    @Override\n    public boolean isScrollEnabled() {\n        return touchHandler.isScrollEnabled();\n    }\n\n    @Override\n    public void setScrollEnabled(boolean isScrollEnabled) {\n        touchHandler.setScrollEnabled(isScrollEnabled);\n    }\n\n    @Override\n    public void moveTo(float x, float y) {\n        Viewport scrollViewport = computeScrollViewport(x, y);\n        setCurrentViewport(scrollViewport);\n    }\n\n    @Override\n    public void moveToWithAnimation(float x, float y) {\n        Viewport scrollViewport = computeScrollViewport(x, y);\n        setCurrentViewportWithAnimation(scrollViewport);\n    }\n\n    private Viewport computeScrollViewport(float x, float y) {\n        Viewport maxViewport = getMaximumViewport();\n        Viewport currentViewport = getCurrentViewport();\n        Viewport scrollViewport = new Viewport(currentViewport);\n\n        if (maxViewport.contains(x, y)) {\n            final float width = currentViewport.width();\n            final float height = currentViewport.height();\n\n            final float halfWidth = width / 2;\n            final float halfHeight = height / 2;\n\n            float left = x - halfWidth;\n            float top = y + halfHeight;\n\n            left = Math.max(maxViewport.left, Math.min(left, maxViewport.right - width));\n            top = Math.max(maxViewport.bottom + height, Math.min(top, maxViewport.top));\n\n            scrollViewport.set(left, top, left + width, top - height);\n        }\n\n        return scrollViewport;\n    }\n\n    @Override\n    public boolean isValueTouchEnabled() {\n        return touchHandler.isValueTouchEnabled();\n    }\n\n    @Override\n    public void setValueTouchEnabled(boolean isValueTouchEnabled) {\n        touchHandler.setValueTouchEnabled(isValueTouchEnabled);\n\n    }\n\n    @Override\n    public ZoomType getZoomType() {\n        return touchHandler.getZoomType();\n    }\n\n    @Override\n    public void setZoomType(ZoomType zoomType) {\n        touchHandler.setZoomType(zoomType);\n    }\n\n    @Override\n    public float getMaxZoom() {\n        return chartComputator.getMaxZoom();\n    }\n\n    @Override\n    public void setMaxZoom(float maxZoom) {\n        chartComputator.setMaxZoom(maxZoom);\n        ViewCompat.postInvalidateOnAnimation(this);\n    }\n\n    @Override\n    public float getZoomLevel() {\n        Viewport maxViewport = getMaximumViewport();\n        Viewport currentViewport = getCurrentViewport();\n\n        return Math.max(maxViewport.width() / currentViewport.width(), maxViewport.height() / currentViewport.height());\n\n    }\n\n    @Override\n    public void setZoomLevel(float x, float y, float zoomLevel) {\n        Viewport zoomViewport = computeZoomViewport(x, y, zoomLevel);\n        setCurrentViewport(zoomViewport);\n    }\n\n    @Override\n    public void setZoomLevelWithAnimation(float x, float y, float zoomLevel) {\n        Viewport zoomViewport = computeZoomViewport(x, y, zoomLevel);\n        setCurrentViewportWithAnimation(zoomViewport);\n    }\n\n    private Viewport computeZoomViewport(float x, float y, float zoomLevel) {\n        final Viewport maxViewport = getMaximumViewport();\n        Viewport zoomViewport = new Viewport(getMaximumViewport());\n\n        if (maxViewport.contains(x, y)) {\n\n            if (zoomLevel < 1) {\n                zoomLevel = 1;\n            } else if (zoomLevel > getMaxZoom()) {\n                zoomLevel = getMaxZoom();\n            }\n\n            final float newWidth = zoomViewport.width() / zoomLevel;\n            final float newHeight = zoomViewport.height() / zoomLevel;\n\n            final float halfWidth = newWidth / 2;\n            final float halfHeight = newHeight / 2;\n\n            float left = x - halfWidth;\n            float right = x + halfWidth;\n            float top = y + halfHeight;\n            float bottom = y - halfHeight;\n\n            if (left < maxViewport.left) {\n                left = maxViewport.left;\n                right = left + newWidth;\n            } else if (right > maxViewport.right) {\n                right = maxViewport.right;\n                left = right - newWidth;\n            }\n\n            if (top > maxViewport.top) {\n                top = maxViewport.top;\n                bottom = top - newHeight;\n            } else if (bottom < maxViewport.bottom) {\n                bottom = maxViewport.bottom;\n                top = bottom + newHeight;\n            }\n\n            ZoomType zoomType = getZoomType();\n            if (ZoomType.HORIZONTAL_AND_VERTICAL == zoomType) {\n                zoomViewport.set(left, top, right, bottom);\n            } else if (ZoomType.HORIZONTAL == zoomType) {\n                zoomViewport.left = left;\n                zoomViewport.right = right;\n            } else if (ZoomType.VERTICAL == zoomType) {\n                zoomViewport.top = top;\n                zoomViewport.bottom = bottom;\n            }\n\n        }\n        return zoomViewport;\n    }\n\n    @Override\n    public Viewport getMaximumViewport() {\n        return chartRenderer.getMaximumViewport();\n    }\n\n    @Override\n    public void setMaximumViewport(Viewport maxViewport) {\n        chartRenderer.setMaximumViewport(maxViewport);\n        ViewCompat.postInvalidateOnAnimation(this);\n    }\n\n    @Override\n    public void setCurrentViewportWithAnimation(Viewport targetViewport) {\n        if (null != targetViewport) {\n            viewportAnimator.cancelAnimation();\n            viewportAnimator.startAnimation(getCurrentViewport(), targetViewport);\n        }\n        ViewCompat.postInvalidateOnAnimation(this);\n    }\n\n    @Override\n    public void setCurrentViewportWithAnimation(Viewport targetViewport, long duration) {\n        if (null != targetViewport) {\n            viewportAnimator.cancelAnimation();\n            viewportAnimator.startAnimation(getCurrentViewport(), targetViewport, duration);\n        }\n        ViewCompat.postInvalidateOnAnimation(this);\n    }\n\n    @Override\n    public Viewport getCurrentViewport() {\n        return getChartRenderer().getCurrentViewport();\n    }\n\n    @Override\n    public void setCurrentViewport(Viewport targetViewport) {\n        if (null != targetViewport) {\n            chartRenderer.setCurrentViewport(targetViewport);\n        }\n        ViewCompat.postInvalidateOnAnimation(this);\n    }\n\n    @Override\n    public void resetViewports() {\n        chartRenderer.setMaximumViewport(null);\n        chartRenderer.setCurrentViewport(null);\n    }\n\n    @Override\n    public boolean isViewportCalculationEnabled() {\n        return chartRenderer.isViewportCalculationEnabled();\n    }\n\n    @Override\n    public void setViewportCalculationEnabled(boolean isEnabled) {\n        chartRenderer.setViewportCalculationEnabled(isEnabled);\n    }\n\n    @Override\n    public boolean isValueSelectionEnabled() {\n        return touchHandler.isValueSelectionEnabled();\n    }\n\n    @Override\n    public void setValueSelectionEnabled(boolean isValueSelectionEnabled) {\n        touchHandler.setValueSelectionEnabled(isValueSelectionEnabled);\n    }\n\n    @Override\n    public void selectValue(SelectedValue selectedValue) {\n        chartRenderer.selectValue(selectedValue);\n        callTouchListener();\n        ViewCompat.postInvalidateOnAnimation(this);\n    }\n\n    @Override\n    public SelectedValue getSelectedValue() {\n        return chartRenderer.getSelectedValue();\n    }\n\n    @Override\n    public boolean isContainerScrollEnabled() {\n        return isContainerScrollEnabled;\n    }\n\n    @Override\n    public void setContainerScrollEnabled(boolean isContainerScrollEnabled, ContainerScrollType containerScrollType) {\n        this.isContainerScrollEnabled = isContainerScrollEnabled;\n        this.containerScrollType = containerScrollType;\n    }\n\n    protected void onChartDataChange() {\n        chartComputator.resetContentRect();\n        chartRenderer.onChartDataChanged();\n        axesRenderer.onChartDataChanged();\n        ViewCompat.postInvalidateOnAnimation(this);\n    }\n\n    /**\n     * You should call this method in derived classes, most likely from constructor if you changed chart/axis renderer,\n     * touch handler or chart computator\n     */\n    protected void resetRendererAndTouchHandler() {\n        this.chartRenderer.resetRenderer();\n        this.axesRenderer.resetRenderer();\n        this.touchHandler.resetTouchHandler();\n    }\n\n    /**\n     * When embedded in a ViewPager, this will be called in order to know if we can scroll.\n     * If this returns true, the ViewPager will ignore the drag so that we can scroll our content.\n     * If this return false, the ViewPager will assume we won't be able to scroll and will consume the drag\n     *\n     * @param direction Amount of pixels being scrolled (x axis)\n     * @return true if the chart can be scrolled (ie. zoomed and not against the edge of the chart)\n     */\n    @Override\n    public boolean canScrollHorizontally(int direction) {\n        if (getZoomLevel() <= 1.0) {\n            return false;\n        }\n        final Viewport currentViewport = getCurrentViewport();\n        final Viewport maximumViewport = getMaximumViewport();\n        if (direction < 0) {\n            return currentViewport.left > maximumViewport.left;\n        } else {\n            return currentViewport.right < maximumViewport.right;\n        }\n    }\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/view/BubbleChartView.java",
    "content": "package lecho.lib.hellocharts.view;\n\nimport android.content.Context;\nimport android.support.v4.view.ViewCompat;\nimport android.util.AttributeSet;\nimport android.util.Log;\n\nimport lecho.lib.hellocharts.BuildConfig;\nimport lecho.lib.hellocharts.listener.BubbleChartOnValueSelectListener;\nimport lecho.lib.hellocharts.listener.DummyBubbleChartOnValueSelectListener;\nimport lecho.lib.hellocharts.model.BubbleChartData;\nimport lecho.lib.hellocharts.model.BubbleValue;\nimport lecho.lib.hellocharts.model.ChartData;\nimport lecho.lib.hellocharts.model.SelectedValue;\nimport lecho.lib.hellocharts.provider.BubbleChartDataProvider;\nimport lecho.lib.hellocharts.renderer.BubbleChartRenderer;\n\n/**\n * BubbleChart, supports circle bubbles and square bubbles.\n *\n * @author lecho\n */\npublic class BubbleChartView extends AbstractChartView implements BubbleChartDataProvider {\n    private static final String TAG = \"BubbleChartView\";\n    protected BubbleChartData data;\n    protected BubbleChartOnValueSelectListener onValueTouchListener = new DummyBubbleChartOnValueSelectListener();\n\n    protected BubbleChartRenderer bubbleChartRenderer;\n\n    public BubbleChartView(Context context) {\n        this(context, null, 0);\n    }\n\n    public BubbleChartView(Context context, AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public BubbleChartView(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n        bubbleChartRenderer = new BubbleChartRenderer(context, this, this);\n        setChartRenderer(bubbleChartRenderer);\n        setBubbleChartData(BubbleChartData.generateDummyData());\n    }\n\n    @Override\n    public BubbleChartData getBubbleChartData() {\n        return data;\n    }\n\n    @Override\n    public void setBubbleChartData(BubbleChartData data) {\n        if (BuildConfig.DEBUG) {\n            Log.d(TAG, \"Setting data for BubbleChartView\");\n        }\n\n        if (null == data) {\n            this.data = BubbleChartData.generateDummyData();\n        } else {\n            this.data = data;\n        }\n\n        super.onChartDataChange();\n    }\n\n    @Override\n    public ChartData getChartData() {\n        return data;\n    }\n\n    @Override\n    public void callTouchListener() {\n        SelectedValue selectedValue = chartRenderer.getSelectedValue();\n\n        if (selectedValue.isSet()) {\n            BubbleValue value = data.getValues().get(selectedValue.getFirstIndex());\n            onValueTouchListener.onValueSelected(selectedValue.getFirstIndex(), value);\n        } else {\n            onValueTouchListener.onValueDeselected();\n        }\n    }\n\n    public BubbleChartOnValueSelectListener getOnValueTouchListener() {\n        return onValueTouchListener;\n    }\n\n    public void setOnValueTouchListener(BubbleChartOnValueSelectListener touchListener) {\n        if (null != touchListener) {\n            this.onValueTouchListener = touchListener;\n        }\n    }\n\n    /**\n     * Removes empty spaces, top-bottom for portrait orientation and left-right for landscape. This method has to be\n     * called after view View#onSizeChanged() method is called and chart data is set. This method may be inaccurate.\n     *\n     * @see BubbleChartRenderer#removeMargins()\n     */\n    public void removeMargins() {\n        bubbleChartRenderer.removeMargins();\n        ViewCompat.postInvalidateOnAnimation(this);\n    }\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/view/Chart.java",
    "content": "package lecho.lib.hellocharts.view;\n\nimport lecho.lib.hellocharts.animation.ChartAnimationListener;\nimport lecho.lib.hellocharts.computator.ChartComputator;\nimport lecho.lib.hellocharts.gesture.ChartTouchHandler;\nimport lecho.lib.hellocharts.gesture.ContainerScrollType;\nimport lecho.lib.hellocharts.gesture.ZoomType;\nimport lecho.lib.hellocharts.listener.ViewportChangeListener;\nimport lecho.lib.hellocharts.model.ChartData;\nimport lecho.lib.hellocharts.model.SelectedValue;\nimport lecho.lib.hellocharts.model.Viewport;\nimport lecho.lib.hellocharts.renderer.AxesRenderer;\nimport lecho.lib.hellocharts.renderer.ChartRenderer;\n\n/**\n * Interface for all charts. Every chart must implements this interface but chart doesn't really have to extends View or\n * ViewGroup class. It can be any java class for example chart that only draw on in-memory bitmap and saves it on sd\n * card.\n */\npublic interface Chart {\n\n    /**\n     * Returns generic chart data. For specific class call get*ChartData method from data provider implementation.\n     */\n    public ChartData getChartData();\n\n    public ChartRenderer getChartRenderer();\n\n    public void setChartRenderer(ChartRenderer renderer);\n\n    public AxesRenderer getAxesRenderer();\n\n    public ChartComputator getChartComputator();\n\n    public ChartTouchHandler getTouchHandler();\n\n    /**\n     * Updates chart data with given scale. Called during chart data animation update.\n     */\n    public void animationDataUpdate(float scale);\n\n    /**\n     * Called when data animation finished.\n     */\n    public void animationDataFinished();\n\n    /**\n     * Starts chart data animation for given duration. Before you call this method you should change target values of\n     * chart data.\n     */\n    public void startDataAnimation();\n\n    /**\n     * Starts chart data animation for given duration. If duration is negative the default value of 500ms will be used.\n     * Before you call this method you should change target values of chart data.\n     */\n    public void startDataAnimation(long duration);\n\n    /**\n     * Stops chart data animation. All chart data values are set to their target values.\n     */\n    public void cancelDataAnimation();\n\n    /**\n     * Return true if auto viewports recalculations are enabled, false otherwise.\n     */\n    public boolean isViewportCalculationEnabled();\n\n    /**\n     * Set true to enable viewports(max and current) recalculations during animations or after set*ChartData method is\n     * called. If you disable viewports calculations viewports will not change until you change them manually or enable\n     * calculations again. Disabled viewport calculations is usefull if you want show only part of chart by setting\n     * custom viewport and don't want any operation to change that viewport\n     */\n    public void setViewportCalculationEnabled(boolean isEnabled);\n\n    /**\n     * Set listener for data animation to be notified when data animation started and finished. By default that flag is\n     * set to true so be careful with animation and custom viewports.\n     */\n    public void setDataAnimationListener(ChartAnimationListener animationListener);\n\n    /**\n     * Set listener for viewport animation to be notified when viewport animation started and finished.\n     */\n    public void setViewportAnimationListener(ChartAnimationListener animationListener);\n\n    /**\n     * Set listener for current viewport changes. It will be called when viewport change either by gesture or\n     * programmatically. Note! This method works only for preview charts. It is intentionally disabled for other types\n     * of charts to avoid unnecessary method calls during invalidation.\n     */\n    public void setViewportChangeListener(ViewportChangeListener viewportChangeListener);\n\n    public void callTouchListener();\n\n    /**\n     * Returns true if chart is interactive.\n     *\n     * @see #setInteractive(boolean)\n     */\n    public boolean isInteractive();\n\n    /**\n     * Set true to allow user use touch gestures. If set to false user will not be able zoom, scroll or select/touch\n     * value. By default true.\n     */\n    public void setInteractive(boolean isInteractive);\n\n    /**\n     * Returns true if pitch to zoom and double tap zoom is enabled.\n     *\n     * @see #setZoomEnabled(boolean)\n     */\n    public boolean isZoomEnabled();\n\n    /**\n     * Set true to enable zoom, false to disable, by default true;\n     */\n    public void setZoomEnabled(boolean isZoomEnabled);\n\n    /**\n     * Returns true if scrolling is enabled.\n     *\n     * @see #setScrollEnabled(boolean)\n     */\n    public boolean isScrollEnabled();\n\n    /**\n     * Set true to enable touch scroll/fling, false to disable touch scroll/fling, by default true;\n     */\n    public void setScrollEnabled(boolean isScrollEnabled);\n\n    /**\n     * Move/Srcoll viewport to position x,y(that position must be within maximum chart viewport). If possible viewport\n     * will be centered at this point. Width and height of viewport will not be modified.\n     *\n     * @see #setCurrentViewport(lecho.lib.hellocharts.model.Viewport)\n     */\n    public void moveTo(float x, float y);\n\n    /**\n     * Animate viewport to position x,y(that position must be within maximum chart viewport). If possible viewport\n     * will be centered at this point. Width and height of viewport will not be modified.\n     *\n     * @see #setCurrentViewport(lecho.lib.hellocharts.model.Viewport) ;\n     */\n    public void moveToWithAnimation(float x, float y);\n\n    /**\n     * Returns current zoom type for this chart.\n     *\n     * @see #setZoomType(ZoomType)\n     */\n    public ZoomType getZoomType();\n\n    /**\n     * Set zoom type, available options: ZoomType.HORIZONTAL_AND_VERTICAL, ZoomType.HORIZONTAL, ZoomType.VERTICAL. By\n     * default HORIZONTAL_AND_VERTICAL.\n     */\n    public void setZoomType(ZoomType zoomType);\n\n    /**\n     * Returns current maximum zoom value.\n     */\n    public float getMaxZoom();\n\n    /**\n     * Set max zoom value. Default maximum zoom is 20.\n     */\n    public void setMaxZoom(float maxZoom);\n\n    /**\n     * Returns current zoom level.\n     */\n    public float getZoomLevel();\n\n    /**\n     * Programatically zoom chart to given point(viewport point). Call this method after chart data had been set.\n     *\n     * @param x         x within chart maximum viewport\n     * @param y         y within chart maximum viewport\n     * @param zoomLevel value from 1 to maxZoom(default 20). 1 means chart has no zoom.\n     */\n    public void setZoomLevel(float x, float y, float zoomLevel);\n\n    /**\n     * Programatically zoom chart to given point(viewport point) with animation. Call this method after chart data\n     * had been set.\n     *\n     * @param x         x within chart maximum viewport\n     * @param y         y within chart maximum viewport\n     * @param zoomLevel value from 1 to maxZoom(default 20). 1 means chart has no zoom.\n     */\n    public void setZoomLevelWithAnimation(float x, float y, float zoomLevel);\n\n    /**\n     * Return true if chart value can be touched.\n     *\n     * @see #setValueTouchEnabled(boolean)\n     */\n    public boolean isValueTouchEnabled();\n\n    /**\n     * Set true if you want allow user to click value on chart, set false to disable that option. By default true.\n     */\n    public void setValueTouchEnabled(boolean isValueTouchEnabled);\n\n    /**\n     * Returns maximum viewport for this chart. Don't modify it directly, use {@link #setMaximumViewport(Viewport)}\n     * instead.\n     *\n     * @see #setMaximumViewport(Viewport)\n     */\n    public Viewport getMaximumViewport();\n\n    /**\n     * Set maximum viewport. If you set bigger maximum viewport data will be more concentrate and there will be more\n     * empty spaces on sides. Note. MaxViewport have to be set after chartData has been set.\n     */\n    public void setMaximumViewport(Viewport maxViewport);\n\n    /**\n     * Returns current viewport. Don't modify it directly, use {@link #setCurrentViewport(Viewport)} instead.\n     *\n     * @see #setCurrentViewport(Viewport)\n     */\n    public Viewport getCurrentViewport();\n\n    /**\n     * Sets current viewport. Note. viewport have to be set after chartData has been set.\n     */\n    public void setCurrentViewport(Viewport targetViewport);\n\n    /**\n     * Sets current viewport with animation. Note. viewport have to be set after chartData has been set.\n     */\n    public void setCurrentViewportWithAnimation(Viewport targetViewport);\n\n    /**\n     * Sets current viewport with animation. Note. viewport have to be set after chartData has been set.\n     */\n    public void setCurrentViewportWithAnimation(Viewport targetViewport, long duration);\n\n    /**\n     * Reset maximum viewport and current viewport. Values for both viewports will be auto-calculated using current\n     * chart data ranges.\n     */\n    public void resetViewports();\n\n    /**\n     * Return true if value selection mode is enabled.\n     *\n     * @see #setValueSelectionEnabled(boolean)\n     */\n    public boolean isValueSelectionEnabled();\n\n    /**\n     * Set true if you want value selection with touch - value will stay selected until you touch somewhere else on the\n     * chart area. By default false and value is automatically unselected when user stop pressing on it.\n     */\n    public void setValueSelectionEnabled(boolean isValueSelectionEnabled);\n\n    /**\n     * Select single value on chart. If indexes are not valid IndexOutOfBoundsException will be thrown.\n     */\n    public void selectValue(SelectedValue selectedValue);\n\n    /**\n     * Return currently selected value indexes.\n     */\n    public SelectedValue getSelectedValue();\n\n    /**\n     * @see #setContainerScrollEnabled(boolean, ContainerScrollType)\n     */\n    public boolean isContainerScrollEnabled();\n\n    /**\n     * Set isContainerScrollEnabled to true and containerScrollType to HORIZONTAL or VERTICAL if you are using chart\n     * within scroll container.\n     */\n    public void setContainerScrollEnabled(boolean isContainerScrollEnabled, ContainerScrollType containerScrollType);\n\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/view/ColumnChartView.java",
    "content": "package lecho.lib.hellocharts.view;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.util.Log;\n\nimport lecho.lib.hellocharts.BuildConfig;\nimport lecho.lib.hellocharts.listener.ColumnChartOnValueSelectListener;\nimport lecho.lib.hellocharts.listener.DummyColumnChartOnValueSelectListener;\nimport lecho.lib.hellocharts.model.ColumnChartData;\nimport lecho.lib.hellocharts.model.SelectedValue;\nimport lecho.lib.hellocharts.model.SubcolumnValue;\nimport lecho.lib.hellocharts.provider.ColumnChartDataProvider;\nimport lecho.lib.hellocharts.renderer.ColumnChartRenderer;\n\n/**\n * ColumnChart/BarChart, supports subcolumns, stacked collumns and negative values.\n *\n * @author Leszek Wach\n */\npublic class ColumnChartView extends AbstractChartView implements ColumnChartDataProvider {\n    private static final String TAG = \"ColumnChartView\";\n    private ColumnChartData data;\n    private ColumnChartOnValueSelectListener onValueTouchListener = new DummyColumnChartOnValueSelectListener();\n\n    public ColumnChartView(Context context) {\n        this(context, null, 0);\n    }\n\n    public ColumnChartView(Context context, AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public ColumnChartView(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n        setChartRenderer(new ColumnChartRenderer(context, this, this));\n        setColumnChartData(ColumnChartData.generateDummyData());\n    }\n\n    @Override\n    public ColumnChartData getColumnChartData() {\n        return data;\n    }\n\n    @Override\n    public void setColumnChartData(ColumnChartData data) {\n        if (BuildConfig.DEBUG) {\n            Log.d(TAG, \"Setting data for ColumnChartView\");\n        }\n\n        if (null == data) {\n            this.data = ColumnChartData.generateDummyData();\n        } else {\n            this.data = data;\n        }\n\n        super.onChartDataChange();\n\n    }\n\n    @Override\n    public ColumnChartData getChartData() {\n        return data;\n    }\n\n    @Override\n    public void callTouchListener() {\n        SelectedValue selectedValue = chartRenderer.getSelectedValue();\n\n        if (selectedValue.isSet()) {\n            SubcolumnValue value = data.getColumns().get(selectedValue.getFirstIndex()).getValues()\n                    .get(selectedValue.getSecondIndex());\n            onValueTouchListener.onValueSelected(selectedValue.getFirstIndex(), selectedValue.getSecondIndex(), value);\n        } else {\n            onValueTouchListener.onValueDeselected();\n        }\n    }\n\n    public ColumnChartOnValueSelectListener getOnValueTouchListener() {\n        return onValueTouchListener;\n    }\n\n    public void setOnValueTouchListener(ColumnChartOnValueSelectListener touchListener) {\n        if (null != touchListener) {\n            this.onValueTouchListener = touchListener;\n        }\n    }\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/view/ComboLineColumnChartView.java",
    "content": "package lecho.lib.hellocharts.view;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.util.Log;\n\nimport lecho.lib.hellocharts.BuildConfig;\nimport lecho.lib.hellocharts.listener.ComboLineColumnChartOnValueSelectListener;\nimport lecho.lib.hellocharts.listener.DummyCompoLineColumnChartOnValueSelectListener;\nimport lecho.lib.hellocharts.model.ChartData;\nimport lecho.lib.hellocharts.model.ColumnChartData;\nimport lecho.lib.hellocharts.model.ComboLineColumnChartData;\nimport lecho.lib.hellocharts.model.LineChartData;\nimport lecho.lib.hellocharts.model.PointValue;\nimport lecho.lib.hellocharts.model.SelectedValue;\nimport lecho.lib.hellocharts.model.SelectedValue.SelectedValueType;\nimport lecho.lib.hellocharts.model.SubcolumnValue;\nimport lecho.lib.hellocharts.provider.ColumnChartDataProvider;\nimport lecho.lib.hellocharts.provider.ComboLineColumnChartDataProvider;\nimport lecho.lib.hellocharts.provider.LineChartDataProvider;\nimport lecho.lib.hellocharts.renderer.ColumnChartRenderer;\nimport lecho.lib.hellocharts.renderer.ComboLineColumnChartRenderer;\nimport lecho.lib.hellocharts.renderer.LineChartRenderer;\n\n/**\n * ComboChart, supports ColumnChart combined with LineChart. Lines are always drawn on top.\n *\n * @author Leszek Wach\n */\npublic class ComboLineColumnChartView extends AbstractChartView implements ComboLineColumnChartDataProvider {\n    private static final String TAG = \"ComboLineColumnChartView\";\n    protected ComboLineColumnChartData data;\n    protected ColumnChartDataProvider columnChartDataProvider = new ComboColumnChartDataProvider();\n    protected LineChartDataProvider lineChartDataProvider = new ComboLineChartDataProvider();\n    protected ComboLineColumnChartOnValueSelectListener onValueTouchListener = new\n            DummyCompoLineColumnChartOnValueSelectListener();\n\n    public ComboLineColumnChartView(Context context) {\n        this(context, null, 0);\n    }\n\n    public ComboLineColumnChartView(Context context, AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public ComboLineColumnChartView(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n        setChartRenderer(new ComboLineColumnChartRenderer(context, this, columnChartDataProvider,\n                lineChartDataProvider));\n        setComboLineColumnChartData(ComboLineColumnChartData.generateDummyData());\n    }\n\n    @Override\n    public ComboLineColumnChartData getComboLineColumnChartData() {\n        return data;\n    }\n\n    @Override\n    public void setComboLineColumnChartData(ComboLineColumnChartData data) {\n        if (BuildConfig.DEBUG) {\n            Log.d(TAG, \"Setting data for ComboLineColumnChartView\");\n        }\n\n        if (null == data) {\n            this.data = null;// generateDummyData();\n        } else {\n            this.data = data;\n        }\n\n        super.onChartDataChange();\n    }\n\n    @Override\n    public ChartData getChartData() {\n        return data;\n    }\n\n    @Override\n    public void callTouchListener() {\n        SelectedValue selectedValue = chartRenderer.getSelectedValue();\n\n        if (selectedValue.isSet()) {\n\n            if (SelectedValueType.COLUMN.equals(selectedValue.getType())) {\n\n                SubcolumnValue value = data.getColumnChartData().getColumns().get(selectedValue.getFirstIndex())\n                        .getValues().get(selectedValue.getSecondIndex());\n                onValueTouchListener.onColumnValueSelected(selectedValue.getFirstIndex(),\n                        selectedValue.getSecondIndex(), value);\n\n            } else if (SelectedValueType.LINE.equals(selectedValue.getType())) {\n\n                PointValue value = data.getLineChartData().getLines().get(selectedValue.getFirstIndex()).getValues()\n                        .get(selectedValue.getSecondIndex());\n                onValueTouchListener.onPointValueSelected(selectedValue.getFirstIndex(), selectedValue.getSecondIndex(),\n                        value);\n\n            } else {\n                throw new IllegalArgumentException(\"Invalid selected value type \" + selectedValue.getType().name());\n            }\n        } else {\n            onValueTouchListener.onValueDeselected();\n        }\n    }\n\n    public ComboLineColumnChartOnValueSelectListener getOnValueTouchListener() {\n        return onValueTouchListener;\n    }\n\n    public void setOnValueTouchListener(ComboLineColumnChartOnValueSelectListener touchListener) {\n        if (null != touchListener) {\n            this.onValueTouchListener = touchListener;\n        }\n    }\n\n    public void setColumnChartRenderer(Context context, ColumnChartRenderer columnChartRenderer){\n        setChartRenderer(new ComboLineColumnChartRenderer(context, this , columnChartRenderer, lineChartDataProvider));\n    }\n\n    public void setLineChartRenderer(Context context, LineChartRenderer lineChartRenderer){\n        setChartRenderer(new ComboLineColumnChartRenderer(context, this, columnChartDataProvider, lineChartRenderer));\n    }\n\n    private class ComboLineChartDataProvider implements LineChartDataProvider {\n\n        @Override\n        public LineChartData getLineChartData() {\n            return ComboLineColumnChartView.this.data.getLineChartData();\n        }\n\n        @Override\n        public void setLineChartData(LineChartData data) {\n            ComboLineColumnChartView.this.data.setLineChartData(data);\n\n        }\n\n    }\n\n    private class ComboColumnChartDataProvider implements ColumnChartDataProvider {\n\n        @Override\n        public ColumnChartData getColumnChartData() {\n            return ComboLineColumnChartView.this.data.getColumnChartData();\n        }\n\n        @Override\n        public void setColumnChartData(ColumnChartData data) {\n            ComboLineColumnChartView.this.data.setColumnChartData(data);\n\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/view/LineChartView.java",
    "content": "package lecho.lib.hellocharts.view;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.util.Log;\n\nimport lecho.lib.hellocharts.BuildConfig;\nimport lecho.lib.hellocharts.listener.DummyLineChartOnValueSelectListener;\nimport lecho.lib.hellocharts.listener.LineChartOnValueSelectListener;\nimport lecho.lib.hellocharts.model.ChartData;\nimport lecho.lib.hellocharts.model.LineChartData;\nimport lecho.lib.hellocharts.model.PointValue;\nimport lecho.lib.hellocharts.model.SelectedValue;\nimport lecho.lib.hellocharts.provider.LineChartDataProvider;\nimport lecho.lib.hellocharts.renderer.LineChartRenderer;\n\n/**\n * LineChart, supports cubic lines, filled lines, circle and square points. Point radius and stroke width can be\n * adjusted using LineChartData attributes.\n *\n * @author Leszek Wach\n */\npublic class LineChartView extends AbstractChartView implements LineChartDataProvider {\n    private static final String TAG = \"LineChartView\";\n    protected LineChartData data;\n    protected LineChartOnValueSelectListener onValueTouchListener = new DummyLineChartOnValueSelectListener();\n\n    public LineChartView(Context context) {\n        this(context, null, 0);\n    }\n\n    public LineChartView(Context context, AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public LineChartView(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n        setChartRenderer(new LineChartRenderer(context, this, this));\n        setLineChartData(LineChartData.generateDummyData());\n    }\n\n    @Override\n    public LineChartData getLineChartData() {\n        return data;\n    }\n\n    @Override\n    public void setLineChartData(LineChartData data) {\n        if (BuildConfig.DEBUG) {\n            Log.d(TAG, \"Setting data for LineChartView\");\n        }\n\n        if (null == data) {\n            this.data = LineChartData.generateDummyData();\n        } else {\n            this.data = data;\n        }\n\n        super.onChartDataChange();\n    }\n\n    @Override\n    public ChartData getChartData() {\n        return data;\n    }\n\n    @Override\n    public void callTouchListener() {\n        SelectedValue selectedValue = chartRenderer.getSelectedValue();\n\n        if (selectedValue.isSet()) {\n            PointValue point = data.getLines().get(selectedValue.getFirstIndex()).getValues()\n                    .get(selectedValue.getSecondIndex());\n            onValueTouchListener.onValueSelected(selectedValue.getFirstIndex(), selectedValue.getSecondIndex(), point);\n        } else {\n            onValueTouchListener.onValueDeselected();\n        }\n    }\n\n    public LineChartOnValueSelectListener getOnValueTouchListener() {\n        return onValueTouchListener;\n    }\n\n    public void setOnValueTouchListener(LineChartOnValueSelectListener touchListener) {\n        if (null != touchListener) {\n            this.onValueTouchListener = touchListener;\n        }\n    }\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/view/PieChartView.java",
    "content": "package lecho.lib.hellocharts.view;\n\nimport android.content.Context;\nimport android.graphics.RectF;\nimport android.os.Build;\nimport android.support.v4.view.ViewCompat;\nimport android.util.AttributeSet;\nimport android.util.Log;\nimport android.view.View;\n\nimport lecho.lib.hellocharts.BuildConfig;\nimport lecho.lib.hellocharts.animation.PieChartRotationAnimator;\nimport lecho.lib.hellocharts.animation.PieChartRotationAnimatorV14;\nimport lecho.lib.hellocharts.animation.PieChartRotationAnimatorV8;\nimport lecho.lib.hellocharts.gesture.PieChartTouchHandler;\nimport lecho.lib.hellocharts.listener.DummyPieChartOnValueSelectListener;\nimport lecho.lib.hellocharts.listener.PieChartOnValueSelectListener;\nimport lecho.lib.hellocharts.model.ChartData;\nimport lecho.lib.hellocharts.model.PieChartData;\nimport lecho.lib.hellocharts.model.SelectedValue;\nimport lecho.lib.hellocharts.model.SliceValue;\nimport lecho.lib.hellocharts.provider.PieChartDataProvider;\nimport lecho.lib.hellocharts.renderer.PieChartRenderer;\n\n/**\n * PieChart is a little different than others charts. It doesn't have axes. It doesn't support viewport so changing\n * viewport wont work. Instead it support \"Circle Oval\". Pinch-to-Zoom and double tap zoom wont work either. Instead of\n * scroll there is chart rotation if isChartRotationEnabled is set to true. PieChart looks the best when it has the same\n * width and height, drawing chart on rectangle with proportions other than 1:1 will left some empty spaces.\n *\n * @author Leszek Wach\n */\npublic class PieChartView extends AbstractChartView implements PieChartDataProvider {\n    private static final String TAG = \"PieChartView\";\n    protected PieChartData data;\n    protected PieChartOnValueSelectListener onValueTouchListener = new DummyPieChartOnValueSelectListener();\n    protected PieChartRenderer pieChartRenderer;\n    protected PieChartRotationAnimator rotationAnimator;\n\n    public PieChartView(Context context) {\n        this(context, null, 0);\n    }\n\n    public PieChartView(Context context, AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public PieChartView(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n        pieChartRenderer = new PieChartRenderer(context, this, this);\n        touchHandler = new PieChartTouchHandler(context, this);\n        setChartRenderer(pieChartRenderer);\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {\n            this.rotationAnimator = new PieChartRotationAnimatorV8(this);\n        } else {\n            this.rotationAnimator = new PieChartRotationAnimatorV14(this);\n        }\n        setPieChartData(PieChartData.generateDummyData());\n    }\n\n    @Override\n    public PieChartData getPieChartData() {\n        return data;\n    }\n\n    @Override\n    public void setPieChartData(PieChartData data) {\n        if (BuildConfig.DEBUG) {\n            Log.d(TAG, \"Setting data for ColumnChartView\");\n        }\n\n        if (null == data) {\n            this.data = PieChartData.generateDummyData();\n        } else {\n            this.data = data;\n        }\n\n        super.onChartDataChange();\n    }\n\n    @Override\n    public ChartData getChartData() {\n        return data;\n    }\n\n    @Override\n    public void callTouchListener() {\n        SelectedValue selectedValue = chartRenderer.getSelectedValue();\n\n        if (selectedValue.isSet()) {\n            SliceValue sliceValue = data.getValues().get(selectedValue.getFirstIndex());\n            onValueTouchListener.onValueSelected(selectedValue.getFirstIndex(), sliceValue);\n        } else {\n            onValueTouchListener.onValueDeselected();\n        }\n    }\n\n    public PieChartOnValueSelectListener getOnValueTouchListener() {\n        return onValueTouchListener;\n    }\n\n    public void setOnValueTouchListener(PieChartOnValueSelectListener touchListener) {\n        if (null != touchListener) {\n            this.onValueTouchListener = touchListener;\n        }\n    }\n\n    /**\n     * Returns rectangle that will constraint pie chart area.\n     */\n    public RectF getCircleOval() {\n        return pieChartRenderer.getCircleOval();\n    }\n\n    /**\n     * Use this to change pie chart area. Because by default CircleOval is calculated onSizeChanged() you must call this\n     * method after size of PieChartView is calculated. In most cases it will probably be easier to use\n     * {@link #setCircleFillRatio(float)} to change chart area or just use view padding.\n     */\n    public void setCircleOval(RectF orginCircleOval) {\n        pieChartRenderer.setCircleOval(orginCircleOval);\n        ViewCompat.postInvalidateOnAnimation(this);\n    }\n\n    /**\n     * Returns pie chart rotation, 0 rotation means that 0 degrees is at 3 o'clock. Don't confuse with\n     * {@link View#getRotation()}.\n     *\n     * @return\n     */\n    public int getChartRotation() {\n        return pieChartRenderer.getChartRotation();\n    }\n\n    /**\n     * Set pie chart rotation. Don't confuse with {@link View#getRotation()}.\n     *\n     * @param rotation\n     * @see #getChartRotation()\n     */\n    public void setChartRotation(int rotation, boolean isAnimated) {\n        if (isAnimated) {\n            rotationAnimator.cancelAnimation();\n            rotationAnimator.startAnimation(pieChartRenderer.getChartRotation(), rotation);\n        } else {\n            pieChartRenderer.setChartRotation(rotation);\n        }\n        ViewCompat.postInvalidateOnAnimation(this);\n    }\n\n    public boolean isChartRotationEnabled() {\n        if (touchHandler instanceof PieChartTouchHandler) {\n            return ((PieChartTouchHandler) touchHandler).isRotationEnabled();\n        } else {\n            return false;\n        }\n    }\n\n    /**\n     * Set false if you don't wont the chart to be rotated by touch gesture. Rotating programmatically will still work.\n     *\n     * @param isRotationEnabled\n     */\n    public void setChartRotationEnabled(boolean isRotationEnabled) {\n        if (touchHandler instanceof PieChartTouchHandler) {\n            ((PieChartTouchHandler) touchHandler).setRotationEnabled(isRotationEnabled);\n        }\n    }\n\n    /**\n     * Returns SliceValue that is under given angle, selectedValue (if not null) will be hold slice index.\n     */\n    public SliceValue getValueForAngle(int angle, SelectedValue selectedValue) {\n        return pieChartRenderer.getValueForAngle(angle, selectedValue);\n    }\n\n    /**\n     * @see #setCircleFillRatio(float)\n     */\n    public float getCircleFillRatio() {\n        return pieChartRenderer.getCircleFillRatio();\n    }\n\n    /**\n     * Set how much of view area should be taken by chart circle. Value should be between 0 and 1. Default is 1 so\n     * circle will have radius equals min(View.width, View.height).\n     */\n    public void setCircleFillRatio(float fillRatio) {\n        pieChartRenderer.setCircleFillRatio(fillRatio);\n        ViewCompat.postInvalidateOnAnimation(this);\n    }\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/view/PreviewColumnChartView.java",
    "content": "package lecho.lib.hellocharts.view;\n\nimport android.content.Context;\nimport android.support.v4.view.ViewCompat;\nimport android.util.AttributeSet;\nimport android.util.Log;\n\nimport lecho.lib.hellocharts.BuildConfig;\nimport lecho.lib.hellocharts.computator.PreviewChartComputator;\nimport lecho.lib.hellocharts.gesture.PreviewChartTouchHandler;\nimport lecho.lib.hellocharts.model.ColumnChartData;\nimport lecho.lib.hellocharts.renderer.PreviewColumnChartRenderer;\n\n/**\n * Preview chart that can be used as overview for other ColumnChart. When you change Viewport of this chart, visible\n * area of other chart will change. For that you need also to use\n * {@link Chart#setViewportChangeListener(lecho.lib.hellocharts.listener.ViewportChangeListener)}\n *\n * @author Leszek Wach\n */\npublic class PreviewColumnChartView extends ColumnChartView {\n    private static final String TAG = \"ColumnChartView\";\n\n    protected PreviewColumnChartRenderer previewChartRenderer;\n\n    public PreviewColumnChartView(Context context) {\n        this(context, null, 0);\n    }\n\n    public PreviewColumnChartView(Context context, AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public PreviewColumnChartView(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n        chartComputator = new PreviewChartComputator();\n        previewChartRenderer = new PreviewColumnChartRenderer(context, this, this);\n        touchHandler = new PreviewChartTouchHandler(context, this);\n        setChartRenderer(previewChartRenderer);\n        setColumnChartData(ColumnChartData.generateDummyData());\n    }\n\n    public int getPreviewColor() {\n        return previewChartRenderer.getPreviewColor();\n    }\n\n    public void setPreviewColor(int color) {\n        if (BuildConfig.DEBUG) {\n            Log.d(TAG, \"Changing preview area color\");\n        }\n\n        previewChartRenderer.setPreviewColor(color);\n        ViewCompat.postInvalidateOnAnimation(this);\n    }\n\n    @Override\n    public boolean canScrollHorizontally(int direction) {\n        final int offset = computeHorizontalScrollOffset();\n        final int range = computeHorizontalScrollRange() - computeHorizontalScrollExtent();\n        if (range == 0) return false;\n        if (direction < 0) {\n            return offset > 0;\n        } else {\n            return offset < range - 1;\n        }\n    }\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/view/PreviewLineChartView.java",
    "content": "package lecho.lib.hellocharts.view;\n\nimport android.content.Context;\nimport android.support.v4.view.ViewCompat;\nimport android.util.AttributeSet;\nimport android.util.Log;\n\nimport lecho.lib.hellocharts.BuildConfig;\nimport lecho.lib.hellocharts.computator.PreviewChartComputator;\nimport lecho.lib.hellocharts.gesture.PreviewChartTouchHandler;\nimport lecho.lib.hellocharts.model.LineChartData;\nimport lecho.lib.hellocharts.renderer.PreviewLineChartRenderer;\n\n/**\n * Preview chart that can be used as overview for other LineChart. When you change Viewport of this chart, visible area\n * of other chart will change. For that you need also to use\n * {@link Chart#setViewportChangeListener(lecho.lib.hellocharts.listener.ViewportChangeListener)}\n *\n * @author Leszek Wach\n */\npublic class PreviewLineChartView extends LineChartView {\n    private static final String TAG = \"PreviewLineChartView\";\n\n    protected PreviewLineChartRenderer previewChartRenderer;\n\n    public PreviewLineChartView(Context context) {\n        this(context, null, 0);\n    }\n\n    public PreviewLineChartView(Context context, AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public PreviewLineChartView(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n        chartComputator = new PreviewChartComputator();\n        previewChartRenderer = new PreviewLineChartRenderer(context, this, this);\n        touchHandler = new PreviewChartTouchHandler(context, this);\n        setChartRenderer(previewChartRenderer);\n        setLineChartData(LineChartData.generateDummyData());\n    }\n\n    public int getPreviewColor() {\n        return previewChartRenderer.getPreviewColor();\n    }\n\n    public void setPreviewColor(int color) {\n        if (BuildConfig.DEBUG) {\n            Log.d(TAG, \"Changing preview area color\");\n        }\n\n        previewChartRenderer.setPreviewColor(color);\n        ViewCompat.postInvalidateOnAnimation(this);\n    }\n\n    @Override\n    public boolean canScrollHorizontally(int direction) {\n        final int offset = computeHorizontalScrollOffset();\n        final int range = computeHorizontalScrollRange() - computeHorizontalScrollExtent();\n        if (range == 0) return false;\n        if (direction < 0) {\n            return offset > 0;\n        } else {\n            return offset < range - 1;\n        }\n    }\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/view/hack/HackyDrawerLayout.java",
    "content": "package lecho.lib.hellocharts.view.hack;\n\nimport android.content.Context;\nimport android.support.v4.widget.DrawerLayout;\nimport android.util.AttributeSet;\nimport android.view.MotionEvent;\n\n/**\n * Hacky fix for issue with DrawerLayout https://github.com/chrisbanes/PhotoView/issues/72\n */\npublic class HackyDrawerLayout extends DrawerLayout {\n\n    public HackyDrawerLayout(Context context) {\n        super(context);\n    }\n\n    public HackyDrawerLayout(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public HackyDrawerLayout(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n    }\n\n    @Override\n    public boolean onInterceptTouchEvent(MotionEvent ev) {\n        try {\n            return super.onInterceptTouchEvent(ev);\n        } catch (Exception e) {\n            e.printStackTrace();\n            return false;\n        }\n    }\n\n}\n"
  },
  {
    "path": "hellocharts-library/src/lecho/lib/hellocharts/view/hack/HackyViewPager.java",
    "content": "package lecho.lib.hellocharts.view.hack;\n\nimport android.content.Context;\nimport android.support.v4.view.ViewPager;\nimport android.util.AttributeSet;\nimport android.view.MotionEvent;\n\n/**\n * ScaleGestureDetector seems to mess up the touch events, which means that ViewGroups which make use of\n * onInterceptTouchEvent throw a lot of IllegalArgumentException: pointerIndex out of range.There's not much I can do\n * in my code for now, but we can mask the result by just catching the problem and ignoring\n * it.\n *\n * @author Chris Banes\n */\npublic class HackyViewPager extends ViewPager {\n\n    public HackyViewPager(Context context) {\n        super(context);\n    }\n\n    public HackyViewPager(final Context context, final AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    @Override\n    public boolean onInterceptTouchEvent(MotionEvent ev) {\n        try {\n            return super.onInterceptTouchEvent(ev);\n        } catch (Exception e) {\n            e.printStackTrace();\n            return false;\n        }\n    }\n\n}\n"
  },
  {
    "path": "hellocharts-samples/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"lecho.lib.hellocharts.samples\"\n    android:versionCode=\"13\"\n    android:versionName=\"1.5.8\" >\n\n    <uses-sdk\n        android:minSdkVersion=\"8\"\n        android:targetSdkVersion=\"22\" />\n\n    <application\n        android:allowBackup=\"true\"\n        android:icon=\"@drawable/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:theme=\"@style/AppTheme\" >\n        <activity\n            android:name=\".MainActivity\"\n            android:label=\"@string/app_name\" >\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".LineChartActivity\"\n            android:label=\"@string/title_activity_line_chart\" >\n        </activity>\n        <activity\n            android:name=\".ColumnChartActivity\"\n            android:label=\"@string/title_activity_column_chart\" >\n        </activity>\n        <activity\n            android:name=\".PieChartActivity\"\n            android:label=\"@string/title_activity_pie_chart\" >\n        </activity>\n        <activity\n            android:name=\".BubbleChartActivity\"\n            android:label=\"@string/title_activity_bubble_chart\" >\n        </activity>\n        <activity\n            android:name=\".PreviewLineChartActivity\"\n            android:label=\"@string/title_activity_preview_line_chart\" >\n        </activity>\n        <activity\n            android:name=\".PreviewColumnChartActivity\"\n            android:label=\"@string/title_activity_preview_column_chart\" >\n        </activity>\n        <activity\n            android:name=\".ComboLineColumnChartActivity\"\n            android:label=\"@string/title_activity_combo_line_column_chart\" >\n        </activity>\n        <activity\n            android:name=\".LineColumnDependencyActivity\"\n            android:label=\"@string/title_activity_line_column_dependency\" >\n        </activity>\n        <activity\n            android:name=\".GoodBadChartActivity\"\n            android:label=\"@string/title_activity_good_bad\" >\n        </activity>\n        <activity\n            android:name=\".TempoChartActivity\"\n            android:label=\"@string/title_activity_tempo_chart\" >\n        </activity>\n        <activity\n            android:name=\".SpeedChartActivity\"\n            android:label=\"@string/title_activity_speed_chart\" >\n        </activity>\n        <activity\n            android:name=\".ViewPagerChartsActivity\"\n            android:label=\"@string/title_activity_view_pager_charts\" >\n        </activity>\n        <activity\n            android:name=\".AboutActivity\"\n            android:label=\"@string/title_activity_about\" >\n        </activity>\n    </application>\n\n</manifest>"
  },
  {
    "path": "hellocharts-samples/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\ndependencies {\n    //compile fileTree(dir: 'libs', include: '*.jar')\n    compile project(':hellocharts-library')\n    compile 'com.android.support:support-v4:23.3.0'\n    compile 'com.android.support:appcompat-v7:23.3.0'\n}\n\nandroid {\n    compileSdkVersion Integer.parseInt(project.ANDROID_BUILD_SDK_VERSION)\n    buildToolsVersion project.ANDROID_BUILD_TOOLS_VERSION\n\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_7\n        targetCompatibility JavaVersion.VERSION_1_7\n    }\n\n    defaultConfig {\n        minSdkVersion Integer.parseInt(project.ANDROID_BUILD_MIN_SDK_VERSION)\n        targetSdkVersion Integer.parseInt(project.ANDROID_BUILD_TARGET_SDK_VERSION)\n        versionCode Integer.parseInt(project.VERSION_CODE)\n        versionName project.VERSION_NAME\n    }\n\n    sourceSets {\n        main {\n            manifest.srcFile 'AndroidManifest.xml'\n            java.srcDirs = ['src']\n            resources.srcDirs = ['src']\n            aidl.srcDirs = ['src']\n            renderscript.srcDirs = ['src']\n            res.srcDirs = ['res']\n            assets.srcDirs = ['assets']\n        }\n    }\n\t\n\tbuildTypes {\n\t\trelease {\n            minifyEnabled false\n\t\t\tproguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n\t\t}\n\t}\n\n\tlintOptions {\n\t\tabortOnError false\n\t}\n}\n"
  },
  {
    "path": "hellocharts-samples/proguard-project.txt",
    "content": "# To enable ProGuard in your project, edit project.properties\n# to define the proguard.config property as described in that file.\n#\n# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in ${sdk.dir}/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the ProGuard\n# include property in project.properties.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n"
  },
  {
    "path": "hellocharts-samples/project.properties",
    "content": "# This file is automatically generated by Android Tools.\n# Do not modify this file -- YOUR CHANGES WILL BE ERASED!\n#\n# This file must be checked in Version Control Systems.\n#\n# To customize properties used by the Ant build system edit\n# \"ant.properties\", and override values to adapt the script to your\n# project structure.\n#\n# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):\n#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt\n\n# Project target.\ntarget=android-21\nandroid.library.reference.1=..\\\\..\\\\appcompat_v7_21\\\\appcompat_v7_21\nandroid.library.reference.2=..\\\\hellocharts-library\n"
  },
  {
    "path": "hellocharts-samples/res/color/selector_text_link.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item android:state_pressed=\"true\" android:color=\"@color/holo_blue_dark\"/>\n    <item android:state_focused=\"true\" android:color=\"@color/holo_blue_light\"/>\n    <item android:color=\"@color/holo_blue\"/>\n\n</selector>"
  },
  {
    "path": "hellocharts-samples/res/layout/activity_about.xml",
    "content": "<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\"lecho.lib.hellocharts.samples.AboutActivity\"\n    tools:ignore=\"MergeRootFrame\" />\n\n"
  },
  {
    "path": "hellocharts-samples/res/layout/activity_bubble_chart.xml",
    "content": "<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\"lecho.lib.hellocharts.samples.BubbleChartActivity\"\n    tools:ignore=\"MergeRootFrame\" />\n\n"
  },
  {
    "path": "hellocharts-samples/res/layout/activity_column_chart.xml",
    "content": "<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\"lecho.lib.hellocharts.samples.ColumnChartActivity\"\n    tools:ignore=\"MergeRootFrame\" />\n\n"
  },
  {
    "path": "hellocharts-samples/res/layout/activity_combo_line_column_chart.xml",
    "content": "<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\"lecho.lib.hellocharts.samples.ComboLineColumnChartActivity\"\n    tools:ignore=\"MergeRootFrame\" />\n\n"
  },
  {
    "path": "hellocharts-samples/res/layout/activity_good_bad.xml",
    "content": "<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\"lecho.lib.hellocharts.samples.GoodBadChartActivity\"\n    tools:ignore=\"MergeRootFrame\" />\n\n"
  },
  {
    "path": "hellocharts-samples/res/layout/activity_line_chart.xml",
    "content": "<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\"lecho.lib.hellocharts.samples.LineChartActivity\"\n    tools:ignore=\"MergeRootFrame\" />\n\n"
  },
  {
    "path": "hellocharts-samples/res/layout/activity_line_column_dependency.xml",
    "content": "<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\"lecho.lib.hellocharts.samples.LineColumnDependencyActivity\"\n    tools:ignore=\"MergeRootFrame\" />\n\n"
  },
  {
    "path": "hellocharts-samples/res/layout/activity_main.xml",
    "content": "<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\"lecho.lib.hellocharts.samples.MainActivity\"\n    tools:ignore=\"MergeRootFrame\" />\n\n"
  },
  {
    "path": "hellocharts-samples/res/layout/activity_pie_chart.xml",
    "content": "<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\"lecho.lib.hellocharts.samples.PieChartActivity\"\n    tools:ignore=\"MergeRootFrame\" />\n\n"
  },
  {
    "path": "hellocharts-samples/res/layout/activity_preview_column_chart.xml",
    "content": "<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\"lecho.lib.hellocharts.samples.PreviewColumnChartActivity\"\n    tools:ignore=\"MergeRootFrame\" />\n\n"
  },
  {
    "path": "hellocharts-samples/res/layout/activity_preview_line_chart.xml",
    "content": "<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\"lecho.lib.hellocharts.samples.PreviewLineChartActivity\"\n    tools:ignore=\"MergeRootFrame\" />\n\n"
  },
  {
    "path": "hellocharts-samples/res/layout/activity_tempo_chart.xml",
    "content": "<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\"lecho.lib.hellocharts.samples.TempoChartActivity\"\n    tools:ignore=\"MergeRootFrame\" />\n\n"
  },
  {
    "path": "hellocharts-samples/res/layout/activity_view_pager_charts.xml",
    "content": "<lecho.lib.hellocharts.view.hack.HackyViewPager xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/pager\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\"lecho.lib.hellocharts.samples.ViewPagerChartsActivity\" />\n\n"
  },
  {
    "path": "hellocharts-samples/res/layout/fragment_about.xml",
    "content": "<ScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:fillViewport=\"true\" >\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:paddingBottom=\"@dimen/activity_vertical_margin\"\n        android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n        android:paddingRight=\"@dimen/activity_horizontal_margin\"\n        android:paddingTop=\"@dimen/activity_vertical_margin\"\n        tools:context=\"lecho.lib.hellocharts.samples.AboutActivity$PlaceholderFragment\" >\n\n        <ImageView\n            android:id=\"@+id/app_icon\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerHorizontal=\"true\"\n            android:layout_margin=\"16dp\"\n            android:contentDescription=\"@string/about_app_icon_content_description\"\n            android:src=\"@drawable/ic_launcher\" />\n\n        <TextView\n            android:id=\"@+id/app_name\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_below=\"@id/app_icon\"\n            android:layout_centerHorizontal=\"true\"\n            android:text=\"@string/app_name\"\n            android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n\n        <LinearLayout\n            android:id=\"@+id/version_layout\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_below=\"@id/app_name\"\n            android:layout_centerHorizontal=\"true\"\n            android:orientation=\"horizontal\" >\n\n            <TextView\n                android:id=\"@+id/version_label\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/about_version_label\" />\n\n            <TextView\n                android:id=\"@+id/version\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\" />\n        </LinearLayout>\n\n        <TextView\n            android:id=\"@+id/author\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_below=\"@id/version_layout\"\n            android:layout_centerHorizontal=\"true\"\n            android:text=\"@string/about_author\" />\n\n        <TextView\n            android:id=\"@+id/go_to_github\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_below=\"@id/author\"\n            android:layout_marginTop=\"8dp\"\n            android:gravity=\"center\"\n            android:padding=\"8dp\"\n            android:text=\"@string/about_go_to_github\"\n            android:textAppearance=\"?android:attr/textAppearanceMedium\"\n            android:textColor=\"@color/selector_text_link\" />\n    </RelativeLayout>\n\n</ScrollView>"
  },
  {
    "path": "hellocharts-samples/res/layout/fragment_bubble_chart.xml",
    "content": "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:paddingBottom=\"@dimen/activity_vertical_margin\"\n    android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n    android:paddingRight=\"@dimen/activity_horizontal_margin\"\n    android:paddingTop=\"@dimen/activity_vertical_margin\"\n    tools:context=\"lecho.lib.hellocharts.samples.BubbleChartActivity$PlaceholderFragment\" >\n\n    <lecho.lib.hellocharts.view.BubbleChartView\n        android:id=\"@+id/chart\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" >\n    </lecho.lib.hellocharts.view.BubbleChartView>\n\n</RelativeLayout>\n"
  },
  {
    "path": "hellocharts-samples/res/layout/fragment_column_chart.xml",
    "content": "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:paddingBottom=\"@dimen/activity_vertical_margin\"\n    android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n    android:paddingRight=\"@dimen/activity_horizontal_margin\"\n    android:paddingTop=\"@dimen/activity_vertical_margin\"\n    tools:context=\"lecho.lib.hellocharts.samples.ColumnChartActivity$PlaceholderFragment\" >\n\n    <lecho.lib.hellocharts.view.ColumnChartView\n        android:id=\"@+id/chart\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" >\n    </lecho.lib.hellocharts.view.ColumnChartView>\n\n</RelativeLayout>\n"
  },
  {
    "path": "hellocharts-samples/res/layout/fragment_combo_line_column_chart.xml",
    "content": "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:paddingBottom=\"@dimen/activity_vertical_margin\"\n    android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n    android:paddingRight=\"@dimen/activity_horizontal_margin\"\n    android:paddingTop=\"@dimen/activity_vertical_margin\"\n    tools:context=\"lecho.lib.hellocharts.samples.ComboLineColumnChartActivity$PlaceholderFragment\" >\n\n    <lecho.lib.hellocharts.view.ComboLineColumnChartView\n        android:id=\"@+id/chart\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" >\n    </lecho.lib.hellocharts.view.ComboLineColumnChartView>\n\n</RelativeLayout>\n"
  },
  {
    "path": "hellocharts-samples/res/layout/fragment_good_bad.xml",
    "content": "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:paddingBottom=\"@dimen/activity_vertical_margin\"\n    android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n    android:paddingRight=\"@dimen/activity_horizontal_margin\"\n    android:paddingTop=\"@dimen/activity_vertical_margin\"\n    tools:context=\"lecho.lib.hellocharts.samples.GoodBadActivity$PlaceholderFragment\" >\n\n    <lecho.lib.hellocharts.view.LineChartView\n        android:id=\"@+id/chart\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" >\n    </lecho.lib.hellocharts.view.LineChartView>\n\n</RelativeLayout>"
  },
  {
    "path": "hellocharts-samples/res/layout/fragment_line_chart.xml",
    "content": "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:paddingBottom=\"@dimen/activity_vertical_margin\"\n    android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n    android:paddingRight=\"@dimen/activity_horizontal_margin\"\n    android:paddingTop=\"@dimen/activity_vertical_margin\"\n    tools:context=\"lecho.lib.hellocharts.samples.LineChartActivity$PlaceholderFragment\" >\n\n    <lecho.lib.hellocharts.view.LineChartView\n        android:id=\"@+id/chart\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" >\n    </lecho.lib.hellocharts.view.LineChartView>\n\n</RelativeLayout>"
  },
  {
    "path": "hellocharts-samples/res/layout/fragment_line_column_dependency.xml",
    "content": "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\"\n    android:paddingBottom=\"@dimen/activity_vertical_margin\"\n    android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n    android:paddingRight=\"@dimen/activity_horizontal_margin\"\n    android:paddingTop=\"@dimen/activity_vertical_margin\"\n    tools:context=\"lecho.lib.hellocharts.samples.PreviewLineChartActivity$PlaceholderFragment\" >\n\n    <lecho.lib.hellocharts.view.LineChartView\n        android:id=\"@+id/chart_top\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:layout_weight=\"1\" >\n    </lecho.lib.hellocharts.view.LineChartView>\n\n    <View\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"2dp\"\n        android:layout_marginBottom=\"8dp\"\n        android:layout_marginTop=\"8dp\"\n        android:background=\"@android:color/darker_gray\" />\n\n    <lecho.lib.hellocharts.view.ColumnChartView\n        android:id=\"@+id/chart_bottom\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:layout_weight=\"1\" >\n    </lecho.lib.hellocharts.view.ColumnChartView>\n\n</LinearLayout>"
  },
  {
    "path": "hellocharts-samples/res/layout/fragment_main.xml",
    "content": "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:paddingBottom=\"@dimen/activity_vertical_margin\"\n    android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n    android:paddingRight=\"@dimen/activity_horizontal_margin\"\n    android:paddingTop=\"@dimen/activity_vertical_margin\"\n    tools:context=\"lecho.lib.hellocharts.samples.MainActivity$PlaceholderFragment\" >\n\n    <ListView\n        android:id=\"@android:id/list\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" >\n    </ListView>\n\n</RelativeLayout>"
  },
  {
    "path": "hellocharts-samples/res/layout/fragment_pie_chart.xml",
    "content": "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:paddingBottom=\"@dimen/activity_vertical_margin\"\n    android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n    android:paddingRight=\"@dimen/activity_horizontal_margin\"\n    android:paddingTop=\"@dimen/activity_vertical_margin\"\n    tools:context=\"lecho.lib.hellocharts.samples.PieChartActivity$PlaceholderFragment\" >\n\n    <lecho.lib.hellocharts.view.PieChartView\n        android:id=\"@+id/chart\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" >\n    </lecho.lib.hellocharts.view.PieChartView>\n\n</RelativeLayout>"
  },
  {
    "path": "hellocharts-samples/res/layout/fragment_preview_column_chart.xml",
    "content": "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\"\n    android:paddingBottom=\"@dimen/activity_vertical_margin\"\n    android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n    android:paddingRight=\"@dimen/activity_horizontal_margin\"\n    android:paddingTop=\"@dimen/activity_vertical_margin\"\n    tools:context=\"lecho.lib.hellocharts.samples.PreviewLineChartActivity$PlaceholderFragment\" >\n\n    <lecho.lib.hellocharts.view.ColumnChartView\n        android:id=\"@+id/chart\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:layout_weight=\"1\" >\n    </lecho.lib.hellocharts.view.ColumnChartView>\n\n    <View\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"2dp\"\n        android:layout_marginBottom=\"8dp\"\n        android:layout_marginTop=\"8dp\"\n        android:background=\"@android:color/darker_gray\" />\n\n    <lecho.lib.hellocharts.view.PreviewColumnChartView\n        android:id=\"@+id/chart_preview\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:layout_weight=\"1\" >\n    </lecho.lib.hellocharts.view.PreviewColumnChartView>\n\n</LinearLayout>"
  },
  {
    "path": "hellocharts-samples/res/layout/fragment_preview_line_chart.xml",
    "content": "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\"\n    android:paddingBottom=\"@dimen/activity_vertical_margin\"\n    android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n    android:paddingRight=\"@dimen/activity_horizontal_margin\"\n    android:paddingTop=\"@dimen/activity_vertical_margin\"\n    tools:context=\"lecho.lib.hellocharts.samples.PreviewLineChartActivity$PlaceholderFragment\" >\n\n    <lecho.lib.hellocharts.view.LineChartView\n        android:id=\"@+id/chart\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:layout_weight=\"1\" >\n    </lecho.lib.hellocharts.view.LineChartView>\n\n    <View\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"2dp\"\n        android:layout_marginBottom=\"8dp\"\n        android:layout_marginTop=\"8dp\"\n        android:background=\"@android:color/darker_gray\" />\n\n    <lecho.lib.hellocharts.view.PreviewLineChartView\n        android:id=\"@+id/chart_preview\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:layout_weight=\"1\" >\n    </lecho.lib.hellocharts.view.PreviewLineChartView>\n\n</LinearLayout>"
  },
  {
    "path": "hellocharts-samples/res/layout/fragment_tempo_chart.xml",
    "content": "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:paddingBottom=\"@dimen/activity_vertical_margin\"\n    android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n    android:paddingRight=\"@dimen/activity_horizontal_margin\"\n    android:paddingTop=\"@dimen/activity_vertical_margin\"\n    tools:context=\"lecho.lib.hellocharts.samples.TempoChartActivity$PlaceholderFragment\" >\n\n    <lecho.lib.hellocharts.view.LineChartView\n        android:id=\"@+id/chart\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" >\n    </lecho.lib.hellocharts.view.LineChartView>\n\n</RelativeLayout>"
  },
  {
    "path": "hellocharts-samples/res/layout/fragment_view_pager_charts.xml",
    "content": "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:paddingBottom=\"@dimen/activity_vertical_margin\"\n    android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n    android:paddingRight=\"@dimen/activity_horizontal_margin\"\n    android:paddingTop=\"@dimen/activity_vertical_margin\"\n    tools:context=\"lecho.lib.hellocharts.samples.ViewPagerChartsActivity$PlaceholderFragment\" >\n\n</RelativeLayout>"
  },
  {
    "path": "hellocharts-samples/res/layout/list_item_sample.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:padding=\"8dp\" >\n\n    <TextView\n        android:id=\"@+id/text1\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentLeft=\"true\"\n        android:layout_alignParentTop=\"true\"\n        android:layout_toLeftOf=\"@+id/chart_layout\"\n        android:text=\"TEXT1\"\n        android:textSize=\"16sp\" />\n\n    <TextView\n        android:id=\"@+id/text2\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignLeft=\"@+id/text1\"\n        android:layout_below=\"@+id/text1\"\n        android:layout_toLeftOf=\"@id/chart_layout\"\n        android:text=\"TEXT2\"\n        android:textSize=\"12sp\" />\n\n    <FrameLayout\n        android:id=\"@id/chart_layout\"\n        android:layout_width=\"54dp\"\n        android:layout_height=\"54dp\"\n        android:layout_alignParentRight=\"true\"\n        android:visibility=\"gone\" >\n    </FrameLayout>\n\n</RelativeLayout>"
  },
  {
    "path": "hellocharts-samples/res/menu/bubble_chart.xml",
    "content": "<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:context=\"lecho.lib.hellocharts.samples.BubbleChartActivity\" >\n\n    <item\n        android:id=\"@+id/action_reset\"\n        android:title=\"Reset chart\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_shape_circles\"\n        android:title=\"Circle shape\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_shape_square\"\n        android:title=\"Square shape\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_toggle_axes\"\n        android:title=\"Toggle axes\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_toggle_axes_names\"\n        android:title=\"Toggle axes names\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_toggle_labels\"\n        android:title=\"Toggle labels\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_animate\"\n        android:title=\"Animate chart\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_toggle_selection_mode\"\n        android:title=\"Toggle selection mode\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_toggle_touch_zoom\"\n        android:title=\"Toggle touch zoom\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_zoom_both\"\n        android:title=\"Zoom X/Y\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_zoom_horizontal\"\n        android:title=\"Zoom X\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_zoom_vertical\"\n        android:title=\"Zoom Y\"\n        app:showAsAction=\"never\"/>\n\n</menu>\n"
  },
  {
    "path": "hellocharts-samples/res/menu/column_chart.xml",
    "content": "<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:context=\"lecho.lib.hellocharts.samples.ColumnChartActivity\" >\n\n    <item\n        android:id=\"@+id/action_reset\"\n        android:title=\"Reset chart\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_subcolumns\"\n        android:title=\"Subcolumns\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_stacked\"\n        android:title=\"Stacked\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_negative_subcolumns\"\n        android:title=\"Negative subcolumns\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_negative_stacked\"\n        android:title=\"Negative stacked\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_toggle_axes\"\n        android:title=\"Toggle axes\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_toggle_axes_names\"\n        android:title=\"Toggle axes names\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_toggle_labels\"\n        android:title=\"Toggle labels\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_animate\"\n        android:title=\"Animate chart\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_toggle_selection_mode\"\n        android:title=\"Toggle selection mode\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_toggle_touch_zoom\"\n        android:title=\"Toggle touch zoom\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_zoom_both\"\n        android:title=\"Zoom X/Y\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_zoom_horizontal\"\n        android:title=\"Zoom X\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_zoom_vertical\"\n        android:title=\"Zoom Y\"\n        app:showAsAction=\"never\"/>\n\n</menu>\n"
  },
  {
    "path": "hellocharts-samples/res/menu/combo_line_column_chart.xml",
    "content": "<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:context=\"lecho.lib.hellocharts.samples.ComboLineColumnChartActivity\" >\n\n    <item\n        android:id=\"@+id/action_reset\"\n        android:title=\"Reset chart\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_add_line\"\n        android:title=\"Add line\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_toggle_lines\"\n        android:title=\"Toggle lines/scattered\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_toggle_points\"\n        android:title=\"Toggle points\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_toggle_cubic\"\n        android:title=\"Toggle Cubic curve\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_toggle_axes\"\n        android:title=\"Toggle axes\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_toggle_axes_names\"\n        android:title=\"Toggle axes names\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_toggle_labels\"\n        android:title=\"Toggle labels\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_animate\"\n        android:title=\"Animate chart\"\n        app:showAsAction=\"never\"/>\n</menu>\n"
  },
  {
    "path": "hellocharts-samples/res/menu/line_chart.xml",
    "content": "<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:context=\"lecho.lib.hellocharts.samples.LineChartActivity\" >\n\n    <item\n        android:id=\"@+id/action_reset\"\n        android:title=\"Reset chart\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_add_line\"\n        android:title=\"Add line\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_toggle_lines\"\n        android:title=\"Toggle lines/scattered\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_toggle_points\"\n        android:title=\"Toggle points\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_toggle_gradient\"\n        android:title=\"Toggle gradient\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_toggle_cubic\"\n        android:title=\"Toggle Cubic curve\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_toggle_area\"\n        android:title=\"Toggle area\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_point_color\"\n        android:title=\"Toggle Point Color\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_shape_circles\"\n        android:title=\"Circle shape\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_shape_square\"\n        android:title=\"Square shape\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_shape_diamond\"\n        android:title=\"Diamond shape\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_toggle_axes\"\n        android:title=\"Toggle axes\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_toggle_axes_names\"\n        android:title=\"Toggle axes names\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_toggle_labels\"\n        android:title=\"Toggle labels\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_animate\"\n        android:title=\"Animate chart\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_toggle_selection_mode\"\n        android:title=\"Toggle selection mode\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_toggle_touch_zoom\"\n        android:title=\"Toggle touch zoom\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_zoom_both\"\n        android:title=\"Zoom X/Y\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_zoom_horizontal\"\n        android:title=\"Zoom X\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_zoom_vertical\"\n        android:title=\"Zoom Y\"\n        app:showAsAction=\"never\"/>\n\n</menu>"
  },
  {
    "path": "hellocharts-samples/res/menu/main.xml",
    "content": "<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:context=\"lecho.lib.hellocharts.samples.MainActivity\" >\n\n    <item\n        android:id=\"@+id/action_about\"\n        android:orderInCategory=\"100\"\n        app:showAsAction=\"never\"\n        android:title=\"@string/action_about\"/>\n\n</menu>"
  },
  {
    "path": "hellocharts-samples/res/menu/pie_chart.xml",
    "content": "<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:context=\"lecho.lib.hellocharts.samples.PieChartActivity\" >\n\n    <item\n        android:id=\"@+id/action_reset\"\n        android:title=\"Reset chart\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_explode\"\n        android:title=\"Explode/Implode chart\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_center_circle\"\n        android:title=\"Center circle\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_center_text1\"\n        android:title=\"Center circle text 1\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_center_text2\"\n        android:title=\"Center circle text 2\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_toggle_labels\"\n        android:title=\"Toggle labels\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_toggle_labels_outside\"\n        android:title=\"Toggle labels outside\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_animate\"\n        android:title=\"Animate chart\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_toggle_selection_mode\"\n        android:title=\"Toggle selection mode\"\n        app:showAsAction=\"never\"/>\n\n</menu>"
  },
  {
    "path": "hellocharts-samples/res/menu/preview_column_chart.xml",
    "content": "<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:context=\"lecho.lib.hellocharts.samples.PreviewColumnChartActivity\" >\n\n    <item\n        android:id=\"@+id/action_reset\"\n        android:title=\"Reset chart\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_preview_both\"\n        android:title=\"Preview X/Y\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_preview_horizontal\"\n        android:title=\"Preveiw X\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_preview_vertical\"\n        android:title=\"Preview Y\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_change_color\"\n        android:title=\"Change color\"\n        app:showAsAction=\"never\"/>\n\n</menu>\n"
  },
  {
    "path": "hellocharts-samples/res/menu/preview_line_chart.xml",
    "content": "<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:context=\"lecho.lib.hellocharts.samples.PreviewLineChartActivity\" >\n\n    <item\n        android:id=\"@+id/action_reset\"\n        android:title=\"Reset chart\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_preview_both\"\n        android:title=\"Preview X/Y\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_preview_horizontal\"\n        android:title=\"Preveiw X\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_preview_vertical\"\n        android:title=\"Preview Y\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_change_color\"\n        android:title=\"Change color\"\n        app:showAsAction=\"never\"/>\n\n</menu>"
  },
  {
    "path": "hellocharts-samples/res/menu/tempo_chart.xml",
    "content": "<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:context=\"lecho.lib.hellocharts.samples.TempoChartActivity\" >\n\n    <item\n        android:id=\"@+id/action_tempo\"\n        android:orderInCategory=\"100\"\n        android:title=\"Tempo/Height\"\n        app:showAsAction=\"never\"/>\n    <item\n        android:id=\"@+id/action_speed\"\n        android:orderInCategory=\"100\"\n        android:title=\"Speed/Height\"\n        app:showAsAction=\"never\"/>\n\n</menu>"
  },
  {
    "path": "hellocharts-samples/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <color name=\"holo_blue\">#33B5E5</color>\n    <color name=\"holo_blue_light\">#8033B5E5</color>\n    <color name=\"holo_blue_dark\">#0099CC</color>\n    <color name=\"holo_orange_dark\">#FF8800</color>\n    <color name=\"holo_orange\">#FFBB33</color>\n\n</resources>"
  },
  {
    "path": "hellocharts-samples/res/values/dimens.xml",
    "content": "<resources>\n\n    <!-- Default screen margins, per the Android Design guidelines. -->\n    <dimen name=\"activity_horizontal_margin\">16dp</dimen>\n    <dimen name=\"activity_vertical_margin\">16dp</dimen>\n    <dimen name=\"pie_chart_text1_size\">42sp</dimen>\n    <dimen name=\"pie_chart_text2_size\">16dp</dimen>\n\n</resources>"
  },
  {
    "path": "hellocharts-samples/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <string name=\"app_name\">HelloCharts</string>\n    <string name=\"action_about\">About</string>\n    <string name=\"title_activity_line_chart\">LineChartActivity</string>\n    <string name=\"title_activity_column_chart\">ColumnChartActivity</string>\n    <string name=\"title_activity_pie_chart\">PieChartActivity</string>\n    <string name=\"title_activity_bubble_chart\">BubbleChartActivity</string>\n    <string name=\"title_activity_preview_line_chart\">PreviewLineChartActivity</string>\n    <string name=\"title_activity_preview_column_chart\">PreviewColumnChartActivity</string>\n    <string name=\"title_activity_combo_line_column_chart\">ComboLineColumnChartActivity</string>\n    <string name=\"title_activity_line_column_dependency\">LineColumnDependency</string>\n    <string name=\"title_activity_good_bad\">GoodBadActivity</string>\n    <string name=\"title_activity_tempo_chart\">TempoChartActivity</string>\n    <string name=\"title_activity_speed_chart\">SpeedChartActivity</string>\n    <string name=\"title_activity_view_pager_charts\">ViewPagerChartsActivity</string>\n    <string name=\"title_section1\">Section 1</string>\n    <string name=\"title_section2\">Section 2</string>\n    <string name=\"title_section3\">Section 3</string>\n    <string name=\"title_activity_about\">AboutActivity</string>\n    <string name=\"about_version_label\">\"Library version \"</string>\n    <string name=\"about_author\">Leszek Wach 2014</string>\n    <string name=\"about_app_icon_content_description\">App icon</string>\n    <string name=\"about_go_to_github\">Go to GitHub</string>\n\n</resources>"
  },
  {
    "path": "hellocharts-samples/res/values/styles.xml",
    "content": "<resources>\n\n    <style name=\"AppBaseTheme\" parent=\"Theme.AppCompat.Light\">\n        <item name=\"colorPrimary\">@color/holo_blue</item>\n        <item name=\"colorPrimaryDark\">@color/holo_blue_dark</item>\n    </style>\n\n    <style name=\"AppTheme\" parent=\"AppBaseTheme\"></style>\n\n</resources>"
  },
  {
    "path": "hellocharts-samples/res/values-land/dimens.xml",
    "content": "<resources>\n\n    <dimen name=\"pie_chart_text1_size\">38sp</dimen>\n    <dimen name=\"pie_chart_text2_size\">12dp</dimen>\n\n</resources>"
  },
  {
    "path": "hellocharts-samples/res/values-sw600dp/dimens.xml",
    "content": "<resources>\n\n    <!--\n         Customize dimensions originally defined in res/values/dimens.xml (such as\n         screen margins) for sw600dp devices (e.g. 7\" tablets) here.\n    -->\n    \n    <dimen name=\"pie_chart_text1_size\">54sp</dimen>\n    <dimen name=\"pie_chart_text2_size\">24dp</dimen>\n\n</resources>\n"
  },
  {
    "path": "hellocharts-samples/res/values-v11/styles.xml",
    "content": "<resources>\n\n</resources>\n"
  },
  {
    "path": "hellocharts-samples/res/values-v14/styles.xml",
    "content": "<resources>\n\n</resources>"
  },
  {
    "path": "hellocharts-samples/res/values-w820dp/dimens.xml",
    "content": "<resources>\n\n    <!--\n         Example customization of dimensions originally defined in res/values/dimens.xml\n         (such as screen margins) for screens with more than 820dp of available width. This\n         would include 7\" and 10\" devices in landscape (~960dp and ~1280dp respectively).\n    -->\n    <dimen name=\"activity_horizontal_margin\">64dp</dimen>\n\n</resources>\n"
  },
  {
    "path": "hellocharts-samples/src/lecho/lib/hellocharts/samples/AboutActivity.java",
    "content": "package lecho.lib.hellocharts.samples;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.pm.PackageInfo;\nimport android.content.pm.PackageManager;\nimport android.content.pm.ResolveInfo;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.support.v7.app.ActionBarActivity;\nimport android.util.Log;\nimport android.util.Pair;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.TextView;\n\npublic class AboutActivity extends ActionBarActivity {\n    public static final String TAG = AboutActivity.class.getSimpleName();\n    public static final String GITHUB_URL = \"github.com/lecho/hellocharts-android\";\n\n    public static Pair<String, Integer> getAppVersionAndBuild(Context context) {\n        try {\n            PackageInfo pInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);\n            return new Pair<String, Integer>(pInfo.versionName, pInfo.versionCode);\n        } catch (Exception e) {\n            Log.e(TAG, \"Could not get version number\");\n            return new Pair<String, Integer>(\"\", 0);\n        }\n    }\n\n    @SuppressLint(\"DefaultLocale\")\n    public static boolean launchWebBrowser(Context context, String url) {\n        try {\n            url = url.toLowerCase();\n            if (!url.startsWith(\"http://\") || !url.startsWith(\"https://\")) {\n                url = \"http://\" + url;\n            }\n\n            Intent intent = new Intent(Intent.ACTION_VIEW);\n            intent.setData(Uri.parse(url));\n            ResolveInfo resolveInfo = context.getPackageManager().resolveActivity(intent,\n                    PackageManager.MATCH_DEFAULT_ONLY);\n            if (null == resolveInfo) {\n                Log.e(TAG, \"No activity to handle web intent\");\n                return false;\n            }\n            context.startActivity(intent);\n            Log.i(TAG, \"Launching browser with url: \" + url);\n            return true;\n        } catch (Exception e) {\n            Log.e(TAG, \"Could not start web browser\", e);\n            return false;\n        }\n    }\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_about);\n        if (savedInstanceState == null) {\n            getSupportFragmentManager().beginTransaction().add(R.id.container, new PlaceholderFragment()).commit();\n        }\n    }\n\n    /**\n     * A placeholder fragment containing a simple view.\n     */\n    public static class PlaceholderFragment extends Fragment {\n\n        public PlaceholderFragment() {\n        }\n\n        @Override\n        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n            View rootView = inflater.inflate(R.layout.fragment_about, container, false);\n\n            TextView version = (TextView) rootView.findViewById(R.id.version);\n            version.setText(getAppVersionAndBuild(getActivity()).first);\n\n            TextView gotToGithub = (TextView) rootView.findViewById(R.id.go_to_github);\n            gotToGithub.setOnClickListener(new View.OnClickListener() {\n\n                @Override\n                public void onClick(View v) {\n                    launchWebBrowser(getActivity(), GITHUB_URL);\n\n                }\n            });\n\n            return rootView;\n        }\n    }\n}\n"
  },
  {
    "path": "hellocharts-samples/src/lecho/lib/hellocharts/samples/BubbleChartActivity.java",
    "content": "package lecho.lib.hellocharts.samples;\n\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.support.v7.app.ActionBarActivity;\nimport android.view.LayoutInflater;\nimport android.view.Menu;\nimport android.view.MenuInflater;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.Toast;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport lecho.lib.hellocharts.gesture.ZoomType;\nimport lecho.lib.hellocharts.listener.BubbleChartOnValueSelectListener;\nimport lecho.lib.hellocharts.model.Axis;\nimport lecho.lib.hellocharts.model.BubbleChartData;\nimport lecho.lib.hellocharts.model.BubbleValue;\nimport lecho.lib.hellocharts.model.ValueShape;\nimport lecho.lib.hellocharts.util.ChartUtils;\nimport lecho.lib.hellocharts.view.BubbleChartView;\nimport lecho.lib.hellocharts.view.Chart;\n\npublic class BubbleChartActivity extends ActionBarActivity {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_bubble_chart);\n        if (savedInstanceState == null) {\n            getSupportFragmentManager().beginTransaction().add(R.id.container, new PlaceholderFragment()).commit();\n        }\n    }\n\n    /**\n     * A fragment containing a bubble chart.\n     */\n    public static class PlaceholderFragment extends Fragment {\n\n        private static final int BUBBLES_NUM = 8;\n\n        private BubbleChartView chart;\n        private BubbleChartData data;\n        private boolean hasAxes = true;\n        private boolean hasAxesNames = true;\n        private ValueShape shape = ValueShape.CIRCLE;\n        private boolean hasLabels = false;\n        private boolean hasLabelForSelected = false;\n\n        public PlaceholderFragment() {\n        }\n\n        @Override\n        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n            setHasOptionsMenu(true);\n            View rootView = inflater.inflate(R.layout.fragment_bubble_chart, container, false);\n\n            chart = (BubbleChartView) rootView.findViewById(R.id.chart);\n            chart.setOnValueTouchListener(new ValueTouchListener());\n\n            generateData();\n\n            return rootView;\n        }\n\n        @Override\n        public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {\n            inflater.inflate(R.menu.bubble_chart, menu);\n        }\n\n        @Override\n        public boolean onOptionsItemSelected(MenuItem item) {\n            int id = item.getItemId();\n            if (id == R.id.action_reset) {\n                reset();\n                generateData();\n                return true;\n            }\n            if (id == R.id.action_shape_circles) {\n                setCircles();\n                return true;\n            }\n            if (id == R.id.action_shape_square) {\n                setSquares();\n                return true;\n            }\n            if (id == R.id.action_toggle_labels) {\n                toggleLabels();\n                return true;\n            }\n            if (id == R.id.action_toggle_axes) {\n                toggleAxes();\n                return true;\n            }\n            if (id == R.id.action_toggle_axes_names) {\n                toggleAxesNames();\n                return true;\n            }\n            if (id == R.id.action_animate) {\n                prepareDataAnimation();\n                chart.startDataAnimation();\n                return true;\n            }\n            if (id == R.id.action_toggle_selection_mode) {\n                toggleLabelForSelected();\n                Toast.makeText(getActivity(),\n                        \"Selection mode set to \" + chart.isValueSelectionEnabled() + \" select any point.\",\n                        Toast.LENGTH_SHORT).show();\n                return true;\n            }\n            if (id == R.id.action_toggle_touch_zoom) {\n                chart.setZoomEnabled(!chart.isZoomEnabled());\n                Toast.makeText(getActivity(), \"IsZoomEnabled \" + chart.isZoomEnabled(), Toast.LENGTH_SHORT).show();\n                return true;\n            }\n            if (id == R.id.action_zoom_both) {\n                chart.setZoomType(ZoomType.HORIZONTAL_AND_VERTICAL);\n                return true;\n            }\n            if (id == R.id.action_zoom_horizontal) {\n                chart.setZoomType(ZoomType.HORIZONTAL);\n                return true;\n            }\n            if (id == R.id.action_zoom_vertical) {\n                chart.setZoomType(ZoomType.VERTICAL);\n                return true;\n            }\n            return super.onOptionsItemSelected(item);\n        }\n\n        private void reset() {\n            hasAxes = true;\n            hasAxesNames = true;\n            shape = ValueShape.CIRCLE;\n            hasLabels = false;\n            hasLabelForSelected = false;\n\n            chart.setValueSelectionEnabled(hasLabelForSelected);\n        }\n\n        private void generateData() {\n\n            List<BubbleValue> values = new ArrayList<BubbleValue>();\n            for (int i = 0; i < BUBBLES_NUM; ++i) {\n                BubbleValue value = new BubbleValue(i, (float) Math.random() * 100, (float) Math.random() * 1000);\n                value.setColor(ChartUtils.pickColor());\n                value.setShape(shape);\n                values.add(value);\n            }\n\n            data = new BubbleChartData(values);\n            data.setHasLabels(hasLabels);\n            data.setHasLabelsOnlyForSelected(hasLabelForSelected);\n\n            if (hasAxes) {\n                Axis axisX = new Axis();\n                Axis axisY = new Axis().setHasLines(true);\n                if (hasAxesNames) {\n                    axisX.setName(\"Axis X\");\n                    axisY.setName(\"Axis Y\");\n                }\n                data.setAxisXBottom(axisX);\n                data.setAxisYLeft(axisY);\n            } else {\n                data.setAxisXBottom(null);\n                data.setAxisYLeft(null);\n            }\n\n            chart.setBubbleChartData(data);\n\n        }\n\n        private void setCircles() {\n            shape = ValueShape.CIRCLE;\n            generateData();\n        }\n\n        private void setSquares() {\n            shape = ValueShape.SQUARE;\n            generateData();\n        }\n\n        private void toggleLabels() {\n            hasLabels = !hasLabels;\n\n            if (hasLabels) {\n                hasLabelForSelected = false;\n                chart.setValueSelectionEnabled(hasLabelForSelected);\n            }\n\n            generateData();\n        }\n\n        private void toggleLabelForSelected() {\n            hasLabelForSelected = !hasLabelForSelected;\n\n            chart.setValueSelectionEnabled(hasLabelForSelected);\n\n            if (hasLabelForSelected) {\n                hasLabels = false;\n            }\n\n            generateData();\n        }\n\n        private void toggleAxes() {\n            hasAxes = !hasAxes;\n\n            generateData();\n        }\n\n        private void toggleAxesNames() {\n            hasAxesNames = !hasAxesNames;\n\n            generateData();\n        }\n\n        /**\n         * To animate values you have to change targets values and then call {@link Chart#startDataAnimation()}\n         * method(don't confuse with View.animate()).\n         */\n        private void prepareDataAnimation() {\n            for (BubbleValue value : data.getValues()) {\n                value.setTarget(value.getX() + (float) Math.random() * 4 * getSign(), (float) Math.random() * 100,\n                        (float) Math.random() * 1000);\n            }\n        }\n\n        private int getSign() {\n            int[] sign = new int[]{-1, 1};\n            return sign[Math.round((float) Math.random())];\n        }\n\n        private class ValueTouchListener implements BubbleChartOnValueSelectListener {\n\n            @Override\n            public void onValueSelected(int bubbleIndex, BubbleValue value) {\n                Toast.makeText(getActivity(), \"Selected: \" + value, Toast.LENGTH_SHORT).show();\n            }\n\n            @Override\n            public void onValueDeselected() {\n                // TODO Auto-generated method stub\n\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "hellocharts-samples/src/lecho/lib/hellocharts/samples/ColumnChartActivity.java",
    "content": "package lecho.lib.hellocharts.samples;\n\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.support.v7.app.ActionBarActivity;\nimport android.view.LayoutInflater;\nimport android.view.Menu;\nimport android.view.MenuInflater;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.Toast;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport lecho.lib.hellocharts.gesture.ZoomType;\nimport lecho.lib.hellocharts.listener.ColumnChartOnValueSelectListener;\nimport lecho.lib.hellocharts.model.Axis;\nimport lecho.lib.hellocharts.model.Column;\nimport lecho.lib.hellocharts.model.ColumnChartData;\nimport lecho.lib.hellocharts.model.SubcolumnValue;\nimport lecho.lib.hellocharts.util.ChartUtils;\nimport lecho.lib.hellocharts.view.Chart;\nimport lecho.lib.hellocharts.view.ColumnChartView;\n\npublic class ColumnChartActivity extends ActionBarActivity {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_column_chart);\n        if (savedInstanceState == null) {\n            getSupportFragmentManager().beginTransaction().add(R.id.container, new PlaceholderFragment()).commit();\n        }\n    }\n\n    /**\n     * A fragment containing a column chart.\n     */\n    public static class PlaceholderFragment extends Fragment {\n\n        private static final int DEFAULT_DATA = 0;\n        private static final int SUBCOLUMNS_DATA = 1;\n        private static final int STACKED_DATA = 2;\n        private static final int NEGATIVE_SUBCOLUMNS_DATA = 3;\n        private static final int NEGATIVE_STACKED_DATA = 4;\n\n        private ColumnChartView chart;\n        private ColumnChartData data;\n        private boolean hasAxes = true;\n        private boolean hasAxesNames = true;\n        private boolean hasLabels = false;\n        private boolean hasLabelForSelected = false;\n        private int dataType = DEFAULT_DATA;\n\n        public PlaceholderFragment() {\n        }\n\n        @Override\n        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n            setHasOptionsMenu(true);\n            View rootView = inflater.inflate(R.layout.fragment_column_chart, container, false);\n\n            chart = (ColumnChartView) rootView.findViewById(R.id.chart);\n            chart.setOnValueTouchListener(new ValueTouchListener());\n\n            generateData();\n\n            return rootView;\n        }\n\n        // MENU\n        @Override\n        public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {\n            inflater.inflate(R.menu.column_chart, menu);\n        }\n\n        @Override\n        public boolean onOptionsItemSelected(MenuItem item) {\n            int id = item.getItemId();\n            if (id == R.id.action_reset) {\n                reset();\n                generateData();\n                return true;\n            }\n            if (id == R.id.action_subcolumns) {\n                dataType = SUBCOLUMNS_DATA;\n                generateData();\n                return true;\n            }\n            if (id == R.id.action_stacked) {\n                dataType = STACKED_DATA;\n                generateData();\n                return true;\n            }\n            if (id == R.id.action_negative_subcolumns) {\n                dataType = NEGATIVE_SUBCOLUMNS_DATA;\n                generateData();\n                return true;\n            }\n            if (id == R.id.action_negative_stacked) {\n                dataType = NEGATIVE_STACKED_DATA;\n                generateData();\n                return true;\n            }\n            if (id == R.id.action_toggle_labels) {\n                toggleLabels();\n                return true;\n            }\n            if (id == R.id.action_toggle_axes) {\n                toggleAxes();\n                return true;\n            }\n            if (id == R.id.action_toggle_axes_names) {\n                toggleAxesNames();\n                return true;\n            }\n            if (id == R.id.action_animate) {\n                prepareDataAnimation();\n                chart.startDataAnimation();\n                return true;\n            }\n            if (id == R.id.action_toggle_selection_mode) {\n                toggleLabelForSelected();\n\n                Toast.makeText(getActivity(),\n                        \"Selection mode set to \" + chart.isValueSelectionEnabled() + \" select any point.\",\n                        Toast.LENGTH_SHORT).show();\n                return true;\n            }\n            if (id == R.id.action_toggle_touch_zoom) {\n                chart.setZoomEnabled(!chart.isZoomEnabled());\n                Toast.makeText(getActivity(), \"IsZoomEnabled \" + chart.isZoomEnabled(), Toast.LENGTH_SHORT).show();\n                return true;\n            }\n            if (id == R.id.action_zoom_both) {\n                chart.setZoomType(ZoomType.HORIZONTAL_AND_VERTICAL);\n                return true;\n            }\n            if (id == R.id.action_zoom_horizontal) {\n                chart.setZoomType(ZoomType.HORIZONTAL);\n                return true;\n            }\n            if (id == R.id.action_zoom_vertical) {\n                chart.setZoomType(ZoomType.VERTICAL);\n                return true;\n            }\n            return super.onOptionsItemSelected(item);\n        }\n\n        private void reset() {\n            hasAxes = true;\n            hasAxesNames = true;\n            hasLabels = false;\n            hasLabelForSelected = false;\n            dataType = DEFAULT_DATA;\n            chart.setValueSelectionEnabled(hasLabelForSelected);\n\n        }\n\n        private void generateDefaultData() {\n            int numSubcolumns = 1;\n            int numColumns = 8;\n            // Column can have many subcolumns, here by default I use 1 subcolumn in each of 8 columns.\n            List<Column> columns = new ArrayList<Column>();\n            List<SubcolumnValue> values;\n            for (int i = 0; i < numColumns; ++i) {\n\n                values = new ArrayList<SubcolumnValue>();\n                for (int j = 0; j < numSubcolumns; ++j) {\n                    values.add(new SubcolumnValue((float) Math.random() * 50f + 5, ChartUtils.pickColor()));\n                }\n\n                Column column = new Column(values);\n                column.setHasLabels(hasLabels);\n                column.setHasLabelsOnlyForSelected(hasLabelForSelected);\n                columns.add(column);\n            }\n\n            data = new ColumnChartData(columns);\n\n            if (hasAxes) {\n                Axis axisX = new Axis();\n                Axis axisY = new Axis().setHasLines(true);\n                if (hasAxesNames) {\n                    axisX.setName(\"Axis X\");\n                    axisY.setName(\"Axis Y\");\n                }\n                data.setAxisXBottom(axisX);\n                data.setAxisYLeft(axisY);\n            } else {\n                data.setAxisXBottom(null);\n                data.setAxisYLeft(null);\n            }\n\n            chart.setColumnChartData(data);\n\n        }\n\n        /**\n         * Generates columns with subcolumns, columns have larger separation than subcolumns.\n         */\n        private void generateSubcolumnsData() {\n            int numSubcolumns = 4;\n            int numColumns = 4;\n            // Column can have many subcolumns, here I use 4 subcolumn in each of 8 columns.\n            List<Column> columns = new ArrayList<Column>();\n            List<SubcolumnValue> values;\n            for (int i = 0; i < numColumns; ++i) {\n\n                values = new ArrayList<SubcolumnValue>();\n                for (int j = 0; j < numSubcolumns; ++j) {\n                    values.add(new SubcolumnValue((float) Math.random() * 50f + 5, ChartUtils.pickColor()));\n                }\n\n                Column column = new Column(values);\n                column.setHasLabels(hasLabels);\n                column.setHasLabelsOnlyForSelected(hasLabelForSelected);\n                columns.add(column);\n            }\n\n            data = new ColumnChartData(columns);\n\n            if (hasAxes) {\n                Axis axisX = new Axis();\n                Axis axisY = new Axis().setHasLines(true);\n                if (hasAxesNames) {\n                    axisX.setName(\"Axis X\");\n                    axisY.setName(\"Axis Y\");\n                }\n                data.setAxisXBottom(axisX);\n                data.setAxisYLeft(axisY);\n            } else {\n                data.setAxisXBottom(null);\n                data.setAxisYLeft(null);\n            }\n\n            chart.setColumnChartData(data);\n\n        }\n\n        /**\n         * Generates columns with stacked subcolumns.\n         */\n        private void generateStackedData() {\n            int numSubcolumns = 4;\n            int numColumns = 8;\n            // Column can have many stacked subcolumns, here I use 4 stacke subcolumn in each of 4 columns.\n            List<Column> columns = new ArrayList<Column>();\n            List<SubcolumnValue> values;\n            for (int i = 0; i < numColumns; ++i) {\n\n                values = new ArrayList<SubcolumnValue>();\n                for (int j = 0; j < numSubcolumns; ++j) {\n                    values.add(new SubcolumnValue((float) Math.random() * 20f + 5, ChartUtils.pickColor()));\n                }\n\n                Column column = new Column(values);\n                column.setHasLabels(hasLabels);\n                column.setHasLabelsOnlyForSelected(hasLabelForSelected);\n                columns.add(column);\n            }\n\n            data = new ColumnChartData(columns);\n\n            // Set stacked flag.\n            data.setStacked(true);\n\n            if (hasAxes) {\n                Axis axisX = new Axis();\n                Axis axisY = new Axis().setHasLines(true);\n                if (hasAxesNames) {\n                    axisX.setName(\"Axis X\");\n                    axisY.setName(\"Axis Y\");\n                }\n                data.setAxisXBottom(axisX);\n                data.setAxisYLeft(axisY);\n            } else {\n                data.setAxisXBottom(null);\n                data.setAxisYLeft(null);\n            }\n\n            chart.setColumnChartData(data);\n        }\n\n        private void generateNegativeSubcolumnsData() {\n\n            int numSubcolumns = 4;\n            int numColumns = 4;\n            List<Column> columns = new ArrayList<Column>();\n            List<SubcolumnValue> values;\n            for (int i = 0; i < numColumns; ++i) {\n\n                values = new ArrayList<SubcolumnValue>();\n                for (int j = 0; j < numSubcolumns; ++j) {\n                    int sign = getSign();\n                    values.add(new SubcolumnValue((float) Math.random() * 50f * sign + 5 * sign, ChartUtils.pickColor\n                            ()));\n                }\n\n                Column column = new Column(values);\n                column.setHasLabels(hasLabels);\n                column.setHasLabelsOnlyForSelected(hasLabelForSelected);\n                columns.add(column);\n            }\n\n            data = new ColumnChartData(columns);\n\n            if (hasAxes) {\n                Axis axisX = new Axis();\n                Axis axisY = new Axis().setHasLines(true);\n                if (hasAxesNames) {\n                    axisX.setName(\"Axis X\");\n                    axisY.setName(\"Axis Y\");\n                }\n                data.setAxisXBottom(axisX);\n                data.setAxisYLeft(axisY);\n            } else {\n                data.setAxisXBottom(null);\n                data.setAxisYLeft(null);\n            }\n\n            chart.setColumnChartData(data);\n        }\n\n        private void generateNegativeStackedData() {\n\n            int numSubcolumns = 4;\n            int numColumns = 8;\n            // Column can have many stacked subcolumns, here I use 4 stacke subcolumn in each of 4 columns.\n            List<Column> columns = new ArrayList<Column>();\n            List<SubcolumnValue> values;\n            for (int i = 0; i < numColumns; ++i) {\n\n                values = new ArrayList<SubcolumnValue>();\n                for (int j = 0; j < numSubcolumns; ++j) {\n                    int sign = getSign();\n                    values.add(new SubcolumnValue((float) Math.random() * 20f * sign + 5 * sign, ChartUtils.pickColor()));\n                }\n\n                Column column = new Column(values);\n                column.setHasLabels(hasLabels);\n                column.setHasLabelsOnlyForSelected(hasLabelForSelected);\n                columns.add(column);\n            }\n\n            data = new ColumnChartData(columns);\n\n            // Set stacked flag.\n            data.setStacked(true);\n\n            if (hasAxes) {\n                Axis axisX = new Axis();\n                Axis axisY = new Axis().setHasLines(true);\n                if (hasAxesNames) {\n                    axisX.setName(\"Axis X\");\n                    axisY.setName(\"Axis Y\");\n                }\n                data.setAxisXBottom(axisX);\n                data.setAxisYLeft(axisY);\n            } else {\n                data.setAxisXBottom(null);\n                data.setAxisYLeft(null);\n            }\n\n            chart.setColumnChartData(data);\n        }\n\n        private int getSign() {\n            int[] sign = new int[]{-1, 1};\n            return sign[Math.round((float) Math.random())];\n        }\n\n        private void generateData() {\n            switch (dataType) {\n                case DEFAULT_DATA:\n                    generateDefaultData();\n                    break;\n                case SUBCOLUMNS_DATA:\n                    generateSubcolumnsData();\n                    break;\n                case STACKED_DATA:\n                    generateStackedData();\n                    break;\n                case NEGATIVE_SUBCOLUMNS_DATA:\n                    generateNegativeSubcolumnsData();\n                    break;\n                case NEGATIVE_STACKED_DATA:\n                    generateNegativeStackedData();\n                    break;\n                default:\n                    generateDefaultData();\n                    break;\n            }\n        }\n\n        private void toggleLabels() {\n            hasLabels = !hasLabels;\n\n            if (hasLabels) {\n                hasLabelForSelected = false;\n                chart.setValueSelectionEnabled(hasLabelForSelected);\n            }\n\n            generateData();\n        }\n\n        private void toggleLabelForSelected() {\n            hasLabelForSelected = !hasLabelForSelected;\n            chart.setValueSelectionEnabled(hasLabelForSelected);\n\n            if (hasLabelForSelected) {\n                hasLabels = false;\n            }\n\n            generateData();\n        }\n\n        private void toggleAxes() {\n            hasAxes = !hasAxes;\n\n            generateData();\n        }\n\n        private void toggleAxesNames() {\n            hasAxesNames = !hasAxesNames;\n\n            generateData();\n        }\n\n        /**\n         * To animate values you have to change targets values and then call {@link Chart#startDataAnimation()}\n         * method(don't confuse with View.animate()).\n         */\n        private void prepareDataAnimation() {\n            for (Column column : data.getColumns()) {\n                for (SubcolumnValue value : column.getValues()) {\n                    value.setTarget((float) Math.random() * 100);\n                }\n            }\n        }\n\n        private class ValueTouchListener implements ColumnChartOnValueSelectListener {\n\n            @Override\n            public void onValueSelected(int columnIndex, int subcolumnIndex, SubcolumnValue value) {\n                Toast.makeText(getActivity(), \"Selected: \" + value, Toast.LENGTH_SHORT).show();\n            }\n\n            @Override\n            public void onValueDeselected() {\n                // TODO Auto-generated method stub\n\n            }\n\n        }\n\n    }\n}\n"
  },
  {
    "path": "hellocharts-samples/src/lecho/lib/hellocharts/samples/ComboLineColumnChartActivity.java",
    "content": "package lecho.lib.hellocharts.samples;\n\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.support.v7.app.ActionBarActivity;\nimport android.view.LayoutInflater;\nimport android.view.Menu;\nimport android.view.MenuInflater;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.Toast;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport lecho.lib.hellocharts.listener.ComboLineColumnChartOnValueSelectListener;\nimport lecho.lib.hellocharts.model.Axis;\nimport lecho.lib.hellocharts.model.Column;\nimport lecho.lib.hellocharts.model.ColumnChartData;\nimport lecho.lib.hellocharts.model.ComboLineColumnChartData;\nimport lecho.lib.hellocharts.model.Line;\nimport lecho.lib.hellocharts.model.LineChartData;\nimport lecho.lib.hellocharts.model.PointValue;\nimport lecho.lib.hellocharts.model.SubcolumnValue;\nimport lecho.lib.hellocharts.util.ChartUtils;\nimport lecho.lib.hellocharts.view.ComboLineColumnChartView;\n\npublic class ComboLineColumnChartActivity extends ActionBarActivity {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_combo_line_column_chart);\n        if (savedInstanceState == null) {\n            getSupportFragmentManager().beginTransaction().add(R.id.container, new PlaceholderFragment()).commit();\n        }\n    }\n\n    /**\n     * A fragment containing a combo line/column chart view.\n     */\n    public static class PlaceholderFragment extends Fragment {\n\n        private ComboLineColumnChartView chart;\n        private ComboLineColumnChartData data;\n\n        private int numberOfLines = 1;\n        private int maxNumberOfLines = 4;\n        private int numberOfPoints = 12;\n\n        float[][] randomNumbersTab = new float[maxNumberOfLines][numberOfPoints];\n\n        private boolean hasAxes = true;\n        private boolean hasAxesNames = true;\n        private boolean hasPoints = true;\n        private boolean hasLines = true;\n        private boolean isCubic = false;\n        private boolean hasLabels = false;\n\n        public PlaceholderFragment() {\n        }\n\n        @Override\n        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n            setHasOptionsMenu(true);\n            View rootView = inflater.inflate(R.layout.fragment_combo_line_column_chart, container, false);\n\n            chart = (ComboLineColumnChartView) rootView.findViewById(R.id.chart);\n            chart.setOnValueTouchListener(new ValueTouchListener());\n\n            generateValues();\n            generateData();\n\n            return rootView;\n        }\n\n        // MENU\n        @Override\n        public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {\n            inflater.inflate(R.menu.combo_line_column_chart, menu);\n        }\n\n        @Override\n        public boolean onOptionsItemSelected(MenuItem item) {\n            int id = item.getItemId();\n            if (id == R.id.action_reset) {\n                reset();\n                generateData();\n                return true;\n            }\n            if (id == R.id.action_add_line) {\n                addLineToData();\n                return true;\n            }\n            if (id == R.id.action_toggle_lines) {\n                toggleLines();\n                return true;\n            }\n            if (id == R.id.action_toggle_points) {\n                togglePoints();\n                return true;\n            }\n            if (id == R.id.action_toggle_cubic) {\n                toggleCubic();\n                return true;\n            }\n            if (id == R.id.action_toggle_labels) {\n                toggleLabels();\n                return true;\n            }\n            if (id == R.id.action_toggle_axes) {\n                toggleAxes();\n                return true;\n            }\n            if (id == R.id.action_toggle_axes_names) {\n                toggleAxesNames();\n                return true;\n            }\n            if (id == R.id.action_animate) {\n                prepareDataAnimation();\n                chart.startDataAnimation();\n                return true;\n            }\n            return super.onOptionsItemSelected(item);\n        }\n\n        private void generateValues() {\n            for (int i = 0; i < maxNumberOfLines; ++i) {\n                for (int j = 0; j < numberOfPoints; ++j) {\n                    randomNumbersTab[i][j] = (float) Math.random() * 50f + 5;\n                }\n            }\n        }\n\n        private void reset() {\n            numberOfLines = 1;\n\n            hasAxes = true;\n            hasAxesNames = true;\n            hasLines = true;\n            hasPoints = true;\n            hasLabels = false;\n            isCubic = false;\n\n        }\n\n        private void generateData() {\n            // Chart looks the best when line data and column data have similar maximum viewports.\n            data = new ComboLineColumnChartData(generateColumnData(), generateLineData());\n\n            if (hasAxes) {\n                Axis axisX = new Axis();\n                Axis axisY = new Axis().setHasLines(true);\n                if (hasAxesNames) {\n                    axisX.setName(\"Axis X\");\n                    axisY.setName(\"Axis Y\");\n                }\n                data.setAxisXBottom(axisX);\n                data.setAxisYLeft(axisY);\n            } else {\n                data.setAxisXBottom(null);\n                data.setAxisYLeft(null);\n            }\n\n            chart.setComboLineColumnChartData(data);\n        }\n\n        private LineChartData generateLineData() {\n\n            List<Line> lines = new ArrayList<Line>();\n            for (int i = 0; i < numberOfLines; ++i) {\n\n                List<PointValue> values = new ArrayList<PointValue>();\n                for (int j = 0; j < numberOfPoints; ++j) {\n                    values.add(new PointValue(j, randomNumbersTab[i][j]));\n                }\n\n                Line line = new Line(values);\n                line.setColor(ChartUtils.COLORS[i]);\n                line.setCubic(isCubic);\n                line.setHasLabels(hasLabels);\n                line.setHasLines(hasLines);\n                line.setHasPoints(hasPoints);\n                lines.add(line);\n            }\n\n            LineChartData lineChartData = new LineChartData(lines);\n\n            return lineChartData;\n\n        }\n\n        private ColumnChartData generateColumnData() {\n            int numSubcolumns = 1;\n            int numColumns = 12;\n            // Column can have many subcolumns, here by default I use 1 subcolumn in each of 8 columns.\n            List<Column> columns = new ArrayList<Column>();\n            List<SubcolumnValue> values;\n            for (int i = 0; i < numColumns; ++i) {\n\n                values = new ArrayList<SubcolumnValue>();\n                for (int j = 0; j < numSubcolumns; ++j) {\n                    values.add(new SubcolumnValue((float) Math.random() * 50 + 5, ChartUtils.COLOR_GREEN));\n                }\n\n                columns.add(new Column(values));\n            }\n\n            ColumnChartData columnChartData = new ColumnChartData(columns);\n            return columnChartData;\n        }\n\n        private void addLineToData() {\n            if (data.getLineChartData().getLines().size() >= maxNumberOfLines) {\n                Toast.makeText(getActivity(), \"Samples app uses max 4 lines!\", Toast.LENGTH_SHORT).show();\n                return;\n            } else {\n                ++numberOfLines;\n            }\n\n            generateData();\n        }\n\n        private void toggleLines() {\n            hasLines = !hasLines;\n\n            generateData();\n        }\n\n        private void togglePoints() {\n            hasPoints = !hasPoints;\n\n            generateData();\n        }\n\n        private void toggleCubic() {\n            isCubic = !isCubic;\n\n            generateData();\n        }\n\n        private void toggleLabels() {\n            hasLabels = !hasLabels;\n\n            generateData();\n        }\n\n        private void toggleAxes() {\n            hasAxes = !hasAxes;\n\n            generateData();\n        }\n\n        private void toggleAxesNames() {\n            hasAxesNames = !hasAxesNames;\n\n            generateData();\n        }\n\n        private void prepareDataAnimation() {\n\n            // Line animations\n            for (Line line : data.getLineChartData().getLines()) {\n                for (PointValue value : line.getValues()) {\n                    // Here I modify target only for Y values but it is OK to modify X targets as well.\n                    value.setTarget(value.getX(), (float) Math.random() * 50 + 5);\n                }\n            }\n\n            // Columns animations\n            for (Column column : data.getColumnChartData().getColumns()) {\n                for (SubcolumnValue value : column.getValues()) {\n                    value.setTarget((float) Math.random() * 50 + 5);\n                }\n            }\n        }\n\n        private class ValueTouchListener implements ComboLineColumnChartOnValueSelectListener {\n\n            @Override\n            public void onValueDeselected() {\n                // TODO Auto-generated method stub\n\n            }\n\n            @Override\n            public void onColumnValueSelected(int columnIndex, int subcolumnIndex, SubcolumnValue value) {\n                Toast.makeText(getActivity(), \"Selected column: \" + value, Toast.LENGTH_SHORT).show();\n            }\n\n            @Override\n            public void onPointValueSelected(int lineIndex, int pointIndex, PointValue value) {\n                Toast.makeText(getActivity(), \"Selected line point: \" + value, Toast.LENGTH_SHORT).show();\n            }\n\n        }\n    }\n}\n"
  },
  {
    "path": "hellocharts-samples/src/lecho/lib/hellocharts/samples/GoodBadChartActivity.java",
    "content": "package lecho.lib.hellocharts.samples;\n\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.support.v7.app.ActionBarActivity;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport lecho.lib.hellocharts.model.Line;\nimport lecho.lib.hellocharts.model.LineChartData;\nimport lecho.lib.hellocharts.model.PointValue;\nimport lecho.lib.hellocharts.model.Viewport;\nimport lecho.lib.hellocharts.util.ChartUtils;\nimport lecho.lib.hellocharts.view.LineChartView;\n\npublic class GoodBadChartActivity extends ActionBarActivity {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_good_bad);\n        if (savedInstanceState == null) {\n            getSupportFragmentManager().beginTransaction().add(R.id.container, new PlaceholderFragment()).commit();\n        }\n    }\n\n    /**\n     * A placeholder fragment containing a simple view.\n     */\n    public static class PlaceholderFragment extends Fragment {\n\n        private LineChartView chart;\n        private LineChartData data;\n\n        public PlaceholderFragment() {\n        }\n\n        @Override\n        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n            View rootView = inflater.inflate(R.layout.fragment_good_bad, container, false);\n\n            chart = (LineChartView) rootView.findViewById(R.id.chart);\n\n            generateDefaultData();\n            chart.setLineChartData(data);\n\n            // Increase viewport height for better look\n            Viewport v = chart.getMaximumViewport();\n            float dy = v.height() * 0.2f;\n            v.inset(0, -dy);\n            chart.setMaximumViewport(v);\n            chart.setCurrentViewport(v);\n\n            return rootView;\n        }\n\n        private void generateDefaultData() {\n\n            // Generate data, every line has 3 points to form filled triangle. Point radius is set to 1 to be almost\n            // invisible but it has to be there because without points there is not labels. Area transparency is set to\n            // 255(full opacity).\n\n            // Important note. This example uses negative values, to properly fill area below 0 chart base value have to\n            // be set to 0. That is default base value but if you want to be sure you can call data.setBaseValue(0)\n            // method.\n\n            Line line;\n            List<PointValue> values;\n            List<Line> lines = new ArrayList<Line>();\n\n            // First good triangle\n            values = new ArrayList<PointValue>();\n            values.add(new PointValue(0, 0).setLabel(\"\".toCharArray()));\n            values.add(new PointValue(1, 1).setLabel(\"Very Good:)\".toCharArray()));\n            values.add(new PointValue(2, 0).setLabel(\"\".toCharArray()));\n\n            line = new Line(values);\n            line.setColor(ChartUtils.COLOR_GREEN);\n            line.setAreaTransparency(255);\n            line.setFilled(true);\n            line.setPointRadius(1);\n            line.setHasLabels(true);\n            lines.add(line);\n\n            // Second good triangle\n            values = new ArrayList<PointValue>();\n            values.add(new PointValue(3, 0).setLabel(\"\".toCharArray()));\n            values.add(new PointValue(4, 0.5f).setLabel(\"Good Enough\".toCharArray()));\n            values.add(new PointValue(5, 0).setLabel(\"\".toCharArray()));\n\n            line = new Line(values);\n            line.setColor(ChartUtils.COLOR_GREEN);\n            line.setAreaTransparency(255);\n            line.setFilled(true);\n            line.setPointRadius(1);\n            line.setHasLabels(true);\n            lines.add(line);\n\n            // Bad triangle\n            values = new ArrayList<PointValue>();\n            values.add(new PointValue(1, 0).setLabel(\"\".toCharArray()));\n            values.add(new PointValue(2, -1).setLabel(\"Very Bad\".toCharArray()));\n            values.add(new PointValue(3, 0).setLabel(\"\".toCharArray()));\n\n            line = new Line(values);\n            line.setColor(ChartUtils.COLOR_RED);\n            line.setAreaTransparency(255);\n            line.setFilled(true);\n            line.setPointRadius(1);\n            line.setHasLabels(true);\n            lines.add(line);\n\n            data = new LineChartData(lines);\n\n            // *** Important, set base value to 0 to fill negative part of chart.\n            // data.setBaseValue(0);\n\n        }\n    }\n}\n"
  },
  {
    "path": "hellocharts-samples/src/lecho/lib/hellocharts/samples/LineChartActivity.java",
    "content": "package lecho.lib.hellocharts.samples;\n\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.support.v7.app.ActionBarActivity;\nimport android.view.LayoutInflater;\nimport android.view.Menu;\nimport android.view.MenuInflater;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.Toast;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport lecho.lib.hellocharts.animation.ChartAnimationListener;\nimport lecho.lib.hellocharts.gesture.ZoomType;\nimport lecho.lib.hellocharts.listener.LineChartOnValueSelectListener;\nimport lecho.lib.hellocharts.model.Axis;\nimport lecho.lib.hellocharts.model.Line;\nimport lecho.lib.hellocharts.model.LineChartData;\nimport lecho.lib.hellocharts.model.PointValue;\nimport lecho.lib.hellocharts.model.ValueShape;\nimport lecho.lib.hellocharts.model.Viewport;\nimport lecho.lib.hellocharts.util.ChartUtils;\nimport lecho.lib.hellocharts.view.Chart;\nimport lecho.lib.hellocharts.view.LineChartView;\n\npublic class LineChartActivity extends ActionBarActivity {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_line_chart);\n        if (savedInstanceState == null) {\n            getSupportFragmentManager().beginTransaction().add(R.id.container, new PlaceholderFragment()).commit();\n        }\n    }\n\n    /**\n     * A fragment containing a line chart.\n     */\n    public static class PlaceholderFragment extends Fragment {\n\n        private LineChartView chart;\n        private LineChartData data;\n        private int numberOfLines = 1;\n        private int maxNumberOfLines = 4;\n        private int numberOfPoints = 12;\n\n        float[][] randomNumbersTab = new float[maxNumberOfLines][numberOfPoints];\n\n        private boolean hasAxes = true;\n        private boolean hasAxesNames = true;\n        private boolean hasLines = true;\n        private boolean hasPoints = true;\n        private ValueShape shape = ValueShape.CIRCLE;\n        private boolean isFilled = false;\n        private boolean hasLabels = false;\n        private boolean isCubic = false;\n        private boolean hasLabelForSelected = false;\n        private boolean pointsHaveDifferentColor;\n        private boolean hasGradientToTransparent = false;\n\n        public PlaceholderFragment() {\n        }\n\n        @Override\n        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n            setHasOptionsMenu(true);\n            View rootView = inflater.inflate(R.layout.fragment_line_chart, container, false);\n\n            chart = (LineChartView) rootView.findViewById(R.id.chart);\n            chart.setOnValueTouchListener(new ValueTouchListener());\n\n            // Generate some random values.\n            generateValues();\n\n            generateData();\n\n            // Disable viewport recalculations, see toggleCubic() method for more info.\n            chart.setViewportCalculationEnabled(false);\n\n            resetViewport();\n\n            return rootView;\n        }\n\n        // MENU\n        @Override\n        public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {\n            inflater.inflate(R.menu.line_chart, menu);\n        }\n\n        @Override\n        public boolean onOptionsItemSelected(MenuItem item) {\n            int id = item.getItemId();\n            if (id == R.id.action_reset) {\n                reset();\n                generateData();\n                return true;\n            }\n            if (id == R.id.action_add_line) {\n                addLineToData();\n                return true;\n            }\n            if (id == R.id.action_toggle_lines) {\n                toggleLines();\n                return true;\n            }\n            if (id == R.id.action_toggle_points) {\n                togglePoints();\n                return true;\n            }\n            if (id == R.id.action_toggle_gradient) {\n                toggleGradient();\n                return true;\n            }\n            if (id == R.id.action_toggle_cubic) {\n                toggleCubic();\n                return true;\n            }\n            if (id == R.id.action_toggle_area) {\n                toggleFilled();\n                return true;\n            }\n            if (id == R.id.action_point_color) {\n                togglePointColor();\n                return true;\n            }\n            if (id == R.id.action_shape_circles) {\n                setCircles();\n                return true;\n            }\n            if (id == R.id.action_shape_square) {\n                setSquares();\n                return true;\n            }\n            if (id == R.id.action_shape_diamond) {\n                setDiamonds();\n                return true;\n            }\n            if (id == R.id.action_toggle_labels) {\n                toggleLabels();\n                return true;\n            }\n            if (id == R.id.action_toggle_axes) {\n                toggleAxes();\n                return true;\n            }\n            if (id == R.id.action_toggle_axes_names) {\n                toggleAxesNames();\n                return true;\n            }\n            if (id == R.id.action_animate) {\n                prepareDataAnimation();\n                chart.startDataAnimation();\n                return true;\n            }\n            if (id == R.id.action_toggle_selection_mode) {\n                toggleLabelForSelected();\n\n                Toast.makeText(getActivity(),\n                        \"Selection mode set to \" + chart.isValueSelectionEnabled() + \" select any point.\",\n                        Toast.LENGTH_SHORT).show();\n                return true;\n            }\n            if (id == R.id.action_toggle_touch_zoom) {\n                chart.setZoomEnabled(!chart.isZoomEnabled());\n                Toast.makeText(getActivity(), \"IsZoomEnabled \" + chart.isZoomEnabled(), Toast.LENGTH_SHORT).show();\n                return true;\n            }\n            if (id == R.id.action_zoom_both) {\n                chart.setZoomType(ZoomType.HORIZONTAL_AND_VERTICAL);\n                return true;\n            }\n            if (id == R.id.action_zoom_horizontal) {\n                chart.setZoomType(ZoomType.HORIZONTAL);\n                return true;\n            }\n            if (id == R.id.action_zoom_vertical) {\n                chart.setZoomType(ZoomType.VERTICAL);\n                return true;\n            }\n            return super.onOptionsItemSelected(item);\n        }\n\n        private void generateValues() {\n            for (int i = 0; i < maxNumberOfLines; ++i) {\n                for (int j = 0; j < numberOfPoints; ++j) {\n                    randomNumbersTab[i][j] = (float) Math.random() * 100f;\n                }\n            }\n        }\n\n        private void reset() {\n            numberOfLines = 1;\n\n            hasAxes = true;\n            hasAxesNames = true;\n            hasLines = true;\n            hasPoints = true;\n            shape = ValueShape.CIRCLE;\n            isFilled = false;\n            hasLabels = false;\n            isCubic = false;\n            hasLabelForSelected = false;\n            pointsHaveDifferentColor = false;\n\n            chart.setValueSelectionEnabled(hasLabelForSelected);\n            resetViewport();\n        }\n\n        private void resetViewport() {\n            // Reset viewport height range to (0,100)\n            final Viewport v = new Viewport(chart.getMaximumViewport());\n            v.bottom = 0;\n            v.top = 100;\n            v.left = 0;\n            v.right = numberOfPoints - 1;\n            chart.setMaximumViewport(v);\n            chart.setCurrentViewport(v);\n        }\n\n        private void generateData() {\n\n            List<Line> lines = new ArrayList<Line>();\n            for (int i = 0; i < numberOfLines; ++i) {\n\n                List<PointValue> values = new ArrayList<PointValue>();\n                for (int j = 0; j < numberOfPoints; ++j) {\n                    values.add(new PointValue(j, randomNumbersTab[i][j]));\n                }\n\n                Line line = new Line(values);\n                line.setColor(ChartUtils.COLORS[i]);\n                line.setShape(shape);\n                line.setCubic(isCubic);\n                line.setFilled(isFilled);\n                line.setHasLabels(hasLabels);\n                line.setHasLabelsOnlyForSelected(hasLabelForSelected);\n                line.setHasLines(hasLines);\n                line.setHasPoints(hasPoints);\n                line.setHasGradientToTransparent(hasGradientToTransparent);\n                if (pointsHaveDifferentColor){\n                    line.setPointColor(ChartUtils.COLORS[(i + 1) % ChartUtils.COLORS.length]);\n                }\n                lines.add(line);\n            }\n\n            data = new LineChartData(lines);\n\n            if (hasAxes) {\n                Axis axisX = new Axis();\n                Axis axisY = new Axis().setHasLines(true);\n                if (hasAxesNames) {\n                    axisX.setName(\"Axis X\");\n                    axisY.setName(\"Axis Y\");\n                }\n                data.setAxisXBottom(axisX);\n                data.setAxisYLeft(axisY);\n            } else {\n                data.setAxisXBottom(null);\n                data.setAxisYLeft(null);\n            }\n\n            data.setBaseValue(Float.NEGATIVE_INFINITY);\n            chart.setLineChartData(data);\n\n        }\n\n        /**\n         * Adds lines to data, after that data should be set again with\n         * {@link LineChartView#setLineChartData(LineChartData)}. Last 4th line has non-monotonically x values.\n         */\n        private void addLineToData() {\n            if (data.getLines().size() >= maxNumberOfLines) {\n                Toast.makeText(getActivity(), \"Samples app uses max 4 lines!\", Toast.LENGTH_SHORT).show();\n                return;\n            } else {\n                ++numberOfLines;\n            }\n\n            generateData();\n        }\n\n        private void toggleLines() {\n            hasLines = !hasLines;\n\n            generateData();\n        }\n\n        private void togglePoints() {\n            hasPoints = !hasPoints;\n\n            generateData();\n        }\n\n        private void toggleGradient() {\n            hasGradientToTransparent = !hasGradientToTransparent;\n\n            generateData();\n        }\n\n        private void toggleCubic() {\n            isCubic = !isCubic;\n\n            generateData();\n\n            if (isCubic) {\n                // It is good idea to manually set a little higher max viewport for cubic lines because sometimes line\n                // go above or below max/min. To do that use Viewport.inest() method and pass negative value as dy\n                // parameter or just set top and bottom values manually.\n                // In this example I know that Y values are within (0,100) range so I set viewport height range manually\n                // to (-5, 105).\n                // To make this works during animations you should use Chart.setViewportCalculationEnabled(false) before\n                // modifying viewport.\n                // Remember to set viewport after you call setLineChartData().\n                final Viewport v = new Viewport(chart.getMaximumViewport());\n                v.bottom = -5;\n                v.top = 105;\n                // You have to set max and current viewports separately.\n                chart.setMaximumViewport(v);\n                // I changing current viewport with animation in this case.\n                chart.setCurrentViewportWithAnimation(v);\n            } else {\n                // If not cubic restore viewport to (0,100) range.\n                final Viewport v = new Viewport(chart.getMaximumViewport());\n                v.bottom = 0;\n                v.top = 100;\n\n                // You have to set max and current viewports separately.\n                // In this case, if I want animation I have to set current viewport first and use animation listener.\n                // Max viewport will be set in onAnimationFinished method.\n                chart.setViewportAnimationListener(new ChartAnimationListener() {\n\n                    @Override\n                    public void onAnimationStarted() {\n                        // TODO Auto-generated method stub\n\n                    }\n\n                    @Override\n                    public void onAnimationFinished() {\n                        // Set max viewpirt and remove listener.\n                        chart.setMaximumViewport(v);\n                        chart.setViewportAnimationListener(null);\n\n                    }\n                });\n                // Set current viewpirt with animation;\n                chart.setCurrentViewportWithAnimation(v);\n            }\n\n        }\n\n        private void toggleFilled() {\n            isFilled = !isFilled;\n\n            generateData();\n        }\n\n        private void togglePointColor() {\n            pointsHaveDifferentColor = !pointsHaveDifferentColor;\n\n            generateData();\n        }\n\n        private void setCircles() {\n            shape = ValueShape.CIRCLE;\n\n            generateData();\n        }\n\n        private void setSquares() {\n            shape = ValueShape.SQUARE;\n\n            generateData();\n        }\n\n        private void setDiamonds() {\n            shape = ValueShape.DIAMOND;\n\n            generateData();\n        }\n\n        private void toggleLabels() {\n            hasLabels = !hasLabels;\n\n            if (hasLabels) {\n                hasLabelForSelected = false;\n                chart.setValueSelectionEnabled(hasLabelForSelected);\n            }\n\n            generateData();\n        }\n\n        private void toggleLabelForSelected() {\n            hasLabelForSelected = !hasLabelForSelected;\n\n            chart.setValueSelectionEnabled(hasLabelForSelected);\n\n            if (hasLabelForSelected) {\n                hasLabels = false;\n            }\n\n            generateData();\n        }\n\n        private void toggleAxes() {\n            hasAxes = !hasAxes;\n\n            generateData();\n        }\n\n        private void toggleAxesNames() {\n            hasAxesNames = !hasAxesNames;\n\n            generateData();\n        }\n\n        /**\n         * To animate values you have to change targets values and then call {@link Chart#startDataAnimation()}\n         * method(don't confuse with View.animate()). If you operate on data that was set before you don't have to call\n         * {@link LineChartView#setLineChartData(LineChartData)} again.\n         */\n        private void prepareDataAnimation() {\n            for (Line line : data.getLines()) {\n                for (PointValue value : line.getValues()) {\n                    // Here I modify target only for Y values but it is OK to modify X targets as well.\n                    value.setTarget(value.getX(), (float) Math.random() * 100);\n                }\n            }\n        }\n\n        private class ValueTouchListener implements LineChartOnValueSelectListener {\n\n            @Override\n            public void onValueSelected(int lineIndex, int pointIndex, PointValue value) {\n                Toast.makeText(getActivity(), \"Selected: \" + value, Toast.LENGTH_SHORT).show();\n            }\n\n            @Override\n            public void onValueDeselected() {\n                // TODO Auto-generated method stub\n\n            }\n\n        }\n    }\n}\n"
  },
  {
    "path": "hellocharts-samples/src/lecho/lib/hellocharts/samples/LineColumnDependencyActivity.java",
    "content": "package lecho.lib.hellocharts.samples;\n\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.support.v7.app.ActionBarActivity;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport lecho.lib.hellocharts.gesture.ZoomType;\nimport lecho.lib.hellocharts.listener.ColumnChartOnValueSelectListener;\nimport lecho.lib.hellocharts.model.Axis;\nimport lecho.lib.hellocharts.model.AxisValue;\nimport lecho.lib.hellocharts.model.Column;\nimport lecho.lib.hellocharts.model.ColumnChartData;\nimport lecho.lib.hellocharts.model.Line;\nimport lecho.lib.hellocharts.model.LineChartData;\nimport lecho.lib.hellocharts.model.PointValue;\nimport lecho.lib.hellocharts.model.SubcolumnValue;\nimport lecho.lib.hellocharts.model.Viewport;\nimport lecho.lib.hellocharts.util.ChartUtils;\nimport lecho.lib.hellocharts.view.ColumnChartView;\nimport lecho.lib.hellocharts.view.LineChartView;\n\npublic class LineColumnDependencyActivity extends ActionBarActivity {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_line_column_dependency);\n        if (savedInstanceState == null) {\n            getSupportFragmentManager().beginTransaction().add(R.id.container, new PlaceholderFragment()).commit();\n        }\n    }\n\n    /**\n     * A placeholder fragment containing a simple view.\n     */\n    public static class PlaceholderFragment extends Fragment {\n        public final static String[] months = new String[]{\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\",\n                \"Sep\", \"Oct\", \"Nov\", \"Dec\",};\n\n        public final static String[] days = new String[]{\"Mon\", \"Tue\", \"Wen\", \"Thu\", \"Fri\", \"Sat\", \"Sun\",};\n\n        private LineChartView chartTop;\n        private ColumnChartView chartBottom;\n\n        private LineChartData lineData;\n        private ColumnChartData columnData;\n\n        public PlaceholderFragment() {\n        }\n\n        @Override\n        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n            View rootView = inflater.inflate(R.layout.fragment_line_column_dependency, container, false);\n\n            // *** TOP LINE CHART ***\n            chartTop = (LineChartView) rootView.findViewById(R.id.chart_top);\n\n            // Generate and set data for line chart\n            generateInitialLineData();\n\n            // *** BOTTOM COLUMN CHART ***\n\n            chartBottom = (ColumnChartView) rootView.findViewById(R.id.chart_bottom);\n\n            generateColumnData();\n\n            return rootView;\n        }\n\n        private void generateColumnData() {\n\n            int numSubcolumns = 1;\n            int numColumns = months.length;\n\n            List<AxisValue> axisValues = new ArrayList<AxisValue>();\n            List<Column> columns = new ArrayList<Column>();\n            List<SubcolumnValue> values;\n            for (int i = 0; i < numColumns; ++i) {\n\n                values = new ArrayList<SubcolumnValue>();\n                for (int j = 0; j < numSubcolumns; ++j) {\n                    values.add(new SubcolumnValue((float) Math.random() * 50f + 5, ChartUtils.pickColor()));\n                }\n\n                axisValues.add(new AxisValue(i).setLabel(months[i]));\n\n                columns.add(new Column(values).setHasLabelsOnlyForSelected(true));\n            }\n\n            columnData = new ColumnChartData(columns);\n\n            columnData.setAxisXBottom(new Axis(axisValues).setHasLines(true));\n            columnData.setAxisYLeft(new Axis().setHasLines(true).setMaxLabelChars(2));\n\n            chartBottom.setColumnChartData(columnData);\n\n            // Set value touch listener that will trigger changes for chartTop.\n            chartBottom.setOnValueTouchListener(new ValueTouchListener());\n\n            // Set selection mode to keep selected month column highlighted.\n            chartBottom.setValueSelectionEnabled(true);\n\n            chartBottom.setZoomType(ZoomType.HORIZONTAL);\n\n            // chartBottom.setOnClickListener(new View.OnClickListener() {\n            //\n            // @Override\n            // public void onClick(View v) {\n            // SelectedValue sv = chartBottom.getSelectedValue();\n            // if (!sv.isSet()) {\n            // generateInitialLineData();\n            // }\n            //\n            // }\n            // });\n\n        }\n\n        /**\n         * Generates initial data for line chart. At the begining all Y values are equals 0. That will change when user\n         * will select value on column chart.\n         */\n        private void generateInitialLineData() {\n            int numValues = 7;\n\n            List<AxisValue> axisValues = new ArrayList<AxisValue>();\n            List<PointValue> values = new ArrayList<PointValue>();\n            for (int i = 0; i < numValues; ++i) {\n                values.add(new PointValue(i, 0));\n                axisValues.add(new AxisValue(i).setLabel(days[i]));\n            }\n\n            Line line = new Line(values);\n            line.setColor(ChartUtils.COLOR_GREEN).setCubic(true);\n\n            List<Line> lines = new ArrayList<Line>();\n            lines.add(line);\n\n            lineData = new LineChartData(lines);\n            lineData.setAxisXBottom(new Axis(axisValues).setHasLines(true));\n            lineData.setAxisYLeft(new Axis().setHasLines(true).setMaxLabelChars(3));\n\n            chartTop.setLineChartData(lineData);\n\n            // For build-up animation you have to disable viewport recalculation.\n            chartTop.setViewportCalculationEnabled(false);\n\n            // And set initial max viewport and current viewport- remember to set viewports after data.\n            Viewport v = new Viewport(0, 110, 6, 0);\n            chartTop.setMaximumViewport(v);\n            chartTop.setCurrentViewport(v);\n\n            chartTop.setZoomType(ZoomType.HORIZONTAL);\n        }\n\n        private void generateLineData(int color, float range) {\n            // Cancel last animation if not finished.\n            chartTop.cancelDataAnimation();\n\n            // Modify data targets\n            Line line = lineData.getLines().get(0);// For this example there is always only one line.\n            line.setColor(color);\n            for (PointValue value : line.getValues()) {\n                // Change target only for Y value.\n                value.setTarget(value.getX(), (float) Math.random() * range);\n            }\n\n            // Start new data animation with 300ms duration;\n            chartTop.startDataAnimation(300);\n        }\n\n        private class ValueTouchListener implements ColumnChartOnValueSelectListener {\n\n            @Override\n            public void onValueSelected(int columnIndex, int subcolumnIndex, SubcolumnValue value) {\n                generateLineData(value.getColor(), 100);\n            }\n\n            @Override\n            public void onValueDeselected() {\n\n                generateLineData(ChartUtils.COLOR_GREEN, 0);\n\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "hellocharts-samples/src/lecho/lib/hellocharts/samples/MainActivity.java",
    "content": "package lecho.lib.hellocharts.samples;\n\nimport android.content.Context;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.support.v7.app.ActionBarActivity;\nimport android.view.LayoutInflater;\nimport android.view.Menu;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.AdapterView;\nimport android.widget.AdapterView.OnItemClickListener;\nimport android.widget.ArrayAdapter;\nimport android.widget.FrameLayout;\nimport android.widget.ListView;\nimport android.widget.TextView;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport lecho.lib.hellocharts.view.AbstractChartView;\nimport lecho.lib.hellocharts.view.BubbleChartView;\nimport lecho.lib.hellocharts.view.ColumnChartView;\nimport lecho.lib.hellocharts.view.LineChartView;\nimport lecho.lib.hellocharts.view.PieChartView;\nimport lecho.lib.hellocharts.view.PreviewColumnChartView;\nimport lecho.lib.hellocharts.view.PreviewLineChartView;\n\npublic class MainActivity extends ActionBarActivity {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_main);\n        if (savedInstanceState == null) {\n            getSupportFragmentManager().beginTransaction().add(R.id.container, new PlaceholderFragment()).commit();\n        }\n    }\n\n    @Override\n    public boolean onCreateOptionsMenu(Menu menu) {\n        // Inflate the menu; this adds items to the action bar if it is present.\n        getMenuInflater().inflate(R.menu.main, menu);\n        return true;\n    }\n\n    @Override\n    public boolean onOptionsItemSelected(MenuItem item) {\n        // Handle action bar item clicks here. The action bar will\n        // automatically handle clicks on the Home/Up button, so long\n        // as you specify a parent activity in AndroidManifest.xml.\n        int id = item.getItemId();\n        if (id == R.id.action_about) {\n            Intent intent = new Intent(this, AboutActivity.class);\n            startActivity(intent);\n            return true;\n        }\n        return super.onOptionsItemSelected(item);\n    }\n\n    public enum ChartType {\n        LINE_CHART, COLUMN_CHART, PIE_CHART, BUBBLE_CHART, PREVIEW_LINE_CHART, PREVIEW_COLUMN_CHART, OTHER\n    }\n\n    /**\n     * A placeholder fragment containing a simple view.\n     */\n    public static class PlaceholderFragment extends Fragment implements OnItemClickListener {\n\n        private ListView listView;\n        private ChartSamplesAdapter adapter;\n\n        public PlaceholderFragment() {\n        }\n\n        @Override\n        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n            View rootView = inflater.inflate(R.layout.fragment_main, container, false);\n            listView = (ListView) rootView.findViewById(android.R.id.list);\n            adapter = new ChartSamplesAdapter(getActivity(), 0, generateSamplesDescriptions());\n            listView.setAdapter(adapter);\n            listView.setOnItemClickListener(this);\n            return rootView;\n        }\n\n        @Override\n        public void onItemClick(AdapterView<?> adapter, View view, int position, long id) {\n            Intent intent;\n\n            switch (position) {\n                case 0:\n                    // Line Chart;\n                    intent = new Intent(getActivity(), LineChartActivity.class);\n                    startActivity(intent);\n                    break;\n                case 1:\n                    // Column Chart;\n                    intent = new Intent(getActivity(), ColumnChartActivity.class);\n                    startActivity(intent);\n                    break;\n                case 2:\n                    // Pie Chart;\n                    intent = new Intent(getActivity(), PieChartActivity.class);\n                    startActivity(intent);\n                    break;\n                case 3:\n                    // Bubble Chart;\n                    intent = new Intent(getActivity(), BubbleChartActivity.class);\n                    startActivity(intent);\n                    break;\n                case 4:\n                    // Preview Line Chart;\n                    intent = new Intent(getActivity(), PreviewLineChartActivity.class);\n                    startActivity(intent);\n                    break;\n                case 5:\n                    // Preview Column Chart;\n                    intent = new Intent(getActivity(), PreviewColumnChartActivity.class);\n                    startActivity(intent);\n                    break;\n                case 6:\n                    // Combo Chart;\n                    intent = new Intent(getActivity(), ComboLineColumnChartActivity.class);\n                    startActivity(intent);\n                    break;\n                case 7:\n                    // Line Column Dependency;\n                    intent = new Intent(getActivity(), LineColumnDependencyActivity.class);\n                    startActivity(intent);\n                    break;\n                case 8:\n                    // Tempo line chart;\n                    intent = new Intent(getActivity(), TempoChartActivity.class);\n                    startActivity(intent);\n                    break;\n                case 9:\n                    // Speed line chart;\n                    intent = new Intent(getActivity(), SpeedChartActivity.class);\n                    startActivity(intent);\n                    break;\n                case 10:\n                    // Good Bad filled line chart;\n                    intent = new Intent(getActivity(), GoodBadChartActivity.class);\n                    startActivity(intent);\n                    break;\n                case 11:\n                    // Good Bad filled line chart;\n                    intent = new Intent(getActivity(), ViewPagerChartsActivity.class);\n                    startActivity(intent);\n                    break;\n                default:\n                    break;\n            }\n        }\n\n        private List<ChartSampleDescription> generateSamplesDescriptions() {\n            List<ChartSampleDescription> list = new ArrayList<MainActivity.ChartSampleDescription>();\n\n            list.add(new ChartSampleDescription(\"Line Chart\", \"\", ChartType.LINE_CHART));\n            list.add(new ChartSampleDescription(\"Column Chart\", \"\", ChartType.COLUMN_CHART));\n            list.add(new ChartSampleDescription(\"Pie Chart\", \"\", ChartType.PIE_CHART));\n            list.add(new ChartSampleDescription(\"Bubble Chart\", \"\", ChartType.BUBBLE_CHART));\n            list.add(new ChartSampleDescription(\"Preview Line Chart\",\n                    \"Control line chart viewport with another line chart.\", ChartType.PREVIEW_LINE_CHART));\n            list.add(new ChartSampleDescription(\"Preview Column Chart\",\n                    \"Control column chart viewport with another column chart.\", ChartType.PREVIEW_COLUMN_CHART));\n            list.add(new ChartSampleDescription(\"Combo Line/Column Chart\", \"Combo chart with lines and columns.\",\n                    ChartType.OTHER));\n            list.add(new ChartSampleDescription(\"Line/Column Chart Dependency\",\n                    \"LineChart responds(with animation) to column chart value selection.\", ChartType.OTHER));\n            list.add(new ChartSampleDescription(\n                    \"Tempo Chart\",\n                    \"Presents tempo and height values on a signle chart. Example of multiple axes and reverted Y axis\" +\n                            \" with time format [mm:ss].\",\n                    ChartType.OTHER));\n            list.add(new ChartSampleDescription(\"Speed Chart\",\n                    \"Presents speed and height values on a signle chart. Exapmle of multiple axes inside chart area.\",\n                    ChartType.OTHER));\n            list.add(new ChartSampleDescription(\"Good/Bad Chart\",\n                    \"Example of filled area line chart with custom labels\", ChartType.OTHER));\n            list.add(new ChartSampleDescription(\"ViewPager with Charts\",\n                    \"Interactive charts within ViewPager. Each chart can be zoom/scroll except pie chart.\",\n                    ChartType.OTHER));\n\n            return list;\n        }\n    }\n\n    public static class ChartSamplesAdapter extends ArrayAdapter<ChartSampleDescription> {\n\n        public ChartSamplesAdapter(Context context, int resource, List<ChartSampleDescription> objects) {\n            super(context, resource, objects);\n        }\n\n        @Override\n        public View getView(int position, View convertView, ViewGroup parent) {\n            ViewHolder holder;\n\n            if (convertView == null) {\n                convertView = View.inflate(getContext(), R.layout.list_item_sample, null);\n\n                holder = new ViewHolder();\n                holder.text1 = (TextView) convertView.findViewById(R.id.text1);\n                holder.text2 = (TextView) convertView.findViewById(R.id.text2);\n                holder.chartLayout = (FrameLayout) convertView.findViewById(R.id.chart_layout);\n\n                convertView.setTag(holder);\n            } else {\n                holder = (ViewHolder) convertView.getTag();\n            }\n\n            ChartSampleDescription item = getItem(position);\n\n            holder.chartLayout.setVisibility(View.VISIBLE);\n            holder.chartLayout.removeAllViews();\n            AbstractChartView chart;\n            switch (item.chartType) {\n                case LINE_CHART:\n                    chart = new LineChartView(getContext());\n                    holder.chartLayout.addView(chart);\n                    break;\n                case COLUMN_CHART:\n                    chart = new ColumnChartView(getContext());\n                    holder.chartLayout.addView(chart);\n                    break;\n                case PIE_CHART:\n                    chart = new PieChartView(getContext());\n                    holder.chartLayout.addView(chart);\n                    break;\n                case BUBBLE_CHART:\n                    chart = new BubbleChartView(getContext());\n                    holder.chartLayout.addView(chart);\n                    break;\n                case PREVIEW_LINE_CHART:\n                    chart = new PreviewLineChartView(getContext());\n                    holder.chartLayout.addView(chart);\n                    break;\n                case PREVIEW_COLUMN_CHART:\n                    chart = new PreviewColumnChartView(getContext());\n                    holder.chartLayout.addView(chart);\n                    break;\n                default:\n                    chart = null;\n                    holder.chartLayout.setVisibility(View.GONE);\n                    break;\n            }\n\n            if (null != chart) {\n                chart.setInteractive(false);// Disable touch handling for chart on the ListView.\n            }\n            holder.text1.setText(item.text1);\n            holder.text2.setText(item.text2);\n\n            return convertView;\n        }\n\n        private class ViewHolder {\n\n            TextView text1;\n            TextView text2;\n            FrameLayout chartLayout;\n        }\n\n    }\n\n    public static class ChartSampleDescription {\n        String text1;\n        String text2;\n        ChartType chartType;\n\n        public ChartSampleDescription(String text1, String text2, ChartType chartType) {\n            this.text1 = text1;\n            this.text2 = text2;\n            this.chartType = chartType;\n        }\n    }\n\n}\n"
  },
  {
    "path": "hellocharts-samples/src/lecho/lib/hellocharts/samples/PieChartActivity.java",
    "content": "package lecho.lib.hellocharts.samples;\n\nimport android.graphics.Typeface;\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.support.v7.app.ActionBarActivity;\nimport android.view.LayoutInflater;\nimport android.view.Menu;\nimport android.view.MenuInflater;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.Toast;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport lecho.lib.hellocharts.listener.PieChartOnValueSelectListener;\nimport lecho.lib.hellocharts.model.PieChartData;\nimport lecho.lib.hellocharts.model.SliceValue;\nimport lecho.lib.hellocharts.util.ChartUtils;\nimport lecho.lib.hellocharts.view.Chart;\nimport lecho.lib.hellocharts.view.PieChartView;\n\npublic class PieChartActivity extends ActionBarActivity {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_pie_chart);\n        if (savedInstanceState == null) {\n            getSupportFragmentManager().beginTransaction().add(R.id.container, new PlaceholderFragment()).commit();\n        }\n    }\n\n    /**\n     * A fragment containing a pie chart.\n     */\n    public static class PlaceholderFragment extends Fragment {\n\n        private PieChartView chart;\n        private PieChartData data;\n\n        private boolean hasLabels = false;\n        private boolean hasLabelsOutside = false;\n        private boolean hasCenterCircle = false;\n        private boolean hasCenterText1 = false;\n        private boolean hasCenterText2 = false;\n        private boolean isExploded = false;\n        private boolean hasLabelForSelected = false;\n\n        public PlaceholderFragment() {\n        }\n\n        @Override\n        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n            setHasOptionsMenu(true);\n            View rootView = inflater.inflate(R.layout.fragment_pie_chart, container, false);\n\n            chart = (PieChartView) rootView.findViewById(R.id.chart);\n            chart.setOnValueTouchListener(new ValueTouchListener());\n\n            generateData();\n\n            return rootView;\n        }\n\n        // MENU\n        @Override\n        public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {\n            inflater.inflate(R.menu.pie_chart, menu);\n        }\n\n        @Override\n        public boolean onOptionsItemSelected(MenuItem item) {\n            int id = item.getItemId();\n            if (id == R.id.action_reset) {\n                reset();\n                generateData();\n                return true;\n            }\n            if (id == R.id.action_explode) {\n                explodeChart();\n                return true;\n            }\n            if (id == R.id.action_center_circle) {\n                hasCenterCircle = !hasCenterCircle;\n                if (!hasCenterCircle) {\n                    hasCenterText1 = false;\n                    hasCenterText2 = false;\n                }\n\n                generateData();\n                return true;\n            }\n            if (id == R.id.action_center_text1) {\n                hasCenterText1 = !hasCenterText1;\n\n                if (hasCenterText1) {\n                    hasCenterCircle = true;\n                }\n\n                hasCenterText2 = false;\n\n                generateData();\n                return true;\n            }\n            if (id == R.id.action_center_text2) {\n                hasCenterText2 = !hasCenterText2;\n\n                if (hasCenterText2) {\n                    hasCenterText1 = true;// text 2 need text 1 to by also drawn.\n                    hasCenterCircle = true;\n                }\n\n                generateData();\n                return true;\n            }\n            if (id == R.id.action_toggle_labels) {\n                toggleLabels();\n                return true;\n            }\n            if (id == R.id.action_toggle_labels_outside) {\n                toggleLabelsOutside();\n                return true;\n            }\n            if (id == R.id.action_animate) {\n                prepareDataAnimation();\n                chart.startDataAnimation();\n                return true;\n            }\n            if (id == R.id.action_toggle_selection_mode) {\n                toggleLabelForSelected();\n                Toast.makeText(getActivity(),\n                        \"Selection mode set to \" + chart.isValueSelectionEnabled() + \" select any point.\",\n                        Toast.LENGTH_SHORT).show();\n                return true;\n            }\n            return super.onOptionsItemSelected(item);\n        }\n\n        private void reset() {\n            chart.setCircleFillRatio(1.0f);\n            hasLabels = false;\n            hasLabelsOutside = false;\n            hasCenterCircle = false;\n            hasCenterText1 = false;\n            hasCenterText2 = false;\n            isExploded = false;\n            hasLabelForSelected = false;\n        }\n\n        private void generateData() {\n            int numValues = 6;\n\n            List<SliceValue> values = new ArrayList<SliceValue>();\n            for (int i = 0; i < numValues; ++i) {\n                SliceValue sliceValue = new SliceValue((float) Math.random() * 30 + 15, ChartUtils.pickColor());\n                values.add(sliceValue);\n            }\n\n            data = new PieChartData(values);\n            data.setHasLabels(hasLabels);\n            data.setHasLabelsOnlyForSelected(hasLabelForSelected);\n            data.setHasLabelsOutside(hasLabelsOutside);\n            data.setHasCenterCircle(hasCenterCircle);\n\n            if (isExploded) {\n                data.setSlicesSpacing(24);\n            }\n\n            if (hasCenterText1) {\n                data.setCenterText1(\"Hello!\");\n\n                // Get roboto-italic font.\n                Typeface tf = Typeface.createFromAsset(getActivity().getAssets(), \"Roboto-Italic.ttf\");\n                data.setCenterText1Typeface(tf);\n\n                // Get font size from dimens.xml and convert it to sp(library uses sp values).\n                data.setCenterText1FontSize(ChartUtils.px2sp(getResources().getDisplayMetrics().scaledDensity,\n                        (int) getResources().getDimension(R.dimen.pie_chart_text1_size)));\n            }\n\n            if (hasCenterText2) {\n                data.setCenterText2(\"Charts (Roboto Italic)\");\n\n                Typeface tf = Typeface.createFromAsset(getActivity().getAssets(), \"Roboto-Italic.ttf\");\n\n                data.setCenterText2Typeface(tf);\n                data.setCenterText2FontSize(ChartUtils.px2sp(getResources().getDisplayMetrics().scaledDensity,\n                        (int) getResources().getDimension(R.dimen.pie_chart_text2_size)));\n            }\n\n            chart.setPieChartData(data);\n        }\n\n        private void explodeChart() {\n            isExploded = !isExploded;\n            generateData();\n\n        }\n\n        private void toggleLabelsOutside() {\n            // has labels have to be true:P\n            hasLabelsOutside = !hasLabelsOutside;\n            if (hasLabelsOutside) {\n                hasLabels = true;\n                hasLabelForSelected = false;\n                chart.setValueSelectionEnabled(hasLabelForSelected);\n            }\n\n            if (hasLabelsOutside) {\n                chart.setCircleFillRatio(0.7f);\n            } else {\n                chart.setCircleFillRatio(1.0f);\n            }\n\n            generateData();\n\n        }\n\n        private void toggleLabels() {\n            hasLabels = !hasLabels;\n\n            if (hasLabels) {\n                hasLabelForSelected = false;\n                chart.setValueSelectionEnabled(hasLabelForSelected);\n\n                if (hasLabelsOutside) {\n                    chart.setCircleFillRatio(0.7f);\n                } else {\n                    chart.setCircleFillRatio(1.0f);\n                }\n            }\n\n            generateData();\n        }\n\n        private void toggleLabelForSelected() {\n            hasLabelForSelected = !hasLabelForSelected;\n\n            chart.setValueSelectionEnabled(hasLabelForSelected);\n\n            if (hasLabelForSelected) {\n                hasLabels = false;\n                hasLabelsOutside = false;\n\n                if (hasLabelsOutside) {\n                    chart.setCircleFillRatio(0.7f);\n                } else {\n                    chart.setCircleFillRatio(1.0f);\n                }\n            }\n\n            generateData();\n        }\n\n        /**\n         * To animate values you have to change targets values and then call {@link Chart#startDataAnimation()}\n         * method(don't confuse with View.animate()).\n         */\n        private void prepareDataAnimation() {\n            for (SliceValue value : data.getValues()) {\n                value.setTarget((float) Math.random() * 30 + 15);\n            }\n        }\n\n        private class ValueTouchListener implements PieChartOnValueSelectListener {\n\n            @Override\n            public void onValueSelected(int arcIndex, SliceValue value) {\n                Toast.makeText(getActivity(), \"Selected: \" + value, Toast.LENGTH_SHORT).show();\n            }\n\n            @Override\n            public void onValueDeselected() {\n                // TODO Auto-generated method stub\n\n            }\n\n        }\n    }\n}\n"
  },
  {
    "path": "hellocharts-samples/src/lecho/lib/hellocharts/samples/PreviewColumnChartActivity.java",
    "content": "package lecho.lib.hellocharts.samples;\n\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.support.v7.app.ActionBarActivity;\nimport android.view.LayoutInflater;\nimport android.view.Menu;\nimport android.view.MenuInflater;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport lecho.lib.hellocharts.gesture.ZoomType;\nimport lecho.lib.hellocharts.listener.ViewportChangeListener;\nimport lecho.lib.hellocharts.model.Axis;\nimport lecho.lib.hellocharts.model.Column;\nimport lecho.lib.hellocharts.model.ColumnChartData;\nimport lecho.lib.hellocharts.model.SubcolumnValue;\nimport lecho.lib.hellocharts.model.Viewport;\nimport lecho.lib.hellocharts.util.ChartUtils;\nimport lecho.lib.hellocharts.view.ColumnChartView;\nimport lecho.lib.hellocharts.view.PreviewColumnChartView;\n\npublic class PreviewColumnChartActivity extends ActionBarActivity {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_preview_column_chart);\n        if (savedInstanceState == null) {\n            getSupportFragmentManager().beginTransaction().add(R.id.container, new PlaceholderFragment()).commit();\n        }\n    }\n\n    /**\n     * A placeholder fragment containing a simple view.\n     */\n    public static class PlaceholderFragment extends Fragment {\n\n        private ColumnChartView chart;\n        private PreviewColumnChartView previewChart;\n        private ColumnChartData data;\n        /**\n         * Deep copy of data.\n         */\n        private ColumnChartData previewData;\n\n        public PlaceholderFragment() {\n        }\n\n        @Override\n        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n            setHasOptionsMenu(true);\n            View rootView = inflater.inflate(R.layout.fragment_preview_column_chart, container, false);\n\n            chart = (ColumnChartView) rootView.findViewById(R.id.chart);\n            previewChart = (PreviewColumnChartView) rootView.findViewById(R.id.chart_preview);\n\n            // Generate data for previewed chart and copy of that data for preview chart.\n            generateDefaultData();\n\n            chart.setColumnChartData(data);\n            // Disable zoom/scroll for previewed chart, visible chart ranges depends on preview chart viewport so\n            // zoom/scroll is unnecessary.\n            chart.setZoomEnabled(false);\n            chart.setScrollEnabled(false);\n\n            previewChart.setColumnChartData(previewData);\n            previewChart.setViewportChangeListener(new ViewportListener());\n\n            previewX(false);\n\n            return rootView;\n        }\n\n        // MENU\n        @Override\n        public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {\n            inflater.inflate(R.menu.preview_column_chart, menu);\n        }\n\n        @Override\n        public boolean onOptionsItemSelected(MenuItem item) {\n            int id = item.getItemId();\n            if (id == R.id.action_reset) {\n                generateDefaultData();\n                chart.setColumnChartData(data);\n                previewChart.setColumnChartData(previewData);\n                previewX(true);\n                return true;\n            }\n            if (id == R.id.action_preview_both) {\n                previewXY();\n                previewChart.setZoomType(ZoomType.HORIZONTAL_AND_VERTICAL);\n                return true;\n            }\n            if (id == R.id.action_preview_horizontal) {\n                previewX(true);\n                return true;\n            }\n            if (id == R.id.action_preview_vertical) {\n                previewY();\n                return true;\n            }\n            if (id == R.id.action_change_color) {\n                int color = ChartUtils.pickColor();\n                while (color == previewChart.getPreviewColor()) {\n                    color = ChartUtils.pickColor();\n                }\n                previewChart.setPreviewColor(color);\n                return true;\n            }\n            return super.onOptionsItemSelected(item);\n        }\n\n        private void generateDefaultData() {\n            int numSubcolumns = 1;\n            int numColumns = 50;\n            List<Column> columns = new ArrayList<Column>();\n            List<SubcolumnValue> values;\n            for (int i = 0; i < numColumns; ++i) {\n\n                values = new ArrayList<SubcolumnValue>();\n                for (int j = 0; j < numSubcolumns; ++j) {\n                    values.add(new SubcolumnValue((float) Math.random() * 50f + 5, ChartUtils.pickColor()));\n                }\n\n                columns.add(new Column(values));\n            }\n\n            data = new ColumnChartData(columns);\n            data.setAxisXBottom(new Axis());\n            data.setAxisYLeft(new Axis().setHasLines(true));\n\n            // prepare preview data, is better to use separate deep copy for preview chart.\n            // set color to grey to make preview area more visible.\n            previewData = new ColumnChartData(data);\n            for (Column column : previewData.getColumns()) {\n                for (SubcolumnValue value : column.getValues()) {\n                    value.setColor(ChartUtils.DEFAULT_DARKEN_COLOR);\n                }\n            }\n\n        }\n\n        private void previewY() {\n            Viewport tempViewport = new Viewport(chart.getMaximumViewport());\n            float dy = tempViewport.height() / 4;\n            tempViewport.inset(0, dy);\n            previewChart.setCurrentViewportWithAnimation(tempViewport);\n            previewChart.setZoomType(ZoomType.VERTICAL);\n        }\n\n        private void previewX(boolean animate) {\n            Viewport tempViewport = new Viewport(chart.getMaximumViewport());\n            float dx = tempViewport.width() / 4;\n            tempViewport.inset(dx, 0);\n            if (animate) {\n                previewChart.setCurrentViewportWithAnimation(tempViewport);\n            } else {\n                previewChart.setCurrentViewport(tempViewport);\n            }\n            previewChart.setZoomType(ZoomType.HORIZONTAL);\n        }\n\n        private void previewXY() {\n            // Better to not modify viewport of any chart directly so create a copy.\n            Viewport tempViewport = new Viewport(chart.getMaximumViewport());\n            // Make temp viewport smaller.\n            float dx = tempViewport.width() / 4;\n            float dy = tempViewport.height() / 4;\n            tempViewport.inset(dx, dy);\n            previewChart.setCurrentViewportWithAnimation(tempViewport);\n        }\n\n        /**\n         * Viewport listener for preview chart(lower one). in {@link #onViewportChanged(Viewport)} method change\n         * viewport of upper chart.\n         */\n        private class ViewportListener implements ViewportChangeListener {\n\n            @Override\n            public void onViewportChanged(Viewport newViewport) {\n                // don't use animation, it is unnecessary when using preview chart because usually viewport changes\n                // happens to often.\n                chart.setCurrentViewport(newViewport);\n            }\n\n        }\n    }\n}\n"
  },
  {
    "path": "hellocharts-samples/src/lecho/lib/hellocharts/samples/PreviewLineChartActivity.java",
    "content": "package lecho.lib.hellocharts.samples;\n\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.support.v7.app.ActionBarActivity;\nimport android.view.LayoutInflater;\nimport android.view.Menu;\nimport android.view.MenuInflater;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport lecho.lib.hellocharts.gesture.ZoomType;\nimport lecho.lib.hellocharts.listener.ViewportChangeListener;\nimport lecho.lib.hellocharts.model.Axis;\nimport lecho.lib.hellocharts.model.Line;\nimport lecho.lib.hellocharts.model.LineChartData;\nimport lecho.lib.hellocharts.model.PointValue;\nimport lecho.lib.hellocharts.model.Viewport;\nimport lecho.lib.hellocharts.util.ChartUtils;\nimport lecho.lib.hellocharts.view.LineChartView;\nimport lecho.lib.hellocharts.view.PreviewLineChartView;\n\npublic class PreviewLineChartActivity extends ActionBarActivity {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_preview_line_chart);\n        if (savedInstanceState == null) {\n            getSupportFragmentManager().beginTransaction().add(R.id.container, new PlaceholderFragment()).commit();\n        }\n    }\n\n    /**\n     * A fragment containing a line chart and preview line chart.\n     */\n    public static class PlaceholderFragment extends Fragment {\n\n        private LineChartView chart;\n        private PreviewLineChartView previewChart;\n        private LineChartData data;\n        /**\n         * Deep copy of data.\n         */\n        private LineChartData previewData;\n\n        public PlaceholderFragment() {\n        }\n\n        @Override\n        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n            setHasOptionsMenu(true);\n            View rootView = inflater.inflate(R.layout.fragment_preview_line_chart, container, false);\n\n            chart = (LineChartView) rootView.findViewById(R.id.chart);\n            previewChart = (PreviewLineChartView) rootView.findViewById(R.id.chart_preview);\n\n            // Generate data for previewed chart and copy of that data for preview chart.\n            generateDefaultData();\n\n            chart.setLineChartData(data);\n            // Disable zoom/scroll for previewed chart, visible chart ranges depends on preview chart viewport so\n            // zoom/scroll is unnecessary.\n            chart.setZoomEnabled(false);\n            chart.setScrollEnabled(false);\n\n            previewChart.setLineChartData(previewData);\n            previewChart.setViewportChangeListener(new ViewportListener());\n\n            previewX(false);\n\n            return rootView;\n        }\n\n        // MENU\n        @Override\n        public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {\n            inflater.inflate(R.menu.preview_line_chart, menu);\n        }\n\n        @Override\n        public boolean onOptionsItemSelected(MenuItem item) {\n            int id = item.getItemId();\n            if (id == R.id.action_reset) {\n                generateDefaultData();\n                chart.setLineChartData(data);\n                previewChart.setLineChartData(previewData);\n                previewX(true);\n                return true;\n            }\n            if (id == R.id.action_preview_both) {\n                previewXY();\n                previewChart.setZoomType(ZoomType.HORIZONTAL_AND_VERTICAL);\n                return true;\n            }\n            if (id == R.id.action_preview_horizontal) {\n                previewX(true);\n                return true;\n            }\n            if (id == R.id.action_preview_vertical) {\n                previewY();\n                return true;\n            }\n            if (id == R.id.action_change_color) {\n                int color = ChartUtils.pickColor();\n                while (color == previewChart.getPreviewColor()) {\n                    color = ChartUtils.pickColor();\n                }\n                previewChart.setPreviewColor(color);\n                return true;\n            }\n            return super.onOptionsItemSelected(item);\n        }\n\n        private void generateDefaultData() {\n            int numValues = 50;\n\n            List<PointValue> values = new ArrayList<PointValue>();\n            for (int i = 0; i < numValues; ++i) {\n                values.add(new PointValue(i, (float) Math.random() * 100f));\n            }\n\n            Line line = new Line(values);\n            line.setColor(ChartUtils.COLOR_GREEN);\n            line.setHasPoints(false);// too many values so don't draw points.\n\n            List<Line> lines = new ArrayList<Line>();\n            lines.add(line);\n\n            data = new LineChartData(lines);\n            data.setAxisXBottom(new Axis());\n            data.setAxisYLeft(new Axis().setHasLines(true));\n\n            // prepare preview data, is better to use separate deep copy for preview chart.\n            // Set color to grey to make preview area more visible.\n            previewData = new LineChartData(data);\n            previewData.getLines().get(0).setColor(ChartUtils.DEFAULT_DARKEN_COLOR);\n\n        }\n\n        private void previewY() {\n            Viewport tempViewport = new Viewport(chart.getMaximumViewport());\n            float dy = tempViewport.height() / 4;\n            tempViewport.inset(0, dy);\n            previewChart.setCurrentViewportWithAnimation(tempViewport);\n            previewChart.setZoomType(ZoomType.VERTICAL);\n        }\n\n        private void previewX(boolean animate) {\n            Viewport tempViewport = new Viewport(chart.getMaximumViewport());\n            float dx = tempViewport.width() / 4;\n            tempViewport.inset(dx, 0);\n            if (animate) {\n                previewChart.setCurrentViewportWithAnimation(tempViewport);\n            } else {\n                previewChart.setCurrentViewport(tempViewport);\n            }\n            previewChart.setZoomType(ZoomType.HORIZONTAL);\n        }\n\n        private void previewXY() {\n            // Better to not modify viewport of any chart directly so create a copy.\n            Viewport tempViewport = new Viewport(chart.getMaximumViewport());\n            // Make temp viewport smaller.\n            float dx = tempViewport.width() / 4;\n            float dy = tempViewport.height() / 4;\n            tempViewport.inset(dx, dy);\n            previewChart.setCurrentViewportWithAnimation(tempViewport);\n        }\n\n        /**\n         * Viewport listener for preview chart(lower one). in {@link #onViewportChanged(Viewport)} method change\n         * viewport of upper chart.\n         */\n        private class ViewportListener implements ViewportChangeListener {\n\n            @Override\n            public void onViewportChanged(Viewport newViewport) {\n                // don't use animation, it is unnecessary when using preview chart.\n                chart.setCurrentViewport(newViewport);\n            }\n\n        }\n\n    }\n}\n"
  },
  {
    "path": "hellocharts-samples/src/lecho/lib/hellocharts/samples/SpeedChartActivity.java",
    "content": "package lecho.lib.hellocharts.samples;\n\nimport android.graphics.Color;\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.support.v7.app.ActionBarActivity;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport lecho.lib.hellocharts.formatter.SimpleAxisValueFormatter;\nimport lecho.lib.hellocharts.model.Axis;\nimport lecho.lib.hellocharts.model.Line;\nimport lecho.lib.hellocharts.model.LineChartData;\nimport lecho.lib.hellocharts.model.PointValue;\nimport lecho.lib.hellocharts.model.Viewport;\nimport lecho.lib.hellocharts.util.ChartUtils;\nimport lecho.lib.hellocharts.view.LineChartView;\n\npublic class SpeedChartActivity extends ActionBarActivity {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_tempo_chart);\n        if (savedInstanceState == null) {\n            getSupportFragmentManager().beginTransaction().add(R.id.container, new PlaceholderFragment()).commit();\n        }\n    }\n\n    public static class PlaceholderFragment extends Fragment {\n\n        private LineChartView chart;\n        private LineChartData data;\n\n        public PlaceholderFragment() {\n        }\n\n        @Override\n        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n            View rootView = inflater.inflate(R.layout.fragment_tempo_chart, container, false);\n\n            chart = (LineChartView) rootView.findViewById(R.id.chart);\n\n            generateSpeedData();\n\n            return rootView;\n        }\n\n        private void generateSpeedData() {\n            // I got speed in range (0-55) and height in meters in range(200 - 300). I want this chart to display both\n            // information. Differences between speed and height values are large and chart doesn't look good so I need\n            // to modify height values to be in range of speed values.\n\n            float speedRange = 55;\n            float minHeight = 200;\n            float maxHeight = 300;\n\n            float scale = speedRange / maxHeight;\n            float sub = (minHeight * scale) / 2;\n\n            int numValues = 52;\n\n            Line line;\n            List<PointValue> values;\n            List<Line> lines = new ArrayList<Line>();\n\n            // Height line, add it as first line to be drawn in the background.\n            values = new ArrayList<PointValue>();\n            for (int i = 0; i < numValues; ++i) {\n                // Some random height values, add +200 to make line a little more natural\n                float rawHeight = (float) (Math.random() * 100 + 200);\n                float normalizedHeight = rawHeight * scale - sub;\n                values.add(new PointValue(i, normalizedHeight));\n            }\n\n            line = new Line(values);\n            line.setColor(Color.GRAY);\n            line.setHasPoints(false);\n            line.setFilled(true);\n            line.setStrokeWidth(1);\n            lines.add(line);\n\n            // Speed line\n            values = new ArrayList<PointValue>();\n            for (int i = 0; i < numValues; ++i) {\n                // Some random speed values, add +20 to make line a little more natural.\n                values.add(new PointValue(i, (float) Math.random() * 30 + 20));\n            }\n\n            line = new Line(values);\n            line.setColor(ChartUtils.COLOR_GREEN);\n            line.setHasPoints(false);\n            line.setStrokeWidth(3);\n            lines.add(line);\n\n            // Data and axes\n            data = new LineChartData(lines);\n\n            // Distance axis(bottom X) with formatter that will ad [km] to values, remember to modify max label charts\n            // value.\n            Axis distanceAxis = new Axis();\n            distanceAxis.setName(\"Distance\");\n            distanceAxis.setTextColor(ChartUtils.COLOR_ORANGE);\n            distanceAxis.setMaxLabelChars(4);\n            distanceAxis.setFormatter(new SimpleAxisValueFormatter().setAppendedText(\"km\".toCharArray()));\n            distanceAxis.setHasLines(true);\n            distanceAxis.setInside(true);\n            data.setAxisXBottom(distanceAxis);\n\n            // Speed axis\n            data.setAxisYLeft(new Axis().setName(\"Speed [km/h]\").setHasLines(true).setMaxLabelChars(3)\n                    .setTextColor(ChartUtils.COLOR_RED).setInside(true));\n\n            // Height axis, this axis need custom formatter that will translate values back to real height values.\n            data.setAxisYRight(new Axis().setName(\"Height [m]\").setMaxLabelChars(3).setTextColor(ChartUtils.COLOR_BLUE)\n                    .setFormatter(new HeightValueFormatter(scale, sub, 0)).setInside(true));\n\n            // Set data\n            chart.setLineChartData(data);\n\n            // Important: adjust viewport, you could skip this step but in this case it will looks better with custom\n            // viewport. Set\n            // viewport with Y range 0-55;\n            Viewport v = chart.getMaximumViewport();\n            v.set(v.left, speedRange, v.right, 0);\n            chart.setMaximumViewport(v);\n            chart.setCurrentViewport(v);\n\n        }\n\n        /**\n         * Recalculated height values to display on axis.\n         */\n        private static class HeightValueFormatter extends SimpleAxisValueFormatter {\n\n            private float scale;\n            private float sub;\n            private int decimalDigits;\n\n            public HeightValueFormatter(float scale, float sub, int decimalDigits) {\n                this.scale = scale;\n                this.sub = sub;\n                this.decimalDigits = decimalDigits;\n            }\n\n            @Override\n            public int formatValueForAutoGeneratedAxis(char[] formattedValue, float value, int autoDecimalDigits) {\n                float scaledValue = (value + sub) / scale;\n                return super.formatValueForAutoGeneratedAxis(formattedValue, scaledValue, this.decimalDigits);\n            }\n        }\n\n    }\n}\n"
  },
  {
    "path": "hellocharts-samples/src/lecho/lib/hellocharts/samples/TempoChartActivity.java",
    "content": "package lecho.lib.hellocharts.samples;\n\nimport android.graphics.Color;\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.support.v7.app.ActionBarActivity;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport lecho.lib.hellocharts.formatter.SimpleAxisValueFormatter;\nimport lecho.lib.hellocharts.model.Axis;\nimport lecho.lib.hellocharts.model.AxisValue;\nimport lecho.lib.hellocharts.model.Line;\nimport lecho.lib.hellocharts.model.LineChartData;\nimport lecho.lib.hellocharts.model.PointValue;\nimport lecho.lib.hellocharts.model.Viewport;\nimport lecho.lib.hellocharts.util.ChartUtils;\nimport lecho.lib.hellocharts.view.LineChartView;\n\npublic class TempoChartActivity extends ActionBarActivity {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_tempo_chart);\n        if (savedInstanceState == null) {\n            getSupportFragmentManager().beginTransaction().add(R.id.container, new PlaceholderFragment()).commit();\n        }\n    }\n\n    public static class PlaceholderFragment extends Fragment {\n\n        private LineChartView chart;\n        private LineChartData data;\n\n        public PlaceholderFragment() {\n        }\n\n        @Override\n        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n            View rootView = inflater.inflate(R.layout.fragment_tempo_chart, container, false);\n\n            chart = (LineChartView) rootView.findViewById(R.id.chart);\n\n            generateTempoData();\n\n            return rootView;\n        }\n\n        private void generateTempoData() {\n            // I got speed in range (0-50) and height in meters in range(200 - 300). I want this chart to display both\n            // information. Differences between speed and height values are large and chart doesn't look good so I need\n            // to modify height values to be in range of speed values.\n\n            // The same for displaying Tempo/Height chart.\n\n            float minHeight = 200;\n            float maxHeight = 300;\n            float tempoRange = 15; // from 0min/km to 15min/km\n\n            float scale = tempoRange / maxHeight;\n            float sub = (minHeight * scale) / 2;\n\n            int numValues = 52;\n\n            Line line;\n            List<PointValue> values;\n            List<Line> lines = new ArrayList<Line>();\n\n            // Height line, add it as first line to be drawn in the background.\n            values = new ArrayList<PointValue>();\n            for (int i = 0; i < numValues; ++i) {\n                // Some random height values, add +200 to make line a little more natural\n                float rawHeight = (float) (Math.random() * 100 + 200);\n                float normalizedHeight = rawHeight * scale - sub;\n                values.add(new PointValue(i, normalizedHeight));\n            }\n\n            line = new Line(values);\n            line.setColor(Color.GRAY);\n            line.setHasPoints(false);\n            line.setFilled(true);\n            line.setStrokeWidth(1);\n            lines.add(line);\n\n            // Tempo line is a little tricky because worse tempo means bigger value for example 11min per km is worse\n            // than 2min per km but the second should be higher on the chart. So you need to know max tempo and\n            // tempoRange and set\n            // chart values to minTempo - realTempo.\n            values = new ArrayList<PointValue>();\n            for (int i = 0; i < numValues; ++i) {\n                // Some random raw tempo values.\n                float realTempo = (float) Math.random() * 6 + 2;\n                float revertedTempo = tempoRange - realTempo;\n                values.add(new PointValue(i, revertedTempo));\n            }\n\n            line = new Line(values);\n            line.setColor(ChartUtils.COLOR_RED);\n            line.setHasPoints(false);\n            line.setStrokeWidth(3);\n            lines.add(line);\n\n            // Data and axes\n            data = new LineChartData(lines);\n\n            // Distance axis(bottom X) with formatter that will ad [km] to values, remember to modify max label charts\n            // value.\n            Axis distanceAxis = new Axis();\n            distanceAxis.setName(\"Distance\");\n            distanceAxis.setTextColor(ChartUtils.COLOR_ORANGE);\n            distanceAxis.setMaxLabelChars(4);\n            distanceAxis.setFormatter(new SimpleAxisValueFormatter().setAppendedText(\"km\".toCharArray()));\n            distanceAxis.setHasLines(true);\n            distanceAxis.setHasTiltedLabels(true);\n            data.setAxisXBottom(distanceAxis);\n\n            // Tempo uses minutes so I can't use auto-generated axis because auto-generation works only for decimal\n            // system. So generate custom axis values for example every 15 seconds and set custom labels in format\n            // minutes:seconds(00:00), you could do it in formatter but here will be faster.\n            List<AxisValue> axisValues = new ArrayList<AxisValue>();\n            for (float i = 0; i < tempoRange; i += 0.25f) {\n                // I'am translating float to minutes because I don't have data in minutes, if You store some time data\n                // you may skip translation.\n                axisValues.add(new AxisValue(i).setLabel(formatMinutes(tempoRange - i)));\n            }\n\n            Axis tempoAxis = new Axis(axisValues).setName(\"Tempo [min/km]\").setHasLines(true).setMaxLabelChars(4)\n                    .setTextColor(ChartUtils.COLOR_RED);\n            data.setAxisYLeft(tempoAxis);\n\n            // *** Same as in Speed/Height chart.\n            // Height axis, this axis need custom formatter that will translate values back to real height values.\n            data.setAxisYRight(new Axis().setName(\"Height [m]\").setMaxLabelChars(3)\n                    .setFormatter(new HeightValueFormatter(scale, sub, 0)));\n\n            // Set data\n            chart.setLineChartData(data);\n\n            // Important: adjust viewport, you could skip this step but in this case it will looks better with custom\n            // viewport. Set\n            // viewport with Y range 0-12;\n            Viewport v = chart.getMaximumViewport();\n            v.set(v.left, tempoRange, v.right, 0);\n            chart.setMaximumViewport(v);\n            chart.setCurrentViewport(v);\n\n        }\n\n        private String formatMinutes(float value) {\n            StringBuilder sb = new StringBuilder();\n\n            // translate value to seconds, for example\n            int valueInSeconds = (int) (value * 60);\n            int minutes = (int) Math.floor(valueInSeconds / 60);\n            int seconds = (int) valueInSeconds % 60;\n\n            sb.append(String.valueOf(minutes)).append(':');\n            if (seconds < 10) {\n                sb.append('0');\n            }\n            sb.append(String.valueOf(seconds));\n            return sb.toString();\n        }\n\n        /**\n         * Recalculated height values to display on axis. For this example I use auto-generated height axis so I\n         * override only formatAutoValue method.\n         */\n        private static class HeightValueFormatter extends SimpleAxisValueFormatter {\n\n            private float scale;\n            private float sub;\n            private int decimalDigits;\n\n            public HeightValueFormatter(float scale, float sub, int decimalDigits) {\n                this.scale = scale;\n                this.sub = sub;\n                this.decimalDigits = decimalDigits;\n            }\n\n            @Override\n            public int formatValueForAutoGeneratedAxis(char[] formattedValue, float value, int autoDecimalDigits) {\n                float scaledValue = (value + sub) / scale;\n                return super.formatValueForAutoGeneratedAxis(formattedValue, scaledValue, this.decimalDigits);\n            }\n        }\n\n    }\n}\n"
  },
  {
    "path": "hellocharts-samples/src/lecho/lib/hellocharts/samples/ViewPagerChartsActivity.java",
    "content": "package lecho.lib.hellocharts.samples;\n\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.support.v4.app.FragmentManager;\nimport android.support.v4.app.FragmentPagerAdapter;\nimport android.support.v4.app.FragmentTransaction;\nimport android.support.v4.view.ViewPager;\nimport android.support.v7.app.ActionBar;\nimport android.support.v7.app.ActionBarActivity;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.RelativeLayout;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport lecho.lib.hellocharts.gesture.ContainerScrollType;\nimport lecho.lib.hellocharts.gesture.ZoomType;\nimport lecho.lib.hellocharts.model.Axis;\nimport lecho.lib.hellocharts.model.BubbleChartData;\nimport lecho.lib.hellocharts.model.BubbleValue;\nimport lecho.lib.hellocharts.model.Column;\nimport lecho.lib.hellocharts.model.ColumnChartData;\nimport lecho.lib.hellocharts.model.Line;\nimport lecho.lib.hellocharts.model.LineChartData;\nimport lecho.lib.hellocharts.model.PieChartData;\nimport lecho.lib.hellocharts.model.PointValue;\nimport lecho.lib.hellocharts.model.SliceValue;\nimport lecho.lib.hellocharts.model.SubcolumnValue;\nimport lecho.lib.hellocharts.model.Viewport;\nimport lecho.lib.hellocharts.util.ChartUtils;\nimport lecho.lib.hellocharts.view.BubbleChartView;\nimport lecho.lib.hellocharts.view.ColumnChartView;\nimport lecho.lib.hellocharts.view.LineChartView;\nimport lecho.lib.hellocharts.view.PieChartView;\nimport lecho.lib.hellocharts.view.PreviewLineChartView;\n\npublic class ViewPagerChartsActivity extends ActionBarActivity implements ActionBar.TabListener {\n\n    /**\n     * The {@link android.support.v4.view.PagerAdapter} that will provide fragments for each of the sections. We use a\n     * {@link FragmentPagerAdapter} derivative, which will keep every loaded fragment in memory. If this becomes too\n     * memory intensive, it may be best to switch to a {@link android.support.v4.app.FragmentStatePagerAdapter}.\n     */\n    SectionsPagerAdapter mSectionsPagerAdapter;\n\n    /**\n     * The {@link ViewPager} that will host the section contents.\n     */\n    ViewPager mViewPager;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_view_pager_charts);\n\n        // Set up the action bar.\n        final ActionBar actionBar = getSupportActionBar();\n        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);\n\n        // Create the adapter that will return a fragment for each of the three\n        // primary sections of the activity.\n        mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());\n\n        // Set up the ViewPager with the sections adapter.\n        mViewPager = (ViewPager) findViewById(R.id.pager);\n        mViewPager.setAdapter(mSectionsPagerAdapter);\n\n        // When swiping between different sections, select the corresponding\n        // tab. We can also use ActionBar.Tab#select() to do this if we have\n        // a reference to the Tab.\n        mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {\n            @Override\n            public void onPageSelected(int position) {\n                actionBar.setSelectedNavigationItem(position);\n            }\n        });\n\n        // For each of the sections in the app, add a tab to the action bar.\n        for (int i = 0; i < mSectionsPagerAdapter.getCount(); i++) {\n            // Create a tab with text corresponding to the page title defined by\n            // the adapter. Also specify this Activity object, which implements\n            // the TabListener interface, as the callback (listener) for when\n            // this tab is selected.\n            actionBar.addTab(actionBar.newTab().setText(mSectionsPagerAdapter.getPageTitle(i)).setTabListener(this));\n        }\n    }\n\n    @Override\n    public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {\n        // When the given tab is selected, switch to the corresponding page in\n        // the ViewPager.\n        mViewPager.setCurrentItem(tab.getPosition());\n    }\n\n    @Override\n    public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {\n    }\n\n    @Override\n    public void onTabReselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {\n    }\n\n    /**\n     * A placeholder fragment containing a simple view.\n     */\n    public static class PlaceholderFragment extends Fragment {\n        /**\n         * The fragment argument representing the section number for this fragment.\n         */\n        private static final String ARG_SECTION_NUMBER = \"section_number\";\n\n        public PlaceholderFragment() {\n        }\n\n        /**\n         * Returns a new instance of this fragment for the given section number.\n         */\n        public static PlaceholderFragment newInstance(int sectionNumber) {\n            PlaceholderFragment fragment = new PlaceholderFragment();\n            Bundle args = new Bundle();\n            args.putInt(ARG_SECTION_NUMBER, sectionNumber);\n            fragment.setArguments(args);\n            return fragment;\n        }\n\n        @Override\n        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n            View rootView = inflater.inflate(R.layout.fragment_view_pager_charts, container, false);\n            RelativeLayout layout = (RelativeLayout) rootView;\n            int sectionNum = getArguments().getInt(ARG_SECTION_NUMBER);\n            switch (sectionNum) {\n                case 1:\n                    LineChartView lineChartView = new LineChartView(getActivity());\n                    lineChartView.setLineChartData(generateLineChartData());\n                    lineChartView.setZoomType(ZoomType.HORIZONTAL);\n\n                    /** Note: Chart is within ViewPager so enable container scroll mode. **/\n                    lineChartView.setContainerScrollEnabled(true, ContainerScrollType.HORIZONTAL);\n\n                    layout.addView(lineChartView);\n                    break;\n                case 2:\n                    ColumnChartView columnChartView = new ColumnChartView(getActivity());\n                    columnChartView.setColumnChartData(generateColumnChartData());\n                    columnChartView.setZoomType(ZoomType.HORIZONTAL);\n\n                    /** Note: Chart is within ViewPager so enable container scroll mode. **/\n                    columnChartView.setContainerScrollEnabled(true, ContainerScrollType.HORIZONTAL);\n\n                    layout.addView(columnChartView);\n                    break;\n                case 3:\n                    BubbleChartView bubbleChartView = new BubbleChartView(getActivity());\n                    bubbleChartView.setBubbleChartData(generateBubbleChartData());\n                    bubbleChartView.setZoomType(ZoomType.HORIZONTAL_AND_VERTICAL);\n\n                    /** Note: Chart is within ViewPager so enable container scroll mode. **/\n                    bubbleChartView.setContainerScrollEnabled(true, ContainerScrollType.HORIZONTAL);\n\n                    layout.addView(bubbleChartView);\n                    break;\n                case 4:\n                    PreviewLineChartView previewLineChartView = new PreviewLineChartView(getActivity());\n                    previewLineChartView.setLineChartData(generatePreviewLineChartData());\n\n                    /** Note: Chart is within ViewPager so enable container scroll mode. **/\n                    previewLineChartView.setContainerScrollEnabled(true, ContainerScrollType.HORIZONTAL);\n\n                    Viewport tempViewport = new Viewport(previewLineChartView.getMaximumViewport());\n                    float dx = tempViewport.width() / 6;\n                    tempViewport.inset(dx, 0);\n                    previewLineChartView.setCurrentViewport(tempViewport);\n                    previewLineChartView.setZoomType(ZoomType.HORIZONTAL);\n\n                    layout.addView(previewLineChartView);\n                    break;\n                case 5:\n                    PieChartView pieChartView = new PieChartView(getActivity());\n                    pieChartView.setPieChartData(generatePieChartData());\n\n                    /** Note: Chart is within ViewPager so enable container scroll mode. **/\n                    pieChartView.setContainerScrollEnabled(true, ContainerScrollType.HORIZONTAL);\n\n                    layout.addView(pieChartView);\n                    break;\n            }\n\n            return rootView;\n        }\n\n        private LineChartData generateLineChartData() {\n            int numValues = 20;\n\n            List<PointValue> values = new ArrayList<PointValue>();\n            for (int i = 0; i < numValues; ++i) {\n                values.add(new PointValue(i, (float) Math.random() * 100f));\n            }\n\n            Line line = new Line(values);\n            line.setColor(ChartUtils.COLOR_GREEN);\n\n            List<Line> lines = new ArrayList<Line>();\n            lines.add(line);\n\n            LineChartData data = new LineChartData(lines);\n            data.setAxisXBottom(new Axis().setName(\"Axis X\"));\n            data.setAxisYLeft(new Axis().setName(\"Axis Y\").setHasLines(true));\n            return data;\n\n        }\n\n        private ColumnChartData generateColumnChartData() {\n            int numSubcolumns = 1;\n            int numColumns = 12;\n            // Column can have many subcolumns, here by default I use 1 subcolumn in each of 8 columns.\n            List<Column> columns = new ArrayList<Column>();\n            List<SubcolumnValue> values;\n            for (int i = 0; i < numColumns; ++i) {\n\n                values = new ArrayList<SubcolumnValue>();\n                for (int j = 0; j < numSubcolumns; ++j) {\n                    values.add(new SubcolumnValue((float) Math.random() * 50f + 5, ChartUtils.pickColor()));\n                }\n\n                columns.add(new Column(values));\n            }\n\n            ColumnChartData data = new ColumnChartData(columns);\n\n            data.setAxisXBottom(new Axis().setName(\"Axis X\"));\n            data.setAxisYLeft(new Axis().setName(\"Axis Y\").setHasLines(true));\n            return data;\n\n        }\n\n        private BubbleChartData generateBubbleChartData() {\n            int numBubbles = 10;\n\n            List<BubbleValue> values = new ArrayList<BubbleValue>();\n            for (int i = 0; i < numBubbles; ++i) {\n                BubbleValue value = new BubbleValue(i, (float) Math.random() * 100, (float) Math.random() * 1000);\n                value.setColor(ChartUtils.pickColor());\n                values.add(value);\n            }\n\n            BubbleChartData data = new BubbleChartData(values);\n\n            data.setAxisXBottom(new Axis().setName(\"Axis X\"));\n            data.setAxisYLeft(new Axis().setName(\"Axis Y\").setHasLines(true));\n            return data;\n        }\n\n        private LineChartData generatePreviewLineChartData() {\n            int numValues = 50;\n\n            List<PointValue> values = new ArrayList<PointValue>();\n            for (int i = 0; i < numValues; ++i) {\n                values.add(new PointValue(i, (float) Math.random() * 100f));\n            }\n\n            Line line = new Line(values);\n            line.setColor(ChartUtils.DEFAULT_DARKEN_COLOR);\n            line.setHasPoints(false);// too many values so don't draw points.\n\n            List<Line> lines = new ArrayList<Line>();\n            lines.add(line);\n\n            LineChartData data = new LineChartData(lines);\n            data.setAxisXBottom(new Axis());\n            data.setAxisYLeft(new Axis().setHasLines(true));\n\n            return data;\n\n        }\n\n        private PieChartData generatePieChartData() {\n            int numValues = 6;\n\n            List<SliceValue> values = new ArrayList<SliceValue>();\n            for (int i = 0; i < numValues; ++i) {\n                values.add(new SliceValue((float) Math.random() * 30 + 15, ChartUtils.pickColor()));\n            }\n\n            PieChartData data = new PieChartData(values);\n            return data;\n        }\n\n    }\n\n    /**\n     * A {@link FragmentPagerAdapter} that returns a fragment corresponding to one of the sections/tabs/pages.\n     */\n    public class SectionsPagerAdapter extends FragmentPagerAdapter {\n\n        public SectionsPagerAdapter(FragmentManager fm) {\n            super(fm);\n        }\n\n        @Override\n        public Fragment getItem(int position) {\n            // getItem is called to instantiate the fragment for the given page.\n            // Return a PlaceholderFragment (defined as a static inner class below).\n            return PlaceholderFragment.newInstance(position + 1);\n        }\n\n        @Override\n        public int getCount() {\n            return 5;\n        }\n\n        @Override\n        public CharSequence getPageTitle(int position) {\n            switch (position) {\n                case 0:\n                    return \"LineChart\";\n                case 1:\n                    return \"ColumnChart\";\n                case 2:\n                    return \"BubbleChart\";\n                case 3:\n                    return \"PreviewLineChart\";\n                case 4:\n                    return \"PieChart\";\n            }\n            return null;\n        }\n    }\n\n}\n"
  },
  {
    "path": "settings.gradle",
    "content": "include ':hellocharts-samples'\ninclude ':hellocharts-library'\n"
  }
]