Initial checkin of androidplot v0.6.0
This commit is a snapshot of androidplot at tag 0.6.0:
commit 24dfe5d708dd409611fff1aa803ce3324a00db85
Author: bamboo <[email protected]>
Date: Sat Jul 20 20:06:07 2013 +0000
[maven-release-plugin] prepare release 0.6.0
Change-Id: I84fb6af3e0c96b1d3d14e21f518a9b49e1126cef
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..01ce2d5
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,7 @@
+/.metadata/
+/AndroidPlot-Core/target/
+/AndroidPlot-Core/.settings/
+/Examples/DemoApp/.settings/
+/Examples/DemoApp/bin/
+/Examples/DemoApp/gen/
+/Examples/DemoApp/target/
\ No newline at end of file
diff --git a/.idea/ant.xml b/.idea/ant.xml
new file mode 100644
index 0000000..8dcb7fc
--- /dev/null
+++ b/.idea/ant.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4" />
+
diff --git a/.idea/codeStyleSettings.xml b/.idea/codeStyleSettings.xml
new file mode 100644
index 0000000..d63a15e
--- /dev/null
+++ b/.idea/codeStyleSettings.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="ProjectCodeStyleSettingsManager">
+ <option name="PER_PROJECT_SETTINGS">
+ <value>
+ <XML>
+ <option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
+ </XML>
+ </value>
+ </option>
+ </component>
+</project>
+
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 0000000..4802278
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="CompilerConfiguration">
+ <option name="DEFAULT_COMPILER" value="Javac" />
+ <excludeFromCompile>
+ <directory url="file://$PROJECT_DIR$/Examples/StepChartExample/gen" includeSubdirectories="true" />
+ <directory url="file://$PROJECT_DIR$/Examples/SimpleXYPlotExample/gen" includeSubdirectories="true" />
+ <directory url="file://$PROJECT_DIR$/Examples/TimeSeriesExample/gen" includeSubdirectories="true" />
+ <directory url="file://$PROJECT_DIR$/Examples/OrientationSensorExample/gen" includeSubdirectories="true" />
+ <directory url="file://$PROJECT_DIR$/Examples/DynamicXYPlotExample/gen" includeSubdirectories="true" />
+ <directory url="file://$PROJECT_DIR$/Examples/HelperExample/gen" includeSubdirectories="true" />
+ <directory url="file://$PROJECT_DIR$/Examples/gen" includeSubdirectories="true" />
+ <directory url="file://$PROJECT_DIR$/Examples/DemoApp/gen" includeSubdirectories="true" />
+ <directory url="file://$PROJECT_DIR$/Examples/TimedXYPlotExample/gen" includeSubdirectories="true" />
+ <directory url="file://$PROJECT_DIR$/Examples/Quickstart/gen" includeSubdirectories="true" />
+ <directory url="file://$PROJECT_DIR$/Examples/DemoApp/target/generated-sources/annotations" includeSubdirectories="true" />
+ <directory url="file://$PROJECT_DIR$/Examples/DemoApp/target/generated-test-sources/test-annotations" includeSubdirectories="true" />
+ <directory url="file://$PROJECT_DIR$/AndroidPlot-Core/target/generated-sources/annotations" includeSubdirectories="true" />
+ <directory url="file://$PROJECT_DIR$/AndroidPlot-Core/target/generated-test-sources/test-annotations" includeSubdirectories="true" />
+ <directory url="file://$PROJECT_DIR$/AndroidPlot-Core/gen" includeSubdirectories="true" />
+ <directory url="file://$PROJECT_DIR$/gen" includeSubdirectories="true" />
+ </excludeFromCompile>
+ <resourceExtensions>
+ <entry name=".+\.(properties|xml|html|dtd|tld)" />
+ <entry name=".+\.(gif|png|jpeg|jpg)" />
+ </resourceExtensions>
+ <wildcardResourcePatterns>
+ <entry name="?*.properties" />
+ <entry name="?*.xml" />
+ <entry name="?*.gif" />
+ <entry name="?*.png" />
+ <entry name="?*.jpeg" />
+ <entry name="?*.jpg" />
+ <entry name="?*.html" />
+ <entry name="?*.dtd" />
+ <entry name="?*.tld" />
+ <entry name="?*.ftl" />
+ </wildcardResourcePatterns>
+ <annotationProcessing>
+ <profile default="true" name="Default" enabled="false">
+ <processorPath useClasspath="true" />
+ </profile>
+ <profile default="false" name="Maven default annotation processors profile" enabled="true">
+ <sourceOutputDir name="target/generated-sources/annotations" />
+ <sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
+ <outputRelativeToContentRoot value="true" />
+ <processorPath useClasspath="true" />
+ <module name="androidplot-core" />
+ <module name="DemoApp" />
+ </profile>
+ </annotationProcessing>
+ <bytecodeTargetLevel>
+ <module name="androidplot-core" target="1.6" />
+ </bytecodeTargetLevel>
+ </component>
+</project>
+
diff --git a/.idea/copyright/AndroidPlot_Apache_2_0.xml b/.idea/copyright/AndroidPlot_Apache_2_0.xml
new file mode 100644
index 0000000..af0b58d
--- /dev/null
+++ b/.idea/copyright/AndroidPlot_Apache_2_0.xml
@@ -0,0 +1,9 @@
+<component name="CopyrightManager">
+ <copyright>
+ <option name="notice" value="Copyright &#36;today.year AndroidPlot.com Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License." />
+ <option name="keyword" value="Copyright" />
+ <option name="allowReplaceKeyword" value="" />
+ <option name="myName" value="AndroidPlot Apache 2.0" />
+ <option name="myLocal" value="true" />
+ </copyright>
+</component>
\ No newline at end of file
diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..a8fb46f
--- /dev/null
+++ b/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,7 @@
+<component name="CopyrightManager">
+ <settings default="AndroidPlot Apache 2.0">
+ <module2copyright>
+ <element module="All" copyright="AndroidPlot Apache 2.0" />
+ </module2copyright>
+ </settings>
+</component>
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 0000000..b7e4cd4
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false">
+ <file url="file://$PROJECT_DIR$" charset="UTF-8" />
+ <file url="file://$PROJECT_DIR$/AndroidPlot-Core" charset="UTF-8" />
+ <file url="file://$PROJECT_DIR$/Examples/DemoApp" charset="UTF-8" />
+ </component>
+</project>
+
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..bfdc003
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,153 @@
+<component name="InspectionProjectProfileManager">
+ <profile version="1.0" is_locked="false">
+ <option name="myName" value="Project Default" />
+ <option name="myLocal" value="false" />
+ <inspection_tool class="AbstractBeanReferencesInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="AssertEqualsBetweenInconvertibleTypesTestNG" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="AutowiredDependenciesInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="ComponentNotRegistered" enabled="false" level="WARNING" enabled_by_default="false">
+ <option name="CHECK_ACTIONS" value="true" />
+ <option name="IGNORE_NON_PUBLIC" value="true" />
+ </inspection_tool>
+ <inspection_tool class="ComponentRegistrationProblems" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="ContextComponentScanInconsistencyInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="ContextJavaBeanUnresolvedMethodsInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="DialogTitleCapitalization" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="DuplicatedBeanNamesInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="ELDeferredExpressionsInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="ELMethodSignatureInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="ELSpecValidationInJSP" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="ELValidationInJSP" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="EjbClassBasicInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="EjbClassWarningsInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="EjbDomInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="EjbEntityClassInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="EjbEntityHomeInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="EjbEntityInterfaceInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="EjbEnvironmentInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="EjbInterceptorInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="EjbInterceptorWarningsInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="EjbInterfaceMethodInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="EjbInterfaceSignatureInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="EjbProhibitedPackageUsageInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="EjbQlInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="EjbRemoteRequirementsInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="EjbSessionHomeInterfaceInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="EjbStaticAccessInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="EjbThisExpressionInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="ExtensionPointBeanClass" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="FlowRequiredBeanTypeInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="HardcodedActionUrl" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="InjectionValueTypeInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="InspectionDescriptionNotFoundInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="InspectionMappingConsistency" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="InspectionUsingGrayColors" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="IntentionDescriptionNotFoundInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="JavaeeApplicationDomInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="JdkProxiedBeanTypeInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="JsfJamExtendsClassInconsistencyInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="JspAbsolutePathInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="JspDirectiveInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="JspPropertiesInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="MVCPathVariableInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="ManagedBeanClassInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="MimeType" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="MissedExecutable" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="PlayCustomTagNameInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="PlayPropertyInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="PluginXmlValidity" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="ReferencesToClassesFromDefaultPackagesInJSPFile" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="RequiredBeanTypeInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="SassScssResolvedByNameOnly" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
+ <inspection_tool class="SassUnresolvedFunction" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="SassUnresolvedImport" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="SassUnresolvedMixin" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="SassUnresolvedPlaceholderSelector" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="SassUnresolvedVariable" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="SelfIncludingJspFiles" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="ServletWithoutMappingInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="SpringBatchModel" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="SpringBeanAutowiringInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="SpringBeanConstructorArgInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="SpringBeanDepedencyCheckInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="SpringBeanInstantiationInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="SpringBeanLookupMethodInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="SpringBeanNameConventionInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="SpringContextConfigurationInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="SpringDataJpaMethodInconsistencyInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="SpringElInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="SpringFacetInspection" enabled="false" level="WARNING" enabled_by_default="false">
+ <option name="checkTestFiles" value="false" />
+ </inspection_tool>
+ <inspection_tool class="SpringFactoryMethodInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="SpringHandlersSchemasHighlighting" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="SpringIncorrectResourceTypeInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="SpringInjectionValueConsistencyInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="SpringInjectionValueStyleInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="SpringIntegrationDeprecations21" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="SpringIntegrationModel" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="SpringJavaAutowiringInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="SpringJavaConfigExternalBeansErrorInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="SpringJavaConfigInconsistencyInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="SpringMVCInitBinder" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="SpringMVCViewInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="SpringMessageDispatcherWebXmlInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="SpringModelInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="SpringOsgiElementsInconsistencyInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="SpringOsgiListenerInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="SpringOsgiServiceCommonInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="SpringPlaceholdersInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="SpringPublicFactoryMethodInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="SpringRequiredAnnotationInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="SpringRequiredPropertyInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="SpringScopesInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="SpringSecurityDebugActivatedInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="SpringSecurityFiltersConfiguredInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="SpringSecurityModelInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="SpringStaticMembersAutowiringInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="SpringTransactionalComponentInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="SpringWebServiceAnnotationsInconsistencyInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="SpringWebServicesConfigurationsInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="SqlAddNotNullColumnInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="SqlAmbiguousColumnInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="SqlAutoIncrementDuplicateInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="SqlConstantConditionInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="SqlDeliverTableNameInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="SqlDeprecateTypeInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="SqlDialectInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="SqlDropIndexedColumnInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="SqlIdentifierInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="SqlInsertValuesInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="SqlNoDataSourceInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="SqlPostgresqlSelectFromProcedureInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="SqlResolveInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="SqlShouldBeInGroupByInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="SqlTypeInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="Struts2ModelInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="StrutsInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="StrutsTilesInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="StrutsValidatorFormInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="StrutsValidatorInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="TaglibDomModelInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="TelReferencesInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="UnparsedCustomBeanInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="UnresolvedMessageChannel" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="UseJBColor" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="UtilSchemaInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="ValidatorConfigModelInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="ValidatorModelInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="WebProperties" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="WebWarnings" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="WebflowConfigModelInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="WebflowModelInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="WebflowSetupInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="dependsOnMethodTestNG" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="groupsTestNG" enabled="false" level="WARNING" enabled_by_default="false">
+ <option name="groups">
+ <value>
+ <list size="0" />
+ </value>
+ </option>
+ </inspection_tool>
+ </profile>
+</component>
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..3b31283
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,7 @@
+<component name="InspectionProjectProfileManager">
+ <settings>
+ <option name="PROJECT_PROFILE" value="Project Default" />
+ <option name="USE_PROJECT_PROFILE" value="true" />
+ <version value="1.0" />
+ </settings>
+</component>
\ No newline at end of file
diff --git a/.idea/libraries/Maven__com_google_android_android_4_1_1_4.xml b/.idea/libraries/Maven__com_google_android_android_4_1_1_4.xml
new file mode 100644
index 0000000..fca0234
--- /dev/null
+++ b/.idea/libraries/Maven__com_google_android_android_4_1_1_4.xml
@@ -0,0 +1,13 @@
+<component name="libraryTable">
+ <library name="Maven: com.google.android:android:4.1.1.4">
+ <CLASSES>
+ <root url="jar://$MAVEN_REPOSITORY$/com/google/android/android/4.1.1.4/android-4.1.1.4.jar!/" />
+ </CLASSES>
+ <JAVADOC>
+ <root url="jar://$MAVEN_REPOSITORY$/com/google/android/android/4.1.1.4/android-4.1.1.4-javadoc.jar!/" />
+ </JAVADOC>
+ <SOURCES>
+ <root url="jar://$MAVEN_REPOSITORY$/com/google/android/android/4.1.1.4/android-4.1.1.4-sources.jar!/" />
+ </SOURCES>
+ </library>
+</component>
\ No newline at end of file
diff --git a/.idea/libraries/Maven__commons_codec_commons_codec_1_3.xml b/.idea/libraries/Maven__commons_codec_commons_codec_1_3.xml
new file mode 100644
index 0000000..3688001
--- /dev/null
+++ b/.idea/libraries/Maven__commons_codec_commons_codec_1_3.xml
@@ -0,0 +1,13 @@
+<component name="libraryTable">
+ <library name="Maven: commons-codec:commons-codec:1.3">
+ <CLASSES>
+ <root url="jar://$MAVEN_REPOSITORY$/commons-codec/commons-codec/1.3/commons-codec-1.3.jar!/" />
+ </CLASSES>
+ <JAVADOC>
+ <root url="jar://$MAVEN_REPOSITORY$/commons-codec/commons-codec/1.3/commons-codec-1.3-javadoc.jar!/" />
+ </JAVADOC>
+ <SOURCES>
+ <root url="jar://$MAVEN_REPOSITORY$/commons-codec/commons-codec/1.3/commons-codec-1.3-sources.jar!/" />
+ </SOURCES>
+ </library>
+</component>
\ No newline at end of file
diff --git a/.idea/libraries/Maven__commons_logging_commons_logging_1_1_1.xml b/.idea/libraries/Maven__commons_logging_commons_logging_1_1_1.xml
new file mode 100644
index 0000000..b770f56
--- /dev/null
+++ b/.idea/libraries/Maven__commons_logging_commons_logging_1_1_1.xml
@@ -0,0 +1,13 @@
+<component name="libraryTable">
+ <library name="Maven: commons-logging:commons-logging:1.1.1">
+ <CLASSES>
+ <root url="jar://$MAVEN_REPOSITORY$/commons-logging/commons-logging/1.1.1/commons-logging-1.1.1.jar!/" />
+ </CLASSES>
+ <JAVADOC>
+ <root url="jar://$MAVEN_REPOSITORY$/commons-logging/commons-logging/1.1.1/commons-logging-1.1.1-javadoc.jar!/" />
+ </JAVADOC>
+ <SOURCES>
+ <root url="jar://$MAVEN_REPOSITORY$/commons-logging/commons-logging/1.1.1/commons-logging-1.1.1-sources.jar!/" />
+ </SOURCES>
+ </library>
+</component>
\ No newline at end of file
diff --git a/.idea/libraries/Maven__junit_junit_4_8_1.xml b/.idea/libraries/Maven__junit_junit_4_8_1.xml
new file mode 100644
index 0000000..bb76199
--- /dev/null
+++ b/.idea/libraries/Maven__junit_junit_4_8_1.xml
@@ -0,0 +1,13 @@
+<component name="libraryTable">
+ <library name="Maven: junit:junit:4.8.1">
+ <CLASSES>
+ <root url="jar://$MAVEN_REPOSITORY$/junit/junit/4.8.1/junit-4.8.1.jar!/" />
+ </CLASSES>
+ <JAVADOC>
+ <root url="jar://$MAVEN_REPOSITORY$/junit/junit/4.8.1/junit-4.8.1-javadoc.jar!/" />
+ </JAVADOC>
+ <SOURCES>
+ <root url="jar://$MAVEN_REPOSITORY$/junit/junit/4.8.1/junit-4.8.1-sources.jar!/" />
+ </SOURCES>
+ </library>
+</component>
\ No newline at end of file
diff --git a/.idea/libraries/Maven__mockit_jmockit_0_999_3.xml b/.idea/libraries/Maven__mockit_jmockit_0_999_3.xml
new file mode 100644
index 0000000..99058c9
--- /dev/null
+++ b/.idea/libraries/Maven__mockit_jmockit_0_999_3.xml
@@ -0,0 +1,13 @@
+<component name="libraryTable">
+ <library name="Maven: mockit:jmockit:0.999.3">
+ <CLASSES>
+ <root url="jar://$MAVEN_REPOSITORY$/mockit/jmockit/0.999.3/jmockit-0.999.3.jar!/" />
+ </CLASSES>
+ <JAVADOC>
+ <root url="jar://$MAVEN_REPOSITORY$/mockit/jmockit/0.999.3/jmockit-0.999.3-javadoc.jar!/" />
+ </JAVADOC>
+ <SOURCES>
+ <root url="jar://$MAVEN_REPOSITORY$/mockit/jmockit/0.999.3/jmockit-0.999.3-sources.jar!/" />
+ </SOURCES>
+ </library>
+</component>
\ No newline at end of file
diff --git a/.idea/libraries/Maven__org_apache_httpcomponents_httpclient_4_0_1.xml b/.idea/libraries/Maven__org_apache_httpcomponents_httpclient_4_0_1.xml
new file mode 100644
index 0000000..a9ca4c6
--- /dev/null
+++ b/.idea/libraries/Maven__org_apache_httpcomponents_httpclient_4_0_1.xml
@@ -0,0 +1,13 @@
+<component name="libraryTable">
+ <library name="Maven: org.apache.httpcomponents:httpclient:4.0.1">
+ <CLASSES>
+ <root url="jar://$MAVEN_REPOSITORY$/org/apache/httpcomponents/httpclient/4.0.1/httpclient-4.0.1.jar!/" />
+ </CLASSES>
+ <JAVADOC>
+ <root url="jar://$MAVEN_REPOSITORY$/org/apache/httpcomponents/httpclient/4.0.1/httpclient-4.0.1-javadoc.jar!/" />
+ </JAVADOC>
+ <SOURCES>
+ <root url="jar://$MAVEN_REPOSITORY$/org/apache/httpcomponents/httpclient/4.0.1/httpclient-4.0.1-sources.jar!/" />
+ </SOURCES>
+ </library>
+</component>
\ No newline at end of file
diff --git a/.idea/libraries/Maven__org_apache_httpcomponents_httpcore_4_0_1.xml b/.idea/libraries/Maven__org_apache_httpcomponents_httpcore_4_0_1.xml
new file mode 100644
index 0000000..eee5c06
--- /dev/null
+++ b/.idea/libraries/Maven__org_apache_httpcomponents_httpcore_4_0_1.xml
@@ -0,0 +1,13 @@
+<component name="libraryTable">
+ <library name="Maven: org.apache.httpcomponents:httpcore:4.0.1">
+ <CLASSES>
+ <root url="jar://$MAVEN_REPOSITORY$/org/apache/httpcomponents/httpcore/4.0.1/httpcore-4.0.1.jar!/" />
+ </CLASSES>
+ <JAVADOC>
+ <root url="jar://$MAVEN_REPOSITORY$/org/apache/httpcomponents/httpcore/4.0.1/httpcore-4.0.1-javadoc.jar!/" />
+ </JAVADOC>
+ <SOURCES>
+ <root url="jar://$MAVEN_REPOSITORY$/org/apache/httpcomponents/httpcore/4.0.1/httpcore-4.0.1-sources.jar!/" />
+ </SOURCES>
+ </library>
+</component>
\ No newline at end of file
diff --git a/.idea/libraries/Maven__org_json_json_20080701.xml b/.idea/libraries/Maven__org_json_json_20080701.xml
new file mode 100644
index 0000000..b86a8bf
--- /dev/null
+++ b/.idea/libraries/Maven__org_json_json_20080701.xml
@@ -0,0 +1,13 @@
+<component name="libraryTable">
+ <library name="Maven: org.json:json:20080701">
+ <CLASSES>
+ <root url="jar://$MAVEN_REPOSITORY$/org/json/json/20080701/json-20080701.jar!/" />
+ </CLASSES>
+ <JAVADOC>
+ <root url="jar://$MAVEN_REPOSITORY$/org/json/json/20080701/json-20080701-javadoc.jar!/" />
+ </JAVADOC>
+ <SOURCES>
+ <root url="jar://$MAVEN_REPOSITORY$/org/json/json/20080701/json-20080701-sources.jar!/" />
+ </SOURCES>
+ </library>
+</component>
\ No newline at end of file
diff --git a/.idea/libraries/Maven__org_khronos_opengl_api_gl1_1_android_2_1_r1.xml b/.idea/libraries/Maven__org_khronos_opengl_api_gl1_1_android_2_1_r1.xml
new file mode 100644
index 0000000..2fe6214
--- /dev/null
+++ b/.idea/libraries/Maven__org_khronos_opengl_api_gl1_1_android_2_1_r1.xml
@@ -0,0 +1,13 @@
+<component name="libraryTable">
+ <library name="Maven: org.khronos:opengl-api:gl1.1-android-2.1_r1">
+ <CLASSES>
+ <root url="jar://$MAVEN_REPOSITORY$/org/khronos/opengl-api/gl1.1-android-2.1_r1/opengl-api-gl1.1-android-2.1_r1.jar!/" />
+ </CLASSES>
+ <JAVADOC>
+ <root url="jar://$MAVEN_REPOSITORY$/org/khronos/opengl-api/gl1.1-android-2.1_r1/opengl-api-gl1.1-android-2.1_r1-javadoc.jar!/" />
+ </JAVADOC>
+ <SOURCES>
+ <root url="jar://$MAVEN_REPOSITORY$/org/khronos/opengl-api/gl1.1-android-2.1_r1/opengl-api-gl1.1-android-2.1_r1-sources.jar!/" />
+ </SOURCES>
+ </library>
+</component>
\ No newline at end of file
diff --git a/.idea/libraries/Maven__xerces_xmlParserAPIs_2_6_2.xml b/.idea/libraries/Maven__xerces_xmlParserAPIs_2_6_2.xml
new file mode 100644
index 0000000..96cae4c
--- /dev/null
+++ b/.idea/libraries/Maven__xerces_xmlParserAPIs_2_6_2.xml
@@ -0,0 +1,13 @@
+<component name="libraryTable">
+ <library name="Maven: xerces:xmlParserAPIs:2.6.2">
+ <CLASSES>
+ <root url="jar://$MAVEN_REPOSITORY$/xerces/xmlParserAPIs/2.6.2/xmlParserAPIs-2.6.2.jar!/" />
+ </CLASSES>
+ <JAVADOC>
+ <root url="jar://$MAVEN_REPOSITORY$/xerces/xmlParserAPIs/2.6.2/xmlParserAPIs-2.6.2-javadoc.jar!/" />
+ </JAVADOC>
+ <SOURCES>
+ <root url="jar://$MAVEN_REPOSITORY$/xerces/xmlParserAPIs/2.6.2/xmlParserAPIs-2.6.2-sources.jar!/" />
+ </SOURCES>
+ </library>
+</component>
\ No newline at end of file
diff --git a/.idea/libraries/Maven__xpp3_xpp3_1_1_4c.xml b/.idea/libraries/Maven__xpp3_xpp3_1_1_4c.xml
new file mode 100644
index 0000000..26694b3
--- /dev/null
+++ b/.idea/libraries/Maven__xpp3_xpp3_1_1_4c.xml
@@ -0,0 +1,13 @@
+<component name="libraryTable">
+ <library name="Maven: xpp3:xpp3:1.1.4c">
+ <CLASSES>
+ <root url="jar://$MAVEN_REPOSITORY$/xpp3/xpp3/1.1.4c/xpp3-1.1.4c.jar!/" />
+ </CLASSES>
+ <JAVADOC>
+ <root url="jar://$MAVEN_REPOSITORY$/xpp3/xpp3/1.1.4c/xpp3-1.1.4c-javadoc.jar!/" />
+ </JAVADOC>
+ <SOURCES>
+ <root url="jar://$MAVEN_REPOSITORY$/xpp3/xpp3/1.1.4c/xpp3-1.1.4c-sources.jar!/" />
+ </SOURCES>
+ </library>
+</component>
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..689ddbb
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="EntryPointsManager">
+ <entry_points version="2.0" />
+ </component>
+ <component name="FrameworkDetectionExcludesConfiguration">
+ <file type="android" url="file://$PROJECT_DIR$/AndroidPlot-Core/src/AndroidManifest.xml" />
+ <file type="android" url="file://$PROJECT_DIR$/Examples/DemoApp" />
+ <file type="android" url="file://$PROJECT_DIR$/Tests/AndroidManifest.xml" />
+ </component>
+ <component name="IdProvider" IDEtalkID="96A70927FCB4C9DA89E754C51C667B37" />
+ <component name="JavadocGenerationManager">
+ <option name="OUTPUT_DIRECTORY" />
+ <option name="OPTION_SCOPE" value="protected" />
+ <option name="OPTION_HIERARCHY" value="true" />
+ <option name="OPTION_NAVIGATOR" value="true" />
+ <option name="OPTION_INDEX" value="true" />
+ <option name="OPTION_SEPARATE_INDEX" value="true" />
+ <option name="OPTION_DOCUMENT_TAG_USE" value="false" />
+ <option name="OPTION_DOCUMENT_TAG_AUTHOR" value="false" />
+ <option name="OPTION_DOCUMENT_TAG_VERSION" value="false" />
+ <option name="OPTION_DOCUMENT_TAG_DEPRECATED" value="true" />
+ <option name="OPTION_DEPRECATED_LIST" value="true" />
+ <option name="OTHER_OPTIONS" value="" />
+ <option name="HEAP_SIZE" />
+ <option name="LOCALE" />
+ <option name="OPEN_IN_BROWSER" value="true" />
+ </component>
+ <component name="MavenProjectsManager">
+ <option name="originalFiles">
+ <list>
+ <option value="$PROJECT_DIR$/AndroidPlot-Lib/pom.xml" />
+ <option value="$PROJECT_DIR$/pom.xml" />
+ <option value="$PROJECT_DIR$/Examples/DemoApp/pom.xml" />
+ </list>
+ </option>
+ </component>
+ <component name="ProjectResources">
+ <default-html-doctype>http://www.w3.org/1999/xhtml</default-html-doctype>
+ </component>
+ <component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" assert-keyword="true" jdk-15="true" project-jdk-name="Maven Android 4.1 Platform" project-jdk-type="Android SDK">
+ <output url="file://$PROJECT_DIR$/out" />
+ </component>
+ <component name="RegexUtilComponent" text="1900-01-01 2007/08/13 1900.01.01 1900 01 01 1900-01.01 1900 13 01 1900 02 31" flags="0" regex="(19|20)\d\d([- /.])(0[1-9]|1[012])\2(0[1-9]|[12][0-9]|3[01])" mode="0" />
+ <component name="SvnBranchConfigurationManager">
+ <option name="myConfigurationMap">
+ <map>
+ <entry key="$PROJECT_DIR$">
+ <value>
+ <SvnBranchConfiguration>
+ <option name="branchUrls">
+ <list>
+ <option value="https://bridalveil.jira.com/svn/ANDROIDPLOT/branches" />
+ <option value="https://bridalveil.jira.com/svn/ANDROIDPLOT/tags" />
+ </list>
+ </option>
+ <option name="trunkUrl" value="https://bridalveil.jira.com/svn/ANDROIDPLOT/trunk" />
+ </SvnBranchConfiguration>
+ </value>
+ </entry>
+ <entry key="$PROJECT_DIR$/AndroidPlot-Core">
+ <value>
+ <SvnBranchConfiguration>
+ <option name="branchUrls">
+ <list>
+ <option value="https://bridalveil.jira.com/svn/ANDROIDPLOT/branches" />
+ <option value="https://bridalveil.jira.com/svn/ANDROIDPLOT/tags" />
+ </list>
+ </option>
+ <option name="trunkUrl" value="https://bridalveil.jira.com/svn/ANDROIDPLOT/trunk" />
+ </SvnBranchConfiguration>
+ </value>
+ </entry>
+ <entry key="$PROJECT_DIR$/Examples/DemoApp">
+ <value>
+ <SvnBranchConfiguration>
+ <option name="trunkUrl" value="https://androidplot.jira.com/svn/ANDROIDPLOT/trunk/Examples/DemoApp" />
+ </SvnBranchConfiguration>
+ </value>
+ </entry>
+ <entry key="$PROJECT_DIR$/Examples/OrientationSensorExample">
+ <value>
+ <SvnBranchConfiguration>
+ <option name="branchUrls">
+ <list>
+ <option value="https://bridalveil.jira.com/svn/ANDROIDPLOT/branches" />
+ <option value="https://bridalveil.jira.com/svn/ANDROIDPLOT/tags" />
+ </list>
+ </option>
+ <option name="trunkUrl" value="https://bridalveil.jira.com/svn/ANDROIDPLOT/trunk" />
+ </SvnBranchConfiguration>
+ </value>
+ </entry>
+ </map>
+ </option>
+ <option name="mySupportsUserInfoFilter" value="true" />
+ </component>
+ <component name="WebServicesPlugin" addRequiredLibraries="true" />
+</project>
+
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..a79b83b
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="ProjectModuleManager">
+ <modules>
+ <module fileurl="file://$PROJECT_DIR$/Examples/DemoApp/DemoApp.iml" filepath="$PROJECT_DIR$/Examples/DemoApp/DemoApp.iml" />
+ <module fileurl="file://$PROJECT_DIR$/androidplot.iml" filepath="$PROJECT_DIR$/androidplot.iml" />
+ <module fileurl="file://$PROJECT_DIR$/AndroidPlot-Core/androidplot-core.iml" filepath="$PROJECT_DIR$/AndroidPlot-Core/androidplot-core.iml" />
+ </modules>
+ </component>
+</project>
+
diff --git a/.idea/runConfigurations/DemoApp.xml b/.idea/runConfigurations/DemoApp.xml
new file mode 100644
index 0000000..733ea80
--- /dev/null
+++ b/.idea/runConfigurations/DemoApp.xml
@@ -0,0 +1,22 @@
+<component name="ProjectRunConfigurationManager">
+ <configuration default="false" name="DemoApp" type="AndroidRunConfigurationType" factoryName="Android Application">
+ <module name="DemoApp" />
+ <option name="ACTIVITY_CLASS" value="" />
+ <option name="MODE" value="default_activity" />
+ <option name="DEPLOY" value="true" />
+ <option name="TARGET_SELECTION_MODE" value="USB_DEVICE" />
+ <option name="PREFERRED_AVD" value="" />
+ <option name="USE_COMMAND_LINE" value="true" />
+ <option name="COMMAND_LINE" value="" />
+ <option name="WIPE_USER_DATA" value="false" />
+ <option name="DISABLE_BOOT_ANIMATION" value="false" />
+ <option name="NETWORK_SPEED" value="full" />
+ <option name="NETWORK_LATENCY" value="none" />
+ <option name="CLEAR_LOGCAT" value="true" />
+ <RunnerSettings RunnerId="AndroidDebugRunner" />
+ <RunnerSettings RunnerId="Run" />
+ <ConfigurationWrapper RunnerId="AndroidDebugRunner" />
+ <ConfigurationWrapper RunnerId="Run" />
+ <method />
+ </configuration>
+</component>
\ No newline at end of file
diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml
new file mode 100644
index 0000000..1e7cce4
--- /dev/null
+++ b/.idea/uiDesigner.xml
@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="Palette2">
+ <group name="Swing">
+ <item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
+ </item>
+ <item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
+ </item>
+ <item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
+ </item>
+ <item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">
+ <default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
+ </item>
+ <item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
+ <initial-values>
+ <property name="text" value="Button" />
+ </initial-values>
+ </item>
+ <item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
+ <initial-values>
+ <property name="text" value="RadioButton" />
+ </initial-values>
+ </item>
+ <item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
+ <initial-values>
+ <property name="text" value="CheckBox" />
+ </initial-values>
+ </item>
+ <item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
+ <initial-values>
+ <property name="text" value="Label" />
+ </initial-values>
+ </item>
+ <item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+ <preferred-size width="150" height="-1" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+ <preferred-size width="150" height="-1" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+ <preferred-size width="150" height="-1" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+ <preferred-size width="150" height="50" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+ <preferred-size width="150" height="50" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+ <preferred-size width="150" height="50" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
+ </item>
+ <item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+ <preferred-size width="150" height="50" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
+ <preferred-size width="150" height="50" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+ <preferred-size width="150" height="50" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
+ <preferred-size width="200" height="200" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
+ <preferred-size width="200" height="200" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
+ </item>
+ <item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
+ </item>
+ <item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
+ </item>
+ <item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
+ </item>
+ <item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
+ <preferred-size width="-1" height="20" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
+ </item>
+ <item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
+ </item>
+ </group>
+ </component>
+</project>
+
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..ab55cf1
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="VcsDirectoryMappings">
+ <mapping directory="$PROJECT_DIR$" vcs="Git" />
+ </component>
+</project>
+
diff --git a/AndroidPlot-Core/.classpath b/AndroidPlot-Core/.classpath
new file mode 100644
index 0000000..dcd1f46
--- /dev/null
+++ b/AndroidPlot-Core/.classpath
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" output="target/classes" path="src/main/java"/>
+ <classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="con" path="org.maven.ide.eclipse.MAVEN2_CLASSPATH_CONTAINER"/>
+ <classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"/>
+ <classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/AndroidPlot-Core/.project b/AndroidPlot-Core/.project
new file mode 100644
index 0000000..1cb89ea
--- /dev/null
+++ b/AndroidPlot-Core/.project
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>AndroidPlot-Core</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.maven.ide.eclipse.maven2Builder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.m2e.core.maven2Builder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>org.eclipse.m2e.core.maven2Nature</nature>
+ <nature>org.maven.ide.eclipse.maven2Nature</nature>
+ </natures>
+</projectDescription>
diff --git a/AndroidPlot-Core/androidplot-core.iml b/AndroidPlot-Core/androidplot-core.iml
new file mode 100644
index 0000000..53f8e0a
--- /dev/null
+++ b/AndroidPlot-Core/androidplot-core.iml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
+ <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_6" inherit-compiler-output="false">
+ <output url="file://$MODULE_DIR$/target/classes" />
+ <output-test url="file://$MODULE_DIR$/target/test-classes" />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$/gen" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
+ <excludeFolder url="file://$MODULE_DIR$/target" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="library" scope="TEST" name="Maven: mockit:jmockit:0.999.3" level="project" />
+ <orderEntry type="library" scope="TEST" name="Maven: junit:junit:4.8.1" level="project" />
+ <orderEntry type="library" scope="PROVIDED" name="Maven: com.google.android:android:4.1.1.4" level="project" />
+ <orderEntry type="library" scope="PROVIDED" name="Maven: commons-logging:commons-logging:1.1.1" level="project" />
+ <orderEntry type="library" scope="PROVIDED" name="Maven: org.apache.httpcomponents:httpclient:4.0.1" level="project" />
+ <orderEntry type="library" scope="PROVIDED" name="Maven: org.apache.httpcomponents:httpcore:4.0.1" level="project" />
+ <orderEntry type="library" scope="PROVIDED" name="Maven: commons-codec:commons-codec:1.3" level="project" />
+ <orderEntry type="library" scope="PROVIDED" name="Maven: org.khronos:opengl-api:gl1.1-android-2.1_r1" level="project" />
+ <orderEntry type="library" scope="PROVIDED" name="Maven: xerces:xmlParserAPIs:2.6.2" level="project" />
+ <orderEntry type="library" scope="PROVIDED" name="Maven: xpp3:xpp3:1.1.4c" level="project" />
+ <orderEntry type="library" scope="PROVIDED" name="Maven: org.json:json:20080701" level="project" />
+ </component>
+</module>
+
diff --git a/AndroidPlot-Core/pom.xml b/AndroidPlot-Core/pom.xml
new file mode 100644
index 0000000..ffdb40b
--- /dev/null
+++ b/AndroidPlot-Core/pom.xml
@@ -0,0 +1,182 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2012 AndroidPlot.com
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>com.androidplot</groupId>
+ <artifactId>androidplot</artifactId>
+ <version>0.6.0</version>
+ </parent>
+ <artifactId>androidplot-core</artifactId>
+ <!--<version>${applicationVersion}</version>-->
+ <name>AndroidPlot-Core</name>
+ <description>AndroidPlot core library</description>
+
+ <profiles>
+ <profile>
+ <id>default</id>
+ <activation>
+ <activeByDefault>true</activeByDefault>
+ </activation>
+ <properties>
+ <path.to.rt.jar>${java.home}/lib/rt.jar</path.to.rt.jar>
+ </properties>
+ </profile>
+ <profile>
+ <id>osx</id>
+ <activation>
+ <os>
+ <family>mac</family>
+ </os>
+ </activation>
+ <properties>
+ <path.to.rt.jar>${java.home}/../Classes/classes.jar</path.to.rt.jar>
+ </properties>
+ </profile>
+ </profiles>
+
+ <!--<repositories>
+ <repository>
+ <id>central</id>
+ <name>Maven Central</name>
+ <url>http://repo1.maven.org/maven2/</url>
+ </repository></repositories>
+-->
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-gpg-plugin</artifactId>
+ <version>1.4</version>
+ <executions>
+ <execution>
+ <id>sign-artifacts</id>
+ <phase>verify</phase>
+ <goals>
+ <goal>sign</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <version>2.7</version>
+ <configuration>
+ <show>public</show>
+ </configuration>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-source-plugin</artifactId>
+ <version>2.2</version>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>2.0.2</version>
+ <configuration>
+ <source>1.6</source>
+ <target>1.6</target>
+ </configuration>
+ </plugin>
+
+
+
+ <plugin>
+ <groupId>com.pyx4me</groupId>
+ <artifactId>proguard-maven-plugin</artifactId>
+ <version>2.0.4</version>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>proguard</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <obfuscate>true</obfuscate>
+ <options>
+ <option>-allowaccessmodification</option>
+ <option>-keep public class * {public *;}</option>
+ <option>-keepclassmembers enum * {public static **[] values();public static ** valueOf(java.lang.String);}</option>
+ <option>-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod</option>
+ </options>
+ <!--<injar>${project.build.finalName}.jar</injar>
+ <outjar>obfuscated/${project.build.finalName}-release.jar</outjar>-->
+ <outputDirectory>${project.build.directory}</outputDirectory>
+ <libs>
+ <!--<lib>${java.home}/lib/rt.jar</lib>-->
+ <lib>${path.to.rt.jar}</lib>
+ </libs>
+ <addMavenDescriptor>false</addMavenDescriptor>
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <version>2.7</version>
+ <inherited>false</inherited>
+ <configuration>
+ <skip>false</skip>
+ </configuration>
+ </plugin>
+ </plugins>
+
+ </build>
+ <dependencies>
+ <dependency>
+ <groupId>mockit</groupId>
+ <artifactId>jmockit</artifactId>
+ <version>0.999.3</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.8.1</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.google.android</groupId>
+ <artifactId>android</artifactId>
+ </dependency>
+ <!--<dependency>
+ <groupId>com.google.android</groupId>
+ <artifactId>android</artifactId>
+ <version>4.1.1.4</version>
+ <scope>provided</scope>
+ </dependency>-->
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/LineRegion.java b/AndroidPlot-Core/src/main/java/com/androidplot/LineRegion.java
new file mode 100644
index 0000000..0f53711
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/LineRegion.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.androidplot;
+
+/**
+ * A one dimensional region represented by a starting and ending value.
+ */
+public class LineRegion {
+ private Number minVal;
+ private Number maxVal;
+
+ public LineRegion(Number val1, Number v2) {
+ if (val1.doubleValue() < v2.doubleValue()) {
+ this.setMinVal(val1);
+ this.setMaxVal(v2);
+ } else {
+ this.setMinVal(v2);
+ this.setMaxVal(val1);
+ }
+ }
+
+ public static Number measure(Number val1, Number val2) {
+ return new LineRegion(val1, val2).length();
+ }
+
+ public Number length() {
+ return maxVal.doubleValue() - minVal.doubleValue();
+ }
+
+ /**
+ * Tests whether a value is within the given range
+ * @param value
+ * @return
+ */
+ public boolean contains(Number value) {
+ return value.doubleValue() >= minVal.doubleValue() && value.doubleValue() <= maxVal.doubleValue();
+ }
+
+ public boolean intersects(LineRegion lineRegion) {
+ return intersects(lineRegion.getMinVal(), lineRegion.getMaxVal());
+ }
+
+ /**
+ * Tests whether this segment intersects another
+ * @param line2Min
+ * @param line2Max
+ * @return
+ */
+ public boolean intersects(Number line2Min, Number line2Max) {
+
+ //double l1min = getMinVal() == null ? Double.NEGATIVE_INFINITY : getMinVal().doubleValue();
+ //double l1max = getMaxVal() == null ? Double.POSITIVE_INFINITY : getMaxVal().doubleValue();
+
+ //double l2min = line2Min == null ? Double.NEGATIVE_INFINITY : line2Min.doubleValue();
+ //double l2max = line2Max == null ? Double.POSITIVE_INFINITY : line2Max.doubleValue();
+
+
+ // is this line completely within line2?
+ if(line2Min.doubleValue() <= this.minVal.doubleValue() && line2Max.doubleValue() >= this.maxVal.doubleValue()) {
+ return true;
+ // is line1 partially within line2
+ } else return contains(line2Min) || contains(line2Max);
+ }
+
+ public Number getMinVal() {
+ return minVal;
+ }
+
+ public void setMinVal(Number minVal) {
+ if(minVal == null) {
+ throw new NullPointerException("Region values can never be null.");
+ }
+ this.minVal = minVal;
+ }
+
+ public Number getMaxVal() {
+ return maxVal;
+ }
+
+ public void setMaxVal(Number maxVal) {
+ if(maxVal == null) {
+ throw new NullPointerException("Region values can never be null.");
+ }
+ this.maxVal = maxVal;
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/Plot.java b/AndroidPlot-Core/src/main/java/com/androidplot/Plot.java
new file mode 100644
index 0000000..e47e568
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/Plot.java
@@ -0,0 +1,868 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot;
+
+import android.content.Context;
+import android.graphics.*;
+import android.os.Build;
+import android.os.Looper;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import com.androidplot.exception.PlotRenderException;
+import com.androidplot.ui.*;
+import com.androidplot.ui.Formatter;
+import com.androidplot.ui.TextOrientationType;
+import com.androidplot.ui.widget.TextLabelWidget;
+import com.androidplot.ui.widget.Widget;
+import com.androidplot.ui.SeriesRenderer;
+import com.androidplot.util.Configurator;
+import com.androidplot.util.DisplayDimensions;
+import com.androidplot.util.PixelUtils;
+import com.androidplot.ui.XLayoutStyle;
+import com.androidplot.ui.YLayoutStyle;
+
+import java.util.*;
+
+/**
+ * Base class for all other Plot implementations..
+ */
+public abstract class Plot<SeriesType extends Series, FormatterType extends Formatter, RendererType extends SeriesRenderer>
+ extends View implements Resizable{
+ private static final String TAG = Plot.class.getName();
+ private static final String XML_ATTR_PREFIX = "androidplot";
+
+ private static final String ATTR_TITLE = "title";
+ private static final String ATTR_RENDER_MODE = "renderMode";
+
+ public DisplayDimensions getDisplayDimensions() {
+ return displayDims;
+ }
+
+ public enum BorderStyle {
+ ROUNDED,
+ SQUARE,
+ NONE
+ }
+
+ /**
+ * The RenderMode used by a Plot to draw it's self onto the screen. The RenderMode can be set
+ * in two ways.
+ *
+ * In an xml layout:
+ *
+ * <code>
+ * <com.androidplot.xy.XYPlot
+ * android:id="@+id/mySimpleXYPlot"
+ * android:layout_width="fill_parent"
+ * android:layout_height="fill_parent"
+ * title="@string/sxy_title"
+ * renderMode="useBackgroundThread"/>
+ * </code>
+ *
+ * Programatically:
+ *
+ * <code>
+ * XYPlot myPlot = new XYPlot(context "MyPlot", Plot.RenderMode.USE_MAIN_THREAD);
+ * </code>
+ *
+ * A Plot's RenderMode cannot be changed after the plot has been initialized.
+ * @since 0.5.1
+ */
+ public enum RenderMode {
+ /**
+ * Use a second thread and an off-screen buffer to do drawing. This is the preferred method
+ * of drawing dynamic data and static data that consists of a large number of points. This mode
+ * provides more efficient CPU utilization at the cost of increased memory usage. As of
+ * version 0.5.1 this is the default RenderMode.
+ *
+ * XML value: use_background_thread
+ * @since 0.5.1
+ */
+ USE_BACKGROUND_THREAD,
+
+ /**
+ * Do everything in the primary thread. This is the preferred method of drawing static charts
+ * and dynamic data that consists of a small number of points. This mode uses less memory at
+ * the cost of poor CPU utilization.
+ *
+ * XML value: use_main_thread
+ * @since 0.5.1
+ */
+ USE_MAIN_THREAD
+ }
+ private BoxModel boxModel = new BoxModel(3, 3, 3, 3, 3, 3, 3, 3);
+ private BorderStyle borderStyle = Plot.BorderStyle.SQUARE;
+ private float borderRadiusX = 15;
+ private float borderRadiusY = 15;
+ private boolean drawBorderEnabled = true;
+ private Paint borderPaint;
+ private Paint backgroundPaint;
+ private LayoutManager layoutManager;
+ private TextLabelWidget titleWidget;
+ private DisplayDimensions displayDims = new DisplayDimensions();
+ private RenderMode renderMode = RenderMode.USE_MAIN_THREAD;
+ private final BufferedCanvas pingPong = new BufferedCanvas();
+
+ // used to get rid of flickering when drawing offScreenBitmap to the visible Canvas.
+ private final Object renderSynch = new Object();
+
+ /**
+ * Used for caching renderer instances. Note that once a renderer is initialized it remains initialized
+ * for the life of the application; does not and should not be destroyed until the application exits.
+ */
+ private LinkedList<RendererType> renderers;
+
+ /**
+ * Associates lists series and formatter pairs with the class of the Renderer used to render them.
+ */
+ private LinkedHashMap<Class, SeriesAndFormatterList<SeriesType,FormatterType>> seriesRegistry;
+
+ private final ArrayList<PlotListener> listeners;
+
+ private Thread renderThread;
+ private boolean keepRunning = false;
+ private boolean isIdle = true;
+
+ {
+ listeners = new ArrayList<PlotListener>();
+ seriesRegistry = new LinkedHashMap<Class, SeriesAndFormatterList<SeriesType,FormatterType>>();
+ renderers = new LinkedList<RendererType>();
+ borderPaint = new Paint();
+ borderPaint.setColor(Color.rgb(150, 150, 150));
+ borderPaint.setStyle(Paint.Style.STROKE);
+ borderPaint.setStrokeWidth(1.0f);
+ borderPaint.setAntiAlias(true);
+ backgroundPaint = new Paint();
+ backgroundPaint.setColor(Color.DKGRAY);
+ backgroundPaint.setStyle(Paint.Style.FILL);
+ }
+
+
+ /**
+ * Any rendering that utilizes a buffer from this class should synchronize rendering on the instance of this class
+ * that is being used.
+ */
+ private class BufferedCanvas {
+ private volatile Bitmap bgBuffer; // all drawing is done on this buffer.
+ private volatile Bitmap fgBuffer;
+ private Canvas canvas = new Canvas();
+
+ /**
+ * Call this method once drawing on a Canvas retrieved by {@link #getCanvas()} to mark
+ * the buffer as fully rendered. Failure to call this method will result in nothing being drawn.
+ */
+ public synchronized void swap() {
+ Bitmap tmp = bgBuffer;
+ bgBuffer = fgBuffer;
+ fgBuffer = tmp;
+ }
+
+ public synchronized void resize(int h, int w) {
+ if (w <= 0 || h <= 0) {
+ bgBuffer = null;
+ fgBuffer = null;
+ } else {
+ bgBuffer = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_4444);
+ fgBuffer = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_4444);
+ }
+ }
+
+
+ /**
+ * Get a Canvas for drawing. Actual drawing should be synchronized on the instance
+ * of BufferedCanvas being used.
+ * @return The Canvas instance to draw onto. Returns null if drawing buffers have not
+ * been initialized a la {@link #resize(int, int)}.
+ */
+ public synchronized Canvas getCanvas() {
+ if(bgBuffer != null) {
+ canvas.setBitmap(bgBuffer);
+ return canvas;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * @return The most recent fully rendered Bitmsp
+ */
+ public Bitmap getBitmap() {
+ return fgBuffer;
+ }
+ }
+
+ /**
+ * Convenience constructor - wraps {@link #Plot(android.content.Context, String, com.androidplot.Plot.RenderMode)}.
+ * RenderMode is set to {@link RenderMode#USE_BACKGROUND_THREAD}.
+ * @param context
+ * @param title The display title of this Plot.
+ */
+ public Plot(Context context, String title) {
+ this(context, title, RenderMode.USE_MAIN_THREAD);
+ }
+
+ /**
+ * Used for programmatic instantiation.
+ * @param context
+ * @param title The display title of this Plot.
+ */
+ public Plot(Context context, String title, RenderMode mode) {
+ super(context);
+ this.renderMode = mode;
+ init(null, null);
+ setTitle(title);
+ }
+
+
+ /**
+ * Required by super-class. Extending class' implementations should add
+ * the following code immediately before exiting to ensure that loadAttrs
+ * is called only once by the derived class:
+ * <code>
+ * if(getClass().equals(DerivedPlot.class) {
+ * loadAttrs(context, attrs);
+ * }
+ * </code>
+ *
+ * See {@link com.androidplot.xy.XYPlot#XYPlot(android.content.Context, android.util.AttributeSet)}
+ * for an example.
+ * @param context
+ * @param attrs
+ */
+ public Plot(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context, attrs);
+ }
+
+ /**
+ * Required by super-class. Extending class' implementations should add
+ * the following code immediately before exiting to ensure that loadAttrs
+ * is called only once by the derived class:
+ * <code>
+ * if(getClass().equals(DerivedPlot.class) {
+ * loadAttrs(context, attrs);
+ * }
+ * </code>
+ *
+ * See {@link com.androidplot.xy.XYPlot#XYPlot(android.content.Context, android.util.AttributeSet, int)}
+ * for an example.
+ * @param context
+ * @param attrs
+ * @param defStyle
+ */
+ public Plot(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ init(context, attrs);
+ }
+
+ /**
+ * Can be overridden by derived classes to control hardware acceleration state.
+ * Note that this setting is only used on Honeycomb and later environments.
+ * @return True if hardware acceleration is allowed, false otherwise.
+ * @since 0.5.1
+ */
+ @SuppressWarnings("BooleanMethodIsAlwaysInverted")
+ protected boolean isHwAccelerationSupported() {
+ return false;
+ }
+
+ /**
+ * Sets the render mode used by the Plot.
+ * WARNING: This method is not currently designed for general use outside of Configurator.
+ * Attempting to reassign the render mode at runtime will result in unexpected behavior.
+ * @param mode
+ */
+ public void setRenderMode(RenderMode mode) {
+ this.renderMode = mode;
+ }
+
+ /**
+ * Concrete implementations should do any final setup / initialization
+ * here. Immediately following this method's invocation, AndroidPlot assumes
+ * that the Plot instance is ready for final configuration via the Configurator.
+ */
+ protected abstract void onPreInit();
+
+
+ private void init(Context context, AttributeSet attrs) {
+ PixelUtils.init(getContext());
+ layoutManager = new LayoutManager();
+ titleWidget = new TextLabelWidget(layoutManager, new SizeMetrics(25,
+ SizeLayoutType.ABSOLUTE, 100,
+ SizeLayoutType.ABSOLUTE),
+ TextOrientationType.HORIZONTAL);
+ titleWidget.position(0, XLayoutStyle.RELATIVE_TO_CENTER, 0,
+ YLayoutStyle.ABSOLUTE_FROM_TOP, AnchorPosition.TOP_MIDDLE);
+
+ onPreInit();
+ // make sure the title widget is always the topmost widget:
+ layoutManager.moveToTop(titleWidget);
+ if(context != null && attrs != null) {
+ loadAttrs(attrs);
+ }
+
+ layoutManager.onPostInit();
+ Log.d(TAG, "AndroidPlot RenderMode: " + renderMode);
+ if (renderMode == RenderMode.USE_BACKGROUND_THREAD) {
+ renderThread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+
+ keepRunning = true;
+ while (keepRunning) {
+ isIdle = false;
+ synchronized (pingPong) {
+ Canvas c = pingPong.getCanvas();
+ renderOnCanvas(c);
+ pingPong.swap();
+ }
+ synchronized (renderSynch) {
+ postInvalidate();
+ // prevent this thread from becoming an orphan
+ // after the view is destroyed
+ if (keepRunning) {
+ try {
+ renderSynch.wait();
+ } catch (InterruptedException e) {
+ keepRunning = false;
+ }
+ }
+ }
+ }
+ System.out.println("AndroidPlot render thread finished.");
+ }
+ });
+ }
+ }
+
+ /**
+ * Parse XML Attributes. Should only be called once and at the end of the base class constructor.
+ *
+ * @param attrs
+ */
+ private void loadAttrs(AttributeSet attrs) {
+
+ if (attrs != null) {
+ // filter out androidplot prefixed attrs:
+ HashMap<String, String> attrHash = new HashMap<String, String>();
+ for (int i = 0; i < attrs.getAttributeCount(); i++) {
+ String attrName = attrs.getAttributeName(i);
+
+ // case insensitive check to see if this attr begins with our prefix:
+ if (attrName.toUpperCase().startsWith(XML_ATTR_PREFIX.toUpperCase())) {
+ attrHash.put(attrName.substring(XML_ATTR_PREFIX.length() + 1), attrs.getAttributeValue(i));
+ }
+ }
+ Configurator.configure(getContext(), this, attrHash);
+ }
+ }
+
+ public RenderMode getRenderMode() {
+ return renderMode;
+ }
+
+ public synchronized boolean addListener(PlotListener listener) {
+ return !listeners.contains(listener) && listeners.add(listener);
+ }
+
+ public synchronized boolean removeListener(PlotListener listener) {
+ return listeners.remove(listener);
+ }
+
+ protected void notifyListenersBeforeDraw(Canvas canvas) {
+ for (PlotListener listener : listeners) {
+ listener.onBeforeDraw(this, canvas);
+ }
+ }
+
+ protected void notifyListenersAfterDraw(Canvas canvas) {
+ for (PlotListener listener : listeners) {
+ listener.onAfterDraw(this, canvas);
+ }
+ }
+
+ /**
+ * @param series
+ */
+ public synchronized boolean addSeries(SeriesType series, FormatterType formatter) {
+ Class rendererClass = formatter.getRendererClass();
+ SeriesAndFormatterList<SeriesType, FormatterType> sfList = seriesRegistry.get(rendererClass);
+
+ // if there is no list for this renderer type, we need to getInstance one:
+ if(sfList == null) {
+ // make sure there is not already an instance of this renderer available:
+ if(getRenderer(rendererClass) == null) {
+ renderers.add((RendererType) formatter.getRendererInstance(this));
+ }
+ sfList = new SeriesAndFormatterList<SeriesType,FormatterType>();
+ seriesRegistry.put(rendererClass, sfList);
+ }
+
+ // if this series implements PlotListener, add it as a listener:
+ if(series instanceof PlotListener) {
+ addListener((PlotListener)series);
+ }
+
+ // do nothing if this series already associated with the renderer:
+ if(sfList.contains(series)) {
+ return false;
+ } else {
+ sfList.add(series, formatter);
+ return true;
+ }
+ }
+
+ public synchronized boolean removeSeries(SeriesType series, Class rendererClass) {
+ boolean result = seriesRegistry.get(rendererClass).remove(series);
+ if(seriesRegistry.get(rendererClass).size() <= 0) {
+ seriesRegistry.remove(rendererClass);
+ }
+
+ // if series implements PlotListener, remove it from listeners:
+ if(series instanceof PlotListener) {
+ removeListener((PlotListener) series);
+ }
+ return result;
+ }
+
+ /**
+ * Remove all occorrences of series from all renderers
+ * @param series
+ */
+ public synchronized void removeSeries(SeriesType series) {
+
+ // remove all occurrences of series from all renderers:
+ for(Class rendererClass : seriesRegistry.keySet()) {
+ seriesRegistry.get(rendererClass).remove(series);
+ }
+
+ // remove empty SeriesAndFormatterList instances from the registry:
+ for(Iterator<SeriesAndFormatterList<SeriesType,FormatterType>> it = seriesRegistry.values().iterator(); it.hasNext();) {
+ if(it.next().size() <= 0) {
+ it.remove();
+ }
+ }
+
+ // if series implements PlotListener, remove it from listeners:
+ if (series instanceof PlotListener) {
+ removeListener((PlotListener) series);
+ }
+ }
+
+ /**
+ * Remove all series from all renderers
+ */
+ public void clear() {
+ for(Iterator<SeriesAndFormatterList<SeriesType,FormatterType>> it = seriesRegistry.values().iterator(); it.hasNext();) {
+ it.next();
+ it.remove();
+ }
+ }
+
+ public boolean isEmpty() {
+ return seriesRegistry.isEmpty();
+ }
+
+ public FormatterType getFormatter(SeriesType series, Class rendererClass) {
+ return seriesRegistry.get(rendererClass).getFormatter(series);
+ }
+
+ public SeriesAndFormatterList<SeriesType,FormatterType> getSeriesAndFormatterListForRenderer(Class rendererClass) {
+ return seriesRegistry.get(rendererClass);
+ }
+
+ /**
+ * Returns a list of all series assigned to the various renderers within the Plot.
+ * The returned List will contain no duplicates.
+ * @return
+ */
+ public Set<SeriesType> getSeriesSet() {
+ Set<SeriesType> seriesSet = new LinkedHashSet<SeriesType>();
+ for (SeriesRenderer renderer : getRendererList()) {
+ List<SeriesType> seriesList = getSeriesListForRenderer(renderer.getClass());
+ if (seriesList != null) {
+ for (SeriesType series : seriesList) {
+ seriesSet.add(series);
+ }
+ }
+ }
+ return seriesSet;
+ }
+
+ public List<SeriesType> getSeriesListForRenderer(Class rendererClass) {
+ SeriesAndFormatterList<SeriesType,FormatterType> lst = seriesRegistry.get(rendererClass);
+ if(lst == null) {
+ return null;
+ } else {
+ return lst.getSeriesList();
+ }
+ }
+
+ public RendererType getRenderer(Class rendererClass) {
+ for(RendererType renderer : renderers) {
+ if(renderer.getClass() == rendererClass) {
+ return renderer;
+ }
+ }
+ return null;
+ }
+
+ public List<RendererType> getRendererList() {
+ return renderers;
+ }
+
+ public void setMarkupEnabled(boolean enabled) {
+ this.layoutManager.setMarkupEnabled(enabled);
+ }
+
+ /**
+ * Causes the plot to be redrawn.
+ * @since 0.5.1
+ */
+ public void redraw() {
+
+ if (renderMode == RenderMode.USE_BACKGROUND_THREAD) {
+
+ // only enter synchronized block if the call is expected to block OR
+ // if the render thread is idle, so we know that we won't have to wait to
+ // obtain a lock.
+ if (isIdle) {
+ synchronized (renderSynch) {
+ renderSynch.notify();
+ }
+ }
+ } else if(renderMode == RenderMode.USE_MAIN_THREAD) {
+
+ // are we on the UI thread?
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ invalidate();
+ } else {
+ postInvalidate();
+ }
+ } else {
+ throw new IllegalArgumentException("Unsupported Render Mode: " + renderMode);
+ }
+ }
+
+ @Override
+ public synchronized void layout(final DisplayDimensions dims) {
+ displayDims = dims;
+ layoutManager.layout(displayDims);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ synchronized(renderSynch) {
+ keepRunning = false;
+ renderSynch.notify();
+ }
+ }
+
+
+ @Override
+ protected synchronized void onSizeChanged (int w, int h, int oldw, int oldh) {
+
+ // update pixel conversion values
+ PixelUtils.init(getContext());
+
+ // disable hardware acceleration if it's not explicitly supported
+ // by the current Plot implementation. this check only applies to
+ // honeycomb and later environments.
+ if (Build.VERSION.SDK_INT >= 11) {
+ if (!isHwAccelerationSupported() && isHardwareAccelerated()) {
+ setLayerType(View.LAYER_TYPE_SOFTWARE, null);
+ }
+ }
+
+ // pingPong is only used in background rendering mode.
+ if(renderMode == RenderMode.USE_BACKGROUND_THREAD) {
+ pingPong.resize(h, w);
+ }
+
+ RectF cRect = new RectF(0, 0, w, h);
+ RectF mRect = boxModel.getMarginatedRect(cRect);
+ RectF pRect = boxModel.getPaddedRect(mRect);
+
+ layout(new DisplayDimensions(cRect, mRect, pRect));
+ super.onSizeChanged(w, h, oldw, oldh);
+ if(renderThread != null && !renderThread.isAlive()) {
+ renderThread.start();
+ }
+ }
+
+ /**
+ * Called whenever the plot needs to be drawn via the Handler, which invokes invalidate().
+ * Should never be called directly; use {@link #redraw()} instead.
+ * @param canvas
+ */
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (renderMode == RenderMode.USE_BACKGROUND_THREAD) {
+ synchronized(pingPong) {
+ Bitmap bmp = pingPong.getBitmap();
+ if(bmp != null) {
+ canvas.drawBitmap(bmp, 0, 0, null);
+ }
+ }
+ } else if (renderMode == RenderMode.USE_MAIN_THREAD) {
+ renderOnCanvas(canvas);
+ } else {
+ throw new IllegalArgumentException("Unsupported Render Mode: " + renderMode);
+ }
+ }
+
+ /**
+ * Renders the plot onto a canvas. Used by both main thread to draw directly
+ * onto the View's canvas as well as by background draw to render onto a
+ * Bitmap buffer. At the end of the day this is the main entry for a plot's
+ * "heavy lifting".
+ * @param canvas
+ */
+ protected synchronized void renderOnCanvas(Canvas canvas) {
+ try {
+ // any series interested in synchronizing with plot should
+ // implement PlotListener.onBeforeDraw(...) and do a read lock from within its
+ // invocation. This is the entry point into that call:
+ notifyListenersBeforeDraw(canvas);
+ try {
+ // need to completely erase what was on the canvas before redrawing, otherwise
+ // some odd aliasing artifacts begin to build up around the edges of aa'd entities
+ // over time.
+ canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
+ if (backgroundPaint != null) {
+ drawBackground(canvas, displayDims.marginatedRect);
+ }
+
+ layoutManager.draw(canvas);
+
+ if (getBorderPaint() != null) {
+ drawBorder(canvas, displayDims.marginatedRect);
+ }
+ } catch (PlotRenderException e) {
+ Log.e(TAG, "Exception while rendering Plot.", e);
+ e.printStackTrace();
+ } catch (Exception e) {
+ Log.e(TAG, "Exception while rendering Plot.", e);
+ }
+ } finally {
+ isIdle = true;
+ // any series interested in synchronizing with plot should
+ // implement PlotListener.onAfterDraw(...) and do a read unlock from within that
+ // invocation. This is the entry point for that invocation.
+ notifyListenersAfterDraw(canvas);
+ }
+ }
+
+
+ /**
+ * Sets the visual style of the plot's border.
+ * @param style
+ * @param radiusX Sets the X radius for BorderStyle.ROUNDED. Use null for all other styles.
+ * @param radiusY Sets the Y radius for BorderStyle.ROUNDED. Use null for all other styles.
+ */
+ public void setBorderStyle(BorderStyle style, Float radiusX, Float radiusY) {
+ if (style == Plot.BorderStyle.ROUNDED) {
+ if (radiusX == null || radiusY == null){
+ throw new IllegalArgumentException("radiusX and radiusY cannot be null when using BorderStyle.ROUNDED");
+ }
+ this.borderRadiusX = radiusX;
+ this.borderRadiusY = radiusY;
+ }
+ this.borderStyle = style;
+ }
+
+ /**
+ * Draws the plot's outer border.
+ * @param canvas
+ * @throws PlotRenderException
+ */
+ protected void drawBorder(Canvas canvas, RectF dims) {
+ switch (borderStyle) {
+ case ROUNDED:
+ canvas.drawRoundRect(dims, borderRadiusX, borderRadiusY, borderPaint);
+ break;
+ case SQUARE:
+ canvas.drawRect(dims, borderPaint);
+ break;
+ default:
+ }
+ }
+
+ protected void drawBackground(Canvas canvas, RectF dims) {
+ switch (borderStyle) {
+ case ROUNDED:
+ canvas.drawRoundRect(dims, borderRadiusX, borderRadiusY, backgroundPaint);
+ break;
+ case SQUARE:
+ canvas.drawRect(dims, backgroundPaint);
+ break;
+ default:
+ }
+ }
+
+ /**
+ *
+ * @return The displayed title of this Plot.
+ */
+ public String getTitle() {
+ return getTitleWidget().getText();
+ }
+
+ /**
+ *
+ * @param title The title to display on this Plot.
+ */
+ public void setTitle(String title) {
+ titleWidget.setText(title);
+ }
+
+ public LayoutManager getLayoutManager() {
+ return layoutManager;
+ }
+
+ public void setLayoutManager(LayoutManager layoutManager) {
+ this.layoutManager = layoutManager;
+ }
+
+ public TextLabelWidget getTitleWidget() {
+ return titleWidget;
+ }
+
+ public void setTitleWidget(TextLabelWidget titleWidget) {
+ this.titleWidget = titleWidget;
+ }
+
+ public Paint getBackgroundPaint() {
+ return backgroundPaint;
+ }
+
+ public void setBackgroundPaint(Paint backgroundPaint) {
+ this.backgroundPaint = backgroundPaint;
+ }
+
+ /**
+ * Convenience method - wraps the individual setMarginXXX methods into a single method.
+ * @param left
+ * @param top
+ * @param right
+ * @param bottom
+ */
+ public void setPlotMargins(float left, float top, float right, float bottom) {
+ setPlotMarginLeft(left);
+ setPlotMarginTop(top);
+ setPlotMarginRight(right);
+ setPlotMarginBottom(bottom);
+ }
+
+ /**
+ * Convenience method - wraps the individual setPaddingXXX methods into a single method.
+ * @param left
+ * @param top
+ * @param right
+ * @param bottom
+ */
+ public void setPlotPadding(float left, float top, float right, float bottom) {
+ setPlotPaddingLeft(left);
+ setPlotPaddingTop(top);
+ setPlotPaddingRight(right);
+ setPlotPaddingBottom(bottom);
+ }
+
+ public float getPlotMarginTop() {
+ return boxModel.getMarginTop();
+ }
+
+ public void setPlotMarginTop(float plotMarginTop) {
+ boxModel.setMarginTop(plotMarginTop);
+ }
+
+ public float getPlotMarginBottom() {
+ return boxModel.getMarginBottom();
+ }
+
+ public void setPlotMarginBottom(float plotMarginBottom) {
+ boxModel.setMarginBottom(plotMarginBottom);
+ }
+
+ public float getPlotMarginLeft() {
+ return boxModel.getMarginLeft();
+ }
+
+ public void setPlotMarginLeft(float plotMarginLeft) {
+ boxModel.setMarginLeft(plotMarginLeft);
+ }
+
+ public float getPlotMarginRight() {
+ return boxModel.getMarginRight();
+ }
+
+ public void setPlotMarginRight(float plotMarginRight) {
+ boxModel.setMarginRight(plotMarginRight);
+ }
+
+ public float getPlotPaddingTop() {
+ return boxModel.getPaddingTop();
+ }
+
+ public void setPlotPaddingTop(float plotPaddingTop) {
+ boxModel.setPaddingTop(plotPaddingTop);
+ }
+
+ public float getPlotPaddingBottom() {
+ return boxModel.getPaddingBottom();
+ }
+
+ public void setPlotPaddingBottom(float plotPaddingBottom) {
+ boxModel.setPaddingBottom(plotPaddingBottom);
+ }
+
+ public float getPlotPaddingLeft() {
+ return boxModel.getPaddingLeft();
+ }
+
+ public void setPlotPaddingLeft(float plotPaddingLeft) {
+ boxModel.setPaddingLeft(plotPaddingLeft);
+ }
+
+ public float getPlotPaddingRight() {
+ return boxModel.getPaddingRight();
+ }
+
+ public void setPlotPaddingRight(float plotPaddingRight) {
+ boxModel.setPaddingRight(plotPaddingRight);
+ }
+
+ public Paint getBorderPaint() {
+ return borderPaint;
+ }
+
+ /**
+ * Set's the paint used to draw the border. Note that this method
+ * copies borderPaint and set's the copy's Paint.Style attribute to
+ * Paint.Style.STROKE.
+ * @param borderPaint
+ */
+ public void setBorderPaint(Paint borderPaint) {
+ if(borderPaint == null) {
+ this.borderPaint = null;
+ } else {
+ this.borderPaint = new Paint(borderPaint);
+ this.borderPaint.setStyle(Paint.Style.STROKE);
+ }
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/PlotListener.java b/AndroidPlot-Core/src/main/java/com/androidplot/PlotListener.java
new file mode 100644
index 0000000..e622215
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/PlotListener.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot;
+
+import android.graphics.Canvas;
+
+/**
+ * Defines methods used for monitoring events generated by a Plot.
+ */
+public interface PlotListener {
+
+ /**
+ * Fired immediately before the Plot "source" is drawn onto canvas.
+ * Commonly used by implementing Series instances to activate a read
+ * lock on it's self in preparation for the Plot's imminent reading
+ * of that series.
+ * @param source
+ * @param canvas
+ */
+ public void onBeforeDraw(Plot source, Canvas canvas);
+
+ /**
+ * Fired immediately after the Plot "source" is drawn onto canvas.
+ * Just as onBeforeDraw(...) is commonly used by Series implementations
+ * to activate a read lock, this method is commonly used to release that
+ * same lock.
+ * @param source
+ * @param canvas
+ */
+ public void onAfterDraw(Plot source, Canvas canvas);
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/Series.java b/AndroidPlot-Core/src/main/java/com/androidplot/Series.java
new file mode 100644
index 0000000..7fc90f5
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/Series.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot;
+
+/**
+ * Base interface for all Series implementations
+ */
+public interface Series<T> {
+
+ /**
+ *
+ * @return The title of this Series.
+ */
+ public String getTitle();
+
+
+
+ // used primarily for synchronization. can also be used to hang a condition on updates.
+
+ /**
+ * Called whenever the plot initiates a read of a Series. In most cases this means that
+ * a complete read of the Series contents will proceed.
+ *//*
+ public void onReadBegin();
+
+ *//**
+ * Called when a Plot concludes reading of a Series.
+ *//*
+ public void onReadEnd();*/
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/exception/PlotRenderException.java b/AndroidPlot-Core/src/main/java/com/androidplot/exception/PlotRenderException.java
new file mode 100644
index 0000000..0515e2a
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/exception/PlotRenderException.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.exception;
+
+public class PlotRenderException extends Exception {
+ public PlotRenderException(String message) {
+ super(message);
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/pie/PieChart.java b/AndroidPlot-Core/src/main/java/com/androidplot/pie/PieChart.java
new file mode 100644
index 0000000..56f609d
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/pie/PieChart.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2013 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.pie;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import com.androidplot.Plot;
+import com.androidplot.ui.AnchorPosition;
+import com.androidplot.ui.SizeLayoutType;
+import com.androidplot.ui.SizeMetrics;
+import com.androidplot.util.PixelUtils;
+import com.androidplot.ui.XLayoutStyle;
+import com.androidplot.ui.YLayoutStyle;
+
+public class PieChart extends Plot<Segment, SegmentFormatter, PieRenderer> {
+
+ private static final int DEFAULT_PIE_WIDGET_H_DP = 18;
+ private static final int DEFAULT_PIE_WIDGET_W_DP = 10;
+
+ private static final int DEFAULT_PIE_WIDGET_Y_OFFSET_DP = 0;
+ private static final int DEFAULT_PIE_WIDGET_X_OFFSET_DP = 0;
+
+ public void setPieWidget(PieWidget pieWidget) {
+ this.pieWidget = pieWidget;
+ }
+
+ @SuppressWarnings("FieldCanBeLocal")
+ private PieWidget pieWidget;
+
+ public PieChart(Context context, String title) {
+ super(context, title);
+ }
+
+ public PieChart(Context context, String title, RenderMode mode) {
+ super(context, title, mode);
+ }
+
+ public PieChart(Context context, AttributeSet attributes) {
+ super(context, attributes);
+ }
+
+ @Override
+ protected void onPreInit() {
+ pieWidget = new PieWidget(
+ getLayoutManager(),
+ this,
+ new SizeMetrics(
+ PixelUtils.dpToPix(DEFAULT_PIE_WIDGET_H_DP),
+ SizeLayoutType.FILL,
+ PixelUtils.dpToPix(DEFAULT_PIE_WIDGET_W_DP),
+ SizeLayoutType.FILL));
+
+ pieWidget.position(
+ PixelUtils.dpToPix(DEFAULT_PIE_WIDGET_X_OFFSET_DP),
+ XLayoutStyle.ABSOLUTE_FROM_CENTER,
+ PixelUtils.dpToPix(DEFAULT_PIE_WIDGET_Y_OFFSET_DP),
+ YLayoutStyle.ABSOLUTE_FROM_CENTER,
+ AnchorPosition.CENTER);
+
+ pieWidget.setPadding(10, 10, 10, 10);
+
+ // TODO: can't remember why this getClass() check is neccessary. test if it actually is...
+ /*if (getClass().equals(PieChart.class) && attrs != null) {
+ loadAttrs(context, attrs);
+ }*/
+ }
+
+ public PieWidget getPieWidget() {
+ return pieWidget;
+ }
+
+ public void addSegment(Segment segment, SegmentFormatter formatter) {
+ addSeries(segment, formatter);
+ }
+
+ public void removeSegment(Segment segment) {
+ removeSeries(segment);
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/pie/PieRenderer.java b/AndroidPlot-Core/src/main/java/com/androidplot/pie/PieRenderer.java
new file mode 100644
index 0000000..86364aa
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/pie/PieRenderer.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright 2013 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.pie;
+
+import android.graphics.*;
+
+import com.androidplot.exception.PlotRenderException;
+import com.androidplot.ui.SeriesRenderer;
+
+import java.util.Set;
+
+public class PieRenderer extends SeriesRenderer<PieChart, Segment, SegmentFormatter> {
+
+ // starting angle to use when drawing the first radial line of the first segment.
+ @SuppressWarnings("FieldCanBeLocal")
+ private float startDeg = 0;
+ private float endDeg = 360;
+
+ // TODO: express donut in units other than px.
+ private float donutSize = 0.5f;
+ private DonutMode donutMode = DonutMode.PERCENT;
+
+ public enum DonutMode {
+ PERCENT,
+ DP,
+ PIXELS
+ }
+
+ public PieRenderer(PieChart plot) {
+ super(plot);
+ }
+
+ public float getRadius(RectF rect) {
+ return rect.width() < rect.height() ? rect.width() / 2 : rect.height() / 2;
+ }
+
+ @Override
+ public void onRender(Canvas canvas, RectF plotArea) throws PlotRenderException {
+
+ float radius = getRadius(plotArea);
+ PointF origin = new PointF(plotArea.centerX(), plotArea.centerY());
+
+ double[] values = getValues();
+ double scale = calculateScale(values);
+ float offset = startDeg;
+ Set<Segment> segments = getPlot().getSeriesSet();
+
+ //PointF lastRadial = calculateLineEnd(origin, radius, offset);
+
+ RectF rec = new RectF(origin.x - radius, origin.y - radius, origin.x + radius, origin.y + radius);
+
+ int i = 0;
+ for (Segment segment : segments) {
+ float lastOffset = offset;
+ float sweep = (float) (scale * (values[i]) * 360);
+ offset += sweep;
+ //PointF radial = calculateLineEnd(origin, radius, offset);
+ drawSegment(canvas, rec, segment, getPlot().getFormatter(segment, PieRenderer.class),
+ radius, lastOffset, sweep);
+ //lastRadial = radial;
+ i++;
+ }
+ }
+
+ protected void drawSegment(Canvas canvas, RectF bounds, Segment seg, SegmentFormatter f,
+ float rad, float startAngle, float sweep) {
+ canvas.save();
+
+ float cx = bounds.centerX();
+ float cy = bounds.centerY();
+
+ float donutSizePx;
+ switch(donutMode) {
+ case PERCENT:
+ donutSizePx = donutSize * rad;
+ break;
+ case PIXELS:
+ donutSizePx = (donutSize > 0)?donutSize:(rad + donutSize);
+ break;
+ default:
+ throw new UnsupportedOperationException("Not yet implemented.");
+ }
+
+ // vertices of the first radial:
+ PointF r1Outer = calculateLineEnd(cx, cy, rad, startAngle);
+ PointF r1Inner = calculateLineEnd(cx, cy, donutSizePx, startAngle);
+
+ // vertices of the second radial:
+ PointF r2Outer = calculateLineEnd(cx, cy, rad, startAngle + sweep);
+ PointF r2Inner = calculateLineEnd(cx, cy, donutSizePx, startAngle + sweep);
+
+ Path clip = new Path();
+
+ //float outerStroke = f.getOuterEdgePaint().getStrokeWidth();
+ //float halfOuterStroke = outerStroke / 2;
+
+ // leave plenty of room on the outside for stroked borders;
+ // necessary because the clipping border is ugly
+ // and cannot be easily anti aliased. Really we only care about masking off the
+ // radial edges.
+ clip.arcTo(new RectF(bounds.left - rad,
+ bounds.top - rad,
+ bounds.right + rad,
+ bounds.bottom + rad),
+ startAngle, sweep);
+ clip.lineTo(cx, cy);
+ clip.close();
+ canvas.clipPath(clip);
+
+ Path p = new Path();
+ p.arcTo(bounds, startAngle, sweep);
+ p.lineTo(r2Inner.x, r2Inner.y);
+
+ // sweep back to original angle:
+ p.arcTo(new RectF(
+ cx - donutSizePx,
+ cy - donutSizePx,
+ cx + donutSizePx,
+ cy + donutSizePx),
+ startAngle + sweep, -sweep);
+
+ p.close();
+
+ // fill segment:
+ canvas.drawPath(p, f.getFillPaint());
+
+ // draw radial lines
+ canvas.drawLine(r1Inner.x, r1Inner.y, r1Outer.x, r1Outer.y, f.getRadialEdgePaint());
+ canvas.drawLine(r2Inner.x, r2Inner.y, r2Outer.x, r2Outer.y, f.getRadialEdgePaint());
+
+ // draw inner line:
+ canvas.drawCircle(cx, cy, donutSizePx, f.getInnerEdgePaint());
+
+ // draw outer line:
+ canvas.drawCircle(cx, cy, rad, f.getOuterEdgePaint());
+ canvas.restore();
+
+ PointF labelOrigin = calculateLineEnd(cx, cy,
+ (rad-((rad- donutSizePx)/2)), startAngle + (sweep/2));
+
+ // TODO: move segment labelling outside the segment drawing loop
+ // TODO: so that the labels will not be clipped by the edge of the next
+ // TODO: segment being drawn.
+ drawSegmentLabel(canvas, labelOrigin, seg, f);
+ }
+
+ protected void drawSegmentLabel(Canvas canvas, PointF origin,
+ Segment seg, SegmentFormatter f) {
+ canvas.drawText(seg.getTitle(), origin.x, origin.y, f.getLabelPaint());
+
+ }
+
+ @Override
+ protected void doDrawLegendIcon(Canvas canvas, RectF rect, SegmentFormatter formatter) {
+ throw new UnsupportedOperationException("Not yet implemented.");
+ }
+
+ /**
+ * Determines how many counts there are per cent of whatever the
+ * pie chart is displaying as a fraction, 1 being 100%.
+ */
+ private double calculateScale(double[] values) {
+ double total = 0;
+ for (int i = 0; i < values.length; i++) {
+ total += values[i];
+ }
+
+ return (1d / total);
+ }
+
+ private double[] getValues() {
+ Set<Segment> segments = getPlot().getSeriesSet();
+ double[] result = new double[segments.size()];
+ int i = 0;
+ for (Segment seg : getPlot().getSeriesSet()) {
+ result[i] = seg.getValue().doubleValue();
+ i++;
+ }
+ return result;
+ }
+
+ private PointF calculateLineEnd(float x, float y, float rad, float deg) {
+ return calculateLineEnd(new PointF(x, y), rad, deg);
+ }
+
+ private PointF calculateLineEnd(PointF origin, float rad, float deg) {
+
+ double radians = deg * Math.PI / 180F;
+ double x = rad * Math.cos(radians);
+ double y = rad * Math.sin(radians);
+
+ // convert to screen space:
+ return new PointF(origin.x + (float) x, origin.y + (float) y);
+ }
+
+ public void setDonutSize(float size, DonutMode mode) {
+ switch(mode) {
+ case PERCENT:
+ if(size < 0 || size > 1) {
+ throw new IllegalArgumentException(
+ "Size parameter must be between 0 and 1 when operating in PERCENT mode.");
+ }
+ break;
+ case PIXELS:
+ break;
+ default:
+ throw new UnsupportedOperationException("Not yet implemented.");
+ }
+ donutMode = mode;
+ donutSize = size;
+ }
+
+ public void setStartDeg(float deg) {
+ startDeg = deg;
+ }
+
+ public void setEndDeg(float deg) {
+ endDeg = deg;
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/pie/PieWidget.java b/AndroidPlot-Core/src/main/java/com/androidplot/pie/PieWidget.java
new file mode 100644
index 0000000..ebfa8f8
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/pie/PieWidget.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2013 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.pie;
+
+import android.graphics.*;
+import com.androidplot.exception.PlotRenderException;
+import com.androidplot.ui.LayoutManager;
+import com.androidplot.ui.SizeMetrics;
+import com.androidplot.ui.widget.Widget;
+
+import java.util.ArrayList;
+import java.util.Set;
+
+/**
+ * Visualizes data as a pie chart.
+ */
+public class PieWidget extends Widget {
+
+ private PieChart pieChart;
+
+ public PieWidget(LayoutManager layoutManager, PieChart pieChart, SizeMetrics metrics) {
+ super(layoutManager, metrics);
+ this.pieChart = pieChart;
+ }
+
+ @Override
+ protected void doOnDraw(Canvas canvas, RectF widgetRect) throws PlotRenderException {
+
+ for(PieRenderer renderer : pieChart.getRendererList()) {
+ renderer.render(canvas, widgetRect);
+ }
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/pie/Segment.java b/AndroidPlot-Core/src/main/java/com/androidplot/pie/Segment.java
new file mode 100644
index 0000000..1b18f15
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/pie/Segment.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2013 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.pie;
+
+import com.androidplot.Series;
+
+public class Segment implements Series<Number> {
+
+ private String title;
+
+ private Number value;
+
+ public Segment(String title, Number value) {
+ this.title = title;
+ this.setValue(value);
+ }
+
+ @Override
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public Number getValue() {
+ return value;
+ }
+
+ public void setValue(Number value) {
+ this.value = value;
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/pie/SegmentFormatter.java b/AndroidPlot-Core/src/main/java/com/androidplot/pie/SegmentFormatter.java
new file mode 100644
index 0000000..5c75950
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/pie/SegmentFormatter.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2013 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.pie;
+
+import android.graphics.Color;
+import android.graphics.Paint;
+import com.androidplot.ui.SeriesRenderer;
+import com.androidplot.ui.Formatter;
+
+public class SegmentFormatter extends Formatter<PieChart> {
+
+ private static final int DEFAULT_FILL_COLOR = Color.TRANSPARENT;
+ private static final int DEFAULT_EDGE_COLOR = Color.BLACK;
+ private static final int DEFAULT_LABEL_COLOR = Color.WHITE;
+ private static final float DEFAULT_EDGE_THICKNESS = 3;
+ private static final float DEFAULT_LABEL_MARKER_THICKNESS = 3;
+ private static final float DEFAULT_LABEL_FONT_SIZE = 18;
+
+ private Paint innerEdgePaint;
+ private Paint outerEdgePaint;
+ private Paint radialEdgePaint;
+ private Paint fillPaint;
+
+ private Paint labelPaint;
+ private Paint labelMarkerPaint;
+
+ {
+ setFillPaint(new Paint());
+ // outer edge:
+ setOuterEdgePaint(new Paint());
+ getOuterEdgePaint().setStyle(Paint.Style.STROKE);
+ getOuterEdgePaint().setStrokeWidth(DEFAULT_EDGE_THICKNESS);
+ getOuterEdgePaint().setAntiAlias(true);
+
+ // inner edge:
+ setInnerEdgePaint(new Paint());
+ getInnerEdgePaint().setStyle(Paint.Style.STROKE);
+ getInnerEdgePaint().setStrokeWidth(DEFAULT_EDGE_THICKNESS);
+ getInnerEdgePaint().setAntiAlias(true);
+
+ // radial edge:
+ setRadialEdgePaint(new Paint());
+ getRadialEdgePaint().setStyle(Paint.Style.STROKE);
+ getRadialEdgePaint().setStrokeWidth(DEFAULT_EDGE_THICKNESS);
+ getRadialEdgePaint().setAntiAlias(true);
+
+ // label paint:
+ setLabelPaint(new Paint());
+ getLabelPaint().setColor(DEFAULT_LABEL_COLOR);
+ getLabelPaint().setTextSize(DEFAULT_LABEL_FONT_SIZE);
+ getLabelPaint().setAntiAlias(true);
+ getLabelPaint().setTextAlign(Paint.Align.CENTER);
+ //getLabelPaint().setShadowLayer(5, 4, 4, Color.BLACK);
+
+ // label marker paint:
+ setLabelMarkerPaint(new Paint());
+ getLabelMarkerPaint().setColor(DEFAULT_LABEL_COLOR);
+ getLabelMarkerPaint().setStrokeWidth(DEFAULT_LABEL_MARKER_THICKNESS);
+ }
+
+ /**
+ * Should only be used in conjunction with calls to configure()...
+ */
+ public SegmentFormatter() {}
+
+ public SegmentFormatter(Integer fillColor) {
+ if(fillColor != null) {
+ getFillPaint().setColor(fillColor);
+ } else {
+ getFillPaint().setColor(DEFAULT_FILL_COLOR);
+ }
+ }
+
+ public SegmentFormatter(Integer fillColor, Integer borderColor) {
+ this(fillColor);
+ getInnerEdgePaint().setColor(borderColor);
+ getOuterEdgePaint().setColor(borderColor);
+ getRadialEdgePaint().setColor(borderColor);
+ }
+
+ public SegmentFormatter(Integer fillColor, Integer outerEdgeColor,
+ Integer innerEdgeColor, Integer radialEdgeColor) {
+ this(fillColor);
+
+
+ if(getOuterEdgePaint() != null) {
+ getOuterEdgePaint().setColor(outerEdgeColor);
+ } else {
+ outerEdgePaint = new Paint();
+ getOuterEdgePaint().setColor(DEFAULT_EDGE_COLOR);
+ }
+
+ if (getInnerEdgePaint() != null) {
+ getInnerEdgePaint().setColor(innerEdgeColor);
+ } else {
+ outerEdgePaint = new Paint();
+ getInnerEdgePaint().setColor(DEFAULT_EDGE_COLOR);
+ }
+
+ if (getRadialEdgePaint() != null) {
+ getRadialEdgePaint().setColor(radialEdgeColor);
+ } else {
+ radialEdgePaint = new Paint();
+ getRadialEdgePaint().setColor(DEFAULT_EDGE_COLOR);
+ }
+ }
+
+ @Override
+ public Class<? extends SeriesRenderer> getRendererClass() {
+ return PieRenderer.class;
+ }
+
+ @Override
+ public SeriesRenderer getRendererInstance(PieChart plot) {
+ return new PieRenderer(plot);
+ }
+
+ public Paint getInnerEdgePaint() {
+ return innerEdgePaint;
+ }
+
+ public void setInnerEdgePaint(Paint innerEdgePaint) {
+ this.innerEdgePaint = innerEdgePaint;
+ }
+
+ public Paint getOuterEdgePaint() {
+ return outerEdgePaint;
+ }
+
+ public void setOuterEdgePaint(Paint outerEdgePaint) {
+ this.outerEdgePaint = outerEdgePaint;
+ }
+
+ public Paint getRadialEdgePaint() {
+ return radialEdgePaint;
+ }
+
+ public void setRadialEdgePaint(Paint radialEdgePaint) {
+ this.radialEdgePaint = radialEdgePaint;
+ }
+
+ public Paint getFillPaint() {
+ return fillPaint;
+ }
+
+ public void setFillPaint(Paint fillPaint) {
+ this.fillPaint = fillPaint;
+ }
+
+ public Paint getLabelPaint() {
+ return labelPaint;
+ }
+
+ public void setLabelPaint(Paint labelPaint) {
+ this.labelPaint = labelPaint;
+ }
+
+ public Paint getLabelMarkerPaint() {
+ return labelMarkerPaint;
+ }
+
+ public void setLabelMarkerPaint(Paint labelMarkerPaint) {
+ this.labelMarkerPaint = labelMarkerPaint;
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/AnchorPosition.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/AnchorPosition.java
new file mode 100644
index 0000000..e1f0b2e
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/AnchorPosition.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.ui;
+
+/**
+ * Enumeration of possible anchor positions that a {@link com.androidplot.ui.widget.Widget} can use. There are a total
+ * 8 possible anchor positions representing each corner of the Widget and the point exactly between each corner.
+ */
+public enum AnchorPosition {
+ TOP_MIDDLE,
+ LEFT_TOP, // default
+ LEFT_MIDDLE,
+ LEFT_BOTTOM,
+ RIGHT_TOP,
+ RIGHT_MIDDLE,
+ RIGHT_BOTTOM,
+ BOTTOM_MIDDLE,
+ CENTER
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/BoxModel.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/BoxModel.java
new file mode 100644
index 0000000..e80728c
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/BoxModel.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.ui;
+
+import android.graphics.RectF;
+
+/**
+ * Convenience implementation of {@link BoxModelable}.
+ */
+public class BoxModel implements BoxModelable{
+
+ private float marginLeft;
+ private float marginTop;
+ private float marginRight;
+ private float marginBottom;
+
+
+ private float paddingLeft;
+ private float paddingTop;
+ private float paddingRight;
+ private float paddingBottom;
+ //private RectF marginRect;
+ //private RectF paddingRect;
+
+ public BoxModel() {
+
+ }
+
+ @SuppressWarnings("SameParameterValue")
+ public BoxModel(float marginLeft, float marginTop, float marginRight, float marginBottom,
+ float paddingLeft, float paddingTop, float paddingRight, float paddingBottom) {
+ this.marginLeft = marginLeft;
+ this.marginTop = marginTop;
+ this.marginRight = marginRight;
+ this.marginBottom = marginBottom;
+
+ this.paddingLeft = paddingLeft;
+ this.paddingTop = paddingTop;
+ this.paddingRight = paddingRight;
+ this.paddingBottom = paddingBottom;
+ }
+
+ /**
+ * Returns a RectF instance describing the inner edge of the margin layer.
+ * @param boundsRect
+ * @return
+ */
+ public RectF getMarginatedRect(RectF boundsRect) {
+ return new RectF( boundsRect.left + getMarginLeft(),
+ boundsRect.top + getMarginTop(),
+ boundsRect.right - getMarginRight(),
+ boundsRect.bottom - getMarginBottom());
+ }
+
+ /**
+ * Returns a RectF instance describing the inner edge of the padding layer.
+ * @param marginRect
+ * @return
+ */
+ public RectF getPaddedRect(RectF marginRect) {
+ return new RectF(marginRect.left + getPaddingLeft(),
+ marginRect.top+getPaddingTop(),
+ marginRect.right - getPaddingRight(),
+ marginRect.bottom - getPaddingBottom());
+ }
+
+ @Override
+ public void setMargins(float left, float top, float right, float bottom) {
+ setMarginLeft(left);
+ setMarginTop(top);
+ setMarginRight(right);
+ setMarginBottom(bottom);
+ }
+
+ @Override
+ public void setPadding(float left, float top, float right, float bottom) {
+ setPaddingLeft(left);
+ setPaddingTop(top);
+ setPaddingRight(right);
+ setPaddingBottom(bottom);
+ }
+
+
+ public float getMarginLeft() {
+ return marginLeft;
+ }
+
+ public void setMarginLeft(float marginLeft) {
+ this.marginLeft = marginLeft;
+ }
+
+ public float getMarginTop() {
+ return marginTop;
+ }
+
+ public void setMarginTop(float marginTop) {
+ this.marginTop = marginTop;
+ }
+
+ public float getMarginRight() {
+ return marginRight;
+ }
+
+ public void setMarginRight(float marginRight) {
+ this.marginRight = marginRight;
+ }
+
+ public float getMarginBottom() {
+ return marginBottom;
+ }
+
+ public void setMarginBottom(float marginBottom) {
+ this.marginBottom = marginBottom;
+ }
+
+ public float getPaddingLeft() {
+ return paddingLeft;
+ }
+
+ public void setPaddingLeft(float paddingLeft) {
+ this.paddingLeft = paddingLeft;
+ }
+
+ public float getPaddingTop() {
+ return paddingTop;
+ }
+
+ public void setPaddingTop(float paddingTop) {
+ this.paddingTop = paddingTop;
+ }
+
+ public float getPaddingRight() {
+ return paddingRight;
+ }
+
+ public void setPaddingRight(float paddingRight) {
+ this.paddingRight = paddingRight;
+ }
+
+ public float getPaddingBottom() {
+ return paddingBottom;
+ }
+
+ public void setPaddingBottom(float paddingBottom) {
+ this.paddingBottom = paddingBottom;
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/BoxModelable.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/BoxModelable.java
new file mode 100644
index 0000000..7389e85
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/BoxModelable.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.ui;
+
+import android.graphics.RectF;
+
+/**
+ * Encapsulates the functionality of a BoxModel.
+ * See http://www.w3.org/TR/CSS21/box.html for a good explanation of how
+ * the box model works.
+ */
+public interface BoxModelable {
+ /**
+ * Returns a RectF instance describing the inner edge of the margin layer.
+ * @param boundsRect
+ * @return
+ */
+ public RectF getMarginatedRect(RectF boundsRect);
+
+ /**
+ * Returns a RectF instance describing the inner edge of the padding layer.
+ * @param marginRect
+ * @return
+ */
+ public RectF getPaddedRect(RectF marginRect);
+
+
+ public void setMargins(float left, float top, float right, float bottom);
+
+ public void setPadding(float left, float top, float right, float bottom);
+
+ public float getMarginLeft();
+
+ public void setMarginLeft(float marginLeft);
+
+ public float getMarginTop();
+
+ public void setMarginTop(float marginTop);
+
+ public float getMarginRight();
+
+ public void setMarginRight(float marginRight);
+
+ public float getMarginBottom();
+
+ public float getPaddingLeft();
+
+ public void setPaddingLeft(float paddingLeft);
+
+ public float getPaddingTop();
+
+ public void setPaddingTop(float paddingTop);
+
+ public float getPaddingRight();
+
+ public void setPaddingRight(float paddingRight);
+
+ public float getPaddingBottom();
+
+ public void setPaddingBottom(float paddingBottom);
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/DynamicTableModel.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/DynamicTableModel.java
new file mode 100644
index 0000000..1654054
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/DynamicTableModel.java
@@ -0,0 +1,286 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.ui;
+
+import android.graphics.RectF;
+
+import java.util.Iterator;
+
+/**
+ * Encapsulates the visual aspects of a table; number of rows and columns
+ * and the height and width in pixels of each element within the table.
+ * There is no support (yet) for variable size cells within a table; all
+ * cells within a table share the same dimensions.
+ *
+ * The DynamicTableModel provides an Iterator implementation which returns a RectF
+ * of each subsequent cell, based on the order of the plot. Tables with
+ * an order of COLUMN_MAJOR are traversed left to right column by column until
+ * the end of the row is reached, then proceeding to the next row.
+ * Tables with an order of ROW_MAJOR are traversed top to bottom row by row
+ * until the end of the row is reached, then proceeding to the next column.
+ */
+public class DynamicTableModel extends TableModel {
+
+ //private float cellWidth;
+ //private float cellHeight;
+ //private TableSizingMethod rowSizingMethod;
+ //private TableSizingMethod columnSizingMethod;
+
+
+ private int numRows;
+ private int numColumns;
+
+ private Float cellWidth;
+ private Float cellHeight;
+
+ private CellSizingMethod rowSizingMethod;
+ private CellSizingMethod columnSizingMethod;
+
+ /**
+ * Convenience method. Sets order to ROW_MAJOR.
+ * @param numColumns
+ * @param numRows
+ */
+ public DynamicTableModel(int numColumns, int numRows) {
+ this(numColumns, numRows, TableOrder.ROW_MAJOR);
+
+ }
+
+ public DynamicTableModel(int numColumns, int numRows, TableOrder order) {
+ super(order);
+ this.numColumns = numColumns;
+ //this.cellWidth = cellWidth;
+ //this.rowSizingMethod = rowSizingMethod;
+ this.numRows = numRows;
+ //this.cellHeight = cellHeight;
+ //this.columnSizingMethod = columnSizingMethod;
+ //this.order = order;
+ }
+
+ /*public DynamicTableModel(Number colVal, CellSizingMethod colSzMethod, Number rowVal, CellSizingMethod rowSzMethod, TableOrder order) {
+ if(colVal == null || rowVal == null) {
+ throw new NullPointerException();
+ }
+ columnSizingMethod = colSzMethod;
+ switch(columnSizingMethod) {
+ case FILL:
+ numColumns = colVal.intValue();
+ break;
+ case FIXED:
+ cellWidth = colVal.floatValue();
+ break;
+ }
+ rowSzMethod = rowSzMethod;
+ }*/
+
+ @Override
+ public TableModelIterator getIterator(RectF tableRect, int totalElements) {
+ return new TableModelIterator(this, tableRect, totalElements);
+ }
+
+ /**
+ * Calculates the dimensions of a single element of this table with
+ * tableRect representing the overall dimensions of the table.
+ * @param tableRect Dimensions/position of the table
+ * @return a RectF representing the first (top-left) element in
+ * the tableRect passed in.
+ */
+ public RectF getCellRect(RectF tableRect, int numElements) {
+ RectF cellRect = new RectF();
+ cellRect.left = tableRect.left;
+ cellRect.top = tableRect.top;
+ //cellRect.bottom = getElementHeightPix(tableRect);
+ cellRect.bottom = tableRect.top + calculateCellSize(tableRect, TableModel.Axis.ROW, numElements);
+ //cellRect.right = getElementWidthPix(tableRect);
+ cellRect.right = tableRect.left + calculateCellSize(tableRect, TableModel.Axis.COLUMN, numElements);
+ return cellRect;
+ }
+
+ /**
+ * Figure out the size of a single cell across the specified axis.
+ * @param tableRect
+ * @param axis
+ * @param numElementsInTable
+ * @return
+ */
+ private float calculateCellSize(RectF tableRect,
+ Axis axis,
+ int numElementsInTable) {
+ //float elementSizeInPix = 0;
+ int axisElements = 0;
+
+ float axisSizePix = 0;
+ switch (axis) {
+ case ROW:
+ //elementSizeInPix = cellHeight;
+ axisElements = numRows;
+ axisSizePix = tableRect.height();
+ break;
+ case COLUMN:
+ //elementSizeInPix = cellWidth;
+ axisElements = numColumns;
+ axisSizePix = tableRect.width();
+ break;
+ }
+ //if (elementSizeInPix != 0) {
+ // return elementSizeInPix;
+ if(axisElements != 0) {
+ return axisSizePix / axisElements;
+ } else {
+ return axisSizePix / numElementsInTable;
+ }
+ }
+
+
+
+ public int getNumRows() {
+ return numRows;
+ }
+
+ public void setNumRows(int numRows) {
+ this.numRows = numRows;
+ }
+
+ public int getNumColumns() {
+ return numColumns;
+ }
+
+ public void setNumColumns(int numColumns) {
+ this.numColumns = numColumns;
+ }
+
+/* public void setCellWidth(Float cellWidth) {
+ this.cellWidth = cellWidth;
+ }
+
+ public Float getCellWidth() {
+ return cellWidth;
+ }
+
+ public Float getCellHeight() {
+ return cellHeight;
+ }
+
+ public void setCellHeight(Float cellHeight) {
+ this.cellHeight = cellHeight;
+ }*/
+
+ private class TableModelIterator implements Iterator<RectF> {
+ private boolean isOk = true;
+ int lastColumn = 0; // most recent column iterated
+ int lastRow = 0; // most recent row iterated
+ int lastElement = 0; // last element index iterated
+ private DynamicTableModel dynamicTableModel;
+ private RectF tableRect;
+ private RectF lastElementRect;
+ private int totalElements;
+ private TableOrder order;
+
+ private int calculatedNumElements;
+ private int calculatedRows; // number of rows to be iterated
+ private int calculatedColumns; // number of columns to be iterated
+
+ public TableModelIterator(DynamicTableModel dynamicTableModel, RectF tableRect, int totalElements) {
+ this.dynamicTableModel = dynamicTableModel;
+ this.tableRect = tableRect;
+ this.totalElements = totalElements;
+ order = dynamicTableModel.getOrder();
+
+ // unlimited columns:
+ if(dynamicTableModel.getNumColumns() == 0 && dynamicTableModel.getNumRows() >= 1) {
+ calculatedRows = dynamicTableModel.getNumRows();
+
+ // round up:
+ calculatedColumns = new Float((totalElements / (float) calculatedRows) + 0.5).intValue();
+ } else if(dynamicTableModel.getNumRows() == 0 && dynamicTableModel.getNumColumns() >= 1) {
+ //order = TableOrder.ROW_MAJOR;
+ calculatedColumns = dynamicTableModel.getNumColumns();
+ calculatedRows = new Float((totalElements / (float) calculatedColumns) + 0.5).intValue();
+ // unlimited rows and columns (impossible) so default a single row with n columns:
+ }else if(dynamicTableModel.getNumColumns() == 0 && dynamicTableModel.getNumRows() == 0) {
+ calculatedRows = 1;
+ calculatedColumns = totalElements;
+ } else {
+ //order = dynamicTableModel.getOrder();
+ calculatedRows = dynamicTableModel.getNumRows();
+ calculatedColumns = dynamicTableModel.getNumColumns();
+ }
+ calculatedNumElements = calculatedRows * calculatedColumns;
+ lastElementRect = dynamicTableModel.getCellRect(tableRect, totalElements);
+ }
+
+ @Override
+ public boolean hasNext() {
+ return isOk && lastElement < calculatedNumElements;
+ }
+
+ @Override
+ public RectF next() {
+ if(!hasNext()) {
+ isOk = false;
+ throw new IndexOutOfBoundsException();
+ }
+
+ if (lastElement == 0) {
+ lastElement++;
+ return lastElementRect;
+ }
+
+ RectF nextElementRect = new RectF(lastElementRect);
+
+ switch (order) {
+ case ROW_MAJOR:
+ if (dynamicTableModel.getNumColumns() > 0 && lastColumn >= (dynamicTableModel.getNumColumns() - 1)) {
+ // move to the begining of the next row down:// move to the begining of the next row down:
+ nextElementRect.offsetTo(tableRect.left, lastElementRect.bottom);
+ lastColumn = 0;
+ lastRow++;
+ } else {
+ // move to the next column over:
+ nextElementRect.offsetTo(lastElementRect.right, lastElementRect.top);
+ lastColumn++;
+ }
+ break;
+ case COLUMN_MAJOR:
+ if (dynamicTableModel.getNumRows() > 0 && lastRow >= (dynamicTableModel.getNumRows() - 1)) {
+ // move to the top of the next column over:
+ nextElementRect.offsetTo(lastElementRect.right, tableRect.top);
+ lastRow = 0;
+ lastColumn++;
+ } else {
+ // move to the next row down:
+ nextElementRect.offsetTo(lastElementRect.left, lastElementRect.bottom);
+ lastRow++;
+ }
+ break;
+ // unknown/unsupported enum val:
+ default:
+ isOk = false;
+ throw new IllegalArgumentException();
+ }
+ lastElement++;
+ lastElementRect = nextElementRect;
+ return nextElementRect;
+
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/FixedTableModel.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/FixedTableModel.java
new file mode 100644
index 0000000..4c7c265
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/FixedTableModel.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.ui;
+
+import android.graphics.RectF;
+
+import java.util.Iterator;
+
+public class FixedTableModel extends TableModel {
+ private float cellWidth;
+ private float cellHeight;
+ protected FixedTableModel(float cellWidth, float cellHeight, TableOrder order) {
+ super(order);
+ setCellWidth(cellWidth);
+ setCellHeight(cellHeight);
+ }
+
+ @Override
+ public Iterator<RectF> getIterator(RectF tableRect, int totalElements) {
+ return new FixedTableModelIterator(this, tableRect, totalElements);
+ }
+
+ public float getCellWidth() {
+ return cellWidth;
+ }
+
+ public void setCellWidth(float cellWidth) {
+ this.cellWidth = cellWidth;
+ }
+
+ public float getCellHeight() {
+ return cellHeight;
+ }
+
+ public void setCellHeight(float cellHeight) {
+ this.cellHeight = cellHeight;
+ }
+
+ private class FixedTableModelIterator implements Iterator<RectF> {
+
+ private FixedTableModel model;
+ private RectF tableRect;
+ private RectF lastRect;
+ private int numElements;
+ private int lastElement;
+ protected FixedTableModelIterator(FixedTableModel model, RectF tableRect, int numElements) {
+ this.model = model;
+ this.tableRect = tableRect;
+ this.numElements = numElements;
+ lastRect = new RectF(
+ tableRect.left,
+ tableRect.top,
+ tableRect.left + model.getCellWidth(),
+ tableRect.top + model.getCellHeight());
+ }
+
+ @Override
+ public boolean hasNext() {
+ // was this the last element or is there no room in either axis for another cell?
+ return !(lastElement >= numElements || (isColumnFinished() && isRowFinished()));
+ }
+
+ private boolean isColumnFinished() {
+ return lastRect.bottom + model.getCellHeight() > tableRect.height();
+ }
+
+ private boolean isRowFinished() {
+ return lastRect.right + model.getCellWidth() > tableRect.width();
+ }
+
+ @Override
+ public RectF next() {
+ try {
+ if (lastElement == 0) {
+ return lastRect;
+ }
+
+ if (lastElement >= numElements) {
+ throw new IndexOutOfBoundsException();
+ }
+ switch (model.getOrder()) {
+ case ROW_MAJOR:
+ if (isColumnFinished()) {
+ moveOverAndUp();
+ } else {
+ moveDown();
+ }
+ break;
+ case COLUMN_MAJOR:
+ if (isRowFinished()) {
+ moveDownAndBack();
+ } else {
+ moveOver();
+ }
+ break;
+ default:
+ throw new UnsupportedOperationException();
+ }
+ return lastRect;
+ } finally {
+ lastElement++;
+ }
+ }
+
+ private void moveDownAndBack() {
+ //RectF rect = new RectF(lastRect);
+ lastRect.offsetTo(tableRect.left, lastRect.bottom);
+ //return rect;
+ }
+
+ private void moveOverAndUp() {
+ //RectF rect = new RectF(lastRect);
+ lastRect.offsetTo(lastRect.right, tableRect.top);
+ //return rect;
+ }
+
+ private void moveOver() {
+ //RectF rect = new RectF(lastRect);
+ lastRect.offsetTo(lastRect.right, lastRect.top);
+ //return rect;
+ }
+
+ private void moveDown() {
+ //RectF rect = new RectF(lastRect);
+ lastRect.offsetTo(lastRect.left, lastRect.bottom);
+ //return rect;
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/Formatter.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/Formatter.java
new file mode 100644
index 0000000..dbebaa2
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/Formatter.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.ui;
+
+import android.content.Context;
+import com.androidplot.Plot;
+import com.androidplot.util.Configurator;
+
+/**
+ * Base class of all Formatters. Encapsulates visual elements of a series; line style, color etc.
+ * Implementors of this class should include both a default constructor and a one argument
+ * constructor in the following form:
+ *
+ * <pre>
+ * {@code
+ * // provided as a convenience to users; allows instantiation and
+ * // xml configuration in a single line.
+ * public MyFormatter(Context ctx, int xmlCfgId) {
+ * // prevent configuration of classes derived from this one:
+ * if (getClass().equals(MyFormatter.class)) {
+ * Configurator.configure(ctx, this, xmlCfgId);
+ * }
+ * }
+ * </pre>
+ */
+public abstract class Formatter<PlotType extends Plot> {
+
+ public Formatter<PlotType> configure(Context ctx, int xmlCfgId) {
+ Configurator.configure(ctx, this, xmlCfgId);
+ return this;
+ }
+
+
+
+ /**
+ *
+ * @return The Class of SeriesRenderer that should be used.
+ */
+ public abstract Class<? extends SeriesRenderer> getRendererClass();
+
+ /**
+ *
+ * @return An instance of SeriesRenderer that took plot as an argument to its constructor.
+ */
+ public abstract SeriesRenderer getRendererInstance(PlotType plot);
+
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/LayoutManager.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/LayoutManager.java
new file mode 100644
index 0000000..1983ca7
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/LayoutManager.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.ui;
+
+import android.graphics.*;
+import android.view.MotionEvent;
+import android.view.View;
+import com.androidplot.exception.PlotRenderException;
+import com.androidplot.ui.widget.Widget;
+import com.androidplot.util.DisplayDimensions;
+import com.androidplot.util.ZLinkedList;
+
+public class LayoutManager extends ZLinkedList<Widget>
+ implements View.OnTouchListener, Resizable {
+ private boolean drawAnchorsEnabled = false;
+ private Paint anchorPaint;
+ private boolean drawOutlinesEnabled = false;
+ private Paint outlinePaint;
+ private boolean drawOutlineShadowsEnabled = false;
+ private Paint outlineShadowPaint;
+ private boolean drawMarginsEnabled = false;
+ private Paint marginPaint;
+ private boolean drawPaddingEnabled = false;
+ private Paint paddingPaint;
+ private DisplayDimensions displayDims = new DisplayDimensions();
+
+ // cache of widget rects
+ //private HashMap<Widget, DisplayDimensions> widgetRects;
+
+ {
+ //widgetRects = new HashMap<Widget, DisplayDimensions>();
+ anchorPaint = new Paint();
+ anchorPaint.setStyle(Paint.Style.FILL);
+ anchorPaint.setColor(Color.GREEN);
+ outlinePaint = new Paint();
+ outlinePaint.setColor(Color.GREEN);
+ outlinePaint.setStyle(Paint.Style.STROKE);
+ marginPaint = new Paint();
+ marginPaint.setColor(Color.YELLOW);
+ marginPaint.setStyle(Paint.Style.FILL);
+ marginPaint.setAlpha(200);
+ paddingPaint= new Paint();
+ paddingPaint.setColor(Color.BLUE);
+ paddingPaint.setStyle(Paint.Style.FILL);
+ paddingPaint.setAlpha(200);
+ }
+
+ /**
+ * Invoked immediately following XML configuration.
+ */
+ public synchronized void onPostInit() {
+ for(Widget w : elements()) {
+ w.onPostInit();
+ }
+ }
+
+ public LayoutManager() {
+ }
+
+ public void setMarkupEnabled(boolean enabled) {
+ setDrawOutlinesEnabled(enabled);
+ setDrawAnchorsEnabled(enabled);
+ setDrawMarginsEnabled(enabled);
+ setDrawPaddingEnabled(enabled);
+ setDrawOutlineShadowsEnabled(enabled);
+ }
+
+ public void draw(Canvas canvas) throws PlotRenderException {
+ if(isDrawMarginsEnabled()) {
+ drawSpacing(canvas, displayDims.canvasRect, displayDims.marginatedRect, marginPaint);
+ }
+ if (isDrawPaddingEnabled()) {
+ drawSpacing(canvas, displayDims.marginatedRect, displayDims.paddedRect, paddingPaint);
+ }
+ for (Widget widget : elements()) {
+ //int canvasState = canvas.save(Canvas.ALL_SAVE_FLAG); // preserve clipping etc
+ try {
+ canvas.save(Canvas.ALL_SAVE_FLAG);
+ PositionMetrics metrics = widget.getPositionMetrics();
+ float elementWidth = widget.getWidthPix(displayDims.paddedRect.width());
+ float elementHeight = widget.getHeightPix(displayDims.paddedRect.height());
+ PointF coords = widget.getElementCoordinates(elementHeight,
+ elementWidth, displayDims.paddedRect, metrics);
+
+ //RectF widgetRect = new RectF(coords.x, coords.y, coords.x + elementWidth, coords.y + elementHeight);
+ //DisplayDimensions dims = widgetRects.get(widget);
+ DisplayDimensions dims = widget.getWidgetDimensions();
+ //RectF widgetRect = widgetRects.get(widget);
+
+ if (drawOutlineShadowsEnabled) {
+ canvas.drawRect(dims.canvasRect, outlineShadowPaint);
+ }
+
+ // not positive why this is, but the rect clipped by clipRect is 1 less than the one drawn by drawRect.
+ // so this is necessary to avoid clipping borders. I suspect that its a floating point
+ // jitter issue.
+ if (widget.isClippingEnabled()) {
+ //RectF clipRect = new RectF(l-1, t-1, r + 1, b + 1);
+ //canvas.clipRect(clipRect, Region.Op.REPLACE);
+ canvas.clipRect(dims.canvasRect, Region.Op.INTERSECT);
+ }
+ widget.draw(canvas, dims.canvasRect);
+
+ //RectF marginatedWidgetRect = widget.getMarginatedRect(dims.canvasRect);
+ //RectF paddedWidgetRect = widget.getPaddedRect(marginatedWidgetRect);
+
+ if (drawMarginsEnabled) {
+ drawSpacing(canvas, dims.canvasRect, dims.marginatedRect, getMarginPaint());
+ }
+
+ if (drawPaddingEnabled) {
+ drawSpacing(canvas, dims.marginatedRect, dims.paddedRect, getPaddingPaint());
+ }
+
+ if (drawAnchorsEnabled) {
+ PointF anchorCoords =
+ Widget.getAnchorCoordinates(coords.x, coords.y, elementWidth,
+ elementHeight, metrics.getAnchor());
+ drawAnchor(canvas, anchorCoords);
+ }
+
+
+ if (drawOutlinesEnabled) {
+ outlinePaint.setAntiAlias(true);
+ canvas.drawRect(dims.canvasRect, outlinePaint);
+ }
+ } finally {
+ //canvas.restoreToCount(canvasState); // restore clipping etc.
+ canvas.restore();
+ }
+ }
+ }
+
+ private void drawSpacing(Canvas canvas, RectF outer, RectF inner, Paint paint) {
+ //int saved = canvas.save(Canvas.ALL_SAVE_FLAG);
+ try {
+ canvas.save(Canvas.ALL_SAVE_FLAG);
+ canvas.clipRect(inner, Region.Op.DIFFERENCE);
+ canvas.drawRect(outer, paint);
+ //canvas.restoreToCount(saved);
+ } finally {
+ canvas.restore();
+ }
+ }
+
+ protected void drawAnchor(Canvas canvas, PointF coords) {
+ float anchorSize = 4;
+ canvas.drawRect(coords.x-anchorSize, coords.y-anchorSize, coords.x+anchorSize, coords.y+anchorSize, anchorPaint);
+
+ }
+
+ public boolean isDrawOutlinesEnabled() {
+ return drawOutlinesEnabled;
+ }
+
+ public void setDrawOutlinesEnabled(boolean drawOutlinesEnabled) {
+ this.drawOutlinesEnabled = drawOutlinesEnabled;
+ }
+
+ public Paint getOutlinePaint() {
+ return outlinePaint;
+ }
+
+ public void setOutlinePaint(Paint outlinePaint) {
+ this.outlinePaint = outlinePaint;
+ }
+
+ public boolean isDrawAnchorsEnabled() {
+ return drawAnchorsEnabled;
+ }
+
+ public void setDrawAnchorsEnabled(boolean drawAnchorsEnabled) {
+ this.drawAnchorsEnabled = drawAnchorsEnabled;
+ }
+
+ public boolean isDrawMarginsEnabled() {
+ return drawMarginsEnabled;
+ }
+
+ public void setDrawMarginsEnabled(boolean drawMarginsEnabled) {
+ this.drawMarginsEnabled = drawMarginsEnabled;
+ }
+
+ public Paint getMarginPaint() {
+ return marginPaint;
+ }
+
+ public void setMarginPaint(Paint marginPaint) {
+ this.marginPaint = marginPaint;
+ }
+
+ public boolean isDrawPaddingEnabled() {
+ return drawPaddingEnabled;
+ }
+
+ public void setDrawPaddingEnabled(boolean drawPaddingEnabled) {
+ this.drawPaddingEnabled = drawPaddingEnabled;
+ }
+
+ public Paint getPaddingPaint() {
+ return paddingPaint;
+ }
+
+ public void setPaddingPaint(Paint paddingPaint) {
+ this.paddingPaint = paddingPaint;
+ }
+
+ public boolean isDrawOutlineShadowsEnabled() {
+ return drawOutlineShadowsEnabled;
+ }
+
+ public void setDrawOutlineShadowsEnabled(boolean drawOutlineShadowsEnabled) {
+ this.drawOutlineShadowsEnabled = drawOutlineShadowsEnabled;
+ if(drawOutlineShadowsEnabled && outlineShadowPaint == null) {
+ // use a default shadow effect in the case where none has been set:
+ outlineShadowPaint = new Paint();
+ outlineShadowPaint.setColor(Color.DKGRAY);
+ outlineShadowPaint.setStyle(Paint.Style.FILL);
+ outlineShadowPaint.setShadowLayer(3, 5, 5, Color.BLACK);
+ }
+ }
+
+ public Paint getOutlineShadowPaint() {
+ return outlineShadowPaint;
+ }
+
+ public void setOutlineShadowPaint(Paint outlineShadowPaint) {
+ this.outlineShadowPaint = outlineShadowPaint;
+ }
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ return false;
+ }
+
+ /**
+ * Recalculates layouts for all widgets using last set
+ * DisplayDimensions. Care should be excersized when choosing when
+ * to call this method as it is a relatively slow operation.
+ */
+ public void refreshLayout() {
+ //widgetRects.clear();
+ for (Widget widget : elements()) {
+ widget.layout(displayDims);
+ }
+ }
+
+ @Override
+ public void layout(final DisplayDimensions dims) {
+ this.displayDims = dims;
+
+ refreshLayout();
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/LayoutMetric.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/LayoutMetric.java
new file mode 100644
index 0000000..9061913
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/LayoutMetric.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.ui;
+
+
+
+abstract class LayoutMetric<LayoutType extends Enum> {
+
+ private LayoutType layoutType;
+
+ //private LayoutType layoutType;
+ private float value;
+ //private float lastRow;
+
+ public LayoutMetric(float value, LayoutType layoutType) {
+ validatePair(value, layoutType);
+ set(value, layoutType);
+ //setLayoutType(layoutType);
+ //setValue(value);
+ //setLayoutType(layoutType);
+ }
+
+ /**
+ * Verifies that the values passed in are valid for the layout algorithm being used.
+ * @param value
+ * @param layoutType
+ */
+ protected abstract void validatePair(float value, LayoutType layoutType);
+
+ public void set(float value, LayoutType layoutType) {
+ validatePair(value, layoutType);
+ this.value = value;
+ this.layoutType = layoutType;
+ }
+
+ public float getValue() {
+ return value;
+ }
+
+ public void setValue(float value) {
+ validatePair(value, layoutType);
+ this.value = value;
+ }
+
+ public abstract float getPixelValue(float size);
+
+ public LayoutType getLayoutType() {
+ return layoutType;
+ }
+
+ public void setLayoutType(LayoutType layoutType) {
+ validatePair(value, layoutType);
+ this.layoutType = layoutType;
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/PositionMetric.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/PositionMetric.java
new file mode 100644
index 0000000..7183760
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/PositionMetric.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.ui;
+
+public abstract class PositionMetric<LayoutType extends Enum> extends LayoutMetric<LayoutType> {
+
+ protected enum Origin {
+ FROM_BEGINING,
+ FROM_CENTER,
+ FROM_END
+ }
+
+ protected enum LayoutMode {
+ ABSOLUTE,
+ RELATIVE
+ }
+
+ public PositionMetric(float value, LayoutType layoutType) {
+ super(value, layoutType);
+ }
+
+ /**
+ * Throws IllegalArgumentException if there is a problem.
+ * @param value
+ * @param layoutMode
+ * @throws IllegalArgumentException
+ */
+ protected static void validateValue(float value, LayoutMode layoutMode) throws IllegalArgumentException {
+ switch(layoutMode) {
+ case ABSOLUTE:
+ break;
+ case RELATIVE:
+ if(value < -1 || value > 1) {
+ throw new IllegalArgumentException("Relative layout values must be within the range of -1 to 1.");
+ }
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown LayoutMode: " + layoutMode);
+ }
+
+ }
+
+ protected float getAbsolutePosition(float size, Origin origin) {
+ switch(origin) {
+ case FROM_BEGINING:
+ return getValue();
+ case FROM_CENTER:
+ return (size/2f) + getValue();
+ case FROM_END:
+ return size - getValue();
+ default:
+ throw new IllegalArgumentException("Unsupported Origin: " + origin);
+ }
+ }
+
+ protected float getRelativePosition(float size, Origin origin) {
+ //throw new UnsupportedOperationException("Not yet implemented.");
+
+ switch(origin) {
+ case FROM_BEGINING:
+ return size * getValue();
+ case FROM_CENTER:
+ return (size/2f) + ((size/2f) * getValue());
+ case FROM_END:
+ return size + (size*getValue());
+ default:
+ throw new IllegalArgumentException("Unsupported Origin: " + origin);
+ }
+
+ }
+
+
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/PositionMetrics.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/PositionMetrics.java
new file mode 100644
index 0000000..34d06a3
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/PositionMetrics.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.ui;
+
+public class PositionMetrics implements Comparable<PositionMetrics> {
+
+ private XPositionMetric xPositionMetric;
+ private YPositionMetric yPositionMetric;
+ private AnchorPosition anchor;
+ private float layerDepth;
+
+ public PositionMetrics(float x, XLayoutStyle xLayoutStyle, float y, YLayoutStyle yLayoutStyle, AnchorPosition anchor) {
+ setXPositionMetric(new XPositionMetric(x, xLayoutStyle));
+ setYPositionMetric(new YPositionMetric(y, yLayoutStyle));
+ setAnchor(anchor);
+
+ }
+
+ public YPositionMetric getYPositionMetric() {
+ return yPositionMetric;
+ }
+
+ public void setYPositionMetric(YPositionMetric yPositionMetric) {
+ this.yPositionMetric = yPositionMetric;
+ }
+
+ public AnchorPosition getAnchor() {
+ return anchor;
+ }
+
+ public void setAnchor(AnchorPosition anchor) {
+ this.anchor = anchor;
+ }
+
+ @Override
+ public int compareTo(PositionMetrics o) {
+ if(this.layerDepth < o.layerDepth) {
+ return -1;
+ } else if(this.layerDepth == o.layerDepth) {
+ return 0;
+ } else {
+ return 1;
+ }
+ }
+
+ public XPositionMetric getXPositionMetric() {
+ return xPositionMetric;
+ }
+
+ public void setXPositionMetric(XPositionMetric xPositionMetric) {
+ this.xPositionMetric = xPositionMetric;
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/RenderBundle.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/RenderBundle.java
new file mode 100644
index 0000000..75303ac
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/RenderBundle.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.ui;
+
+import com.androidplot.Series;
+import com.androidplot.xy.XYSeriesFormatter;
+
+public abstract class RenderBundle<RenderBundleType extends RenderBundle, SeriesType extends Series, SeriesFormatterType extends XYSeriesFormatter> {
+ //private XYDataset series;
+ private Series series;
+ private SeriesFormatterType formatter;
+
+ public RenderBundle(SeriesType series, SeriesFormatterType formatter) {
+ this.formatter = formatter;
+ this.series = series;
+ }
+
+ public Series getSeries() {
+ return series;
+ }
+
+ public void setSeries(Series series) {
+ this.series = series;
+ }
+
+ public SeriesFormatterType getFormatter() {
+ return formatter;
+ }
+
+ public void setFormatter(SeriesFormatterType formatter) {
+ this.formatter = formatter;
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/Resizable.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/Resizable.java
new file mode 100644
index 0000000..34a1c89
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/Resizable.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.ui;
+
+import android.graphics.Canvas;
+import android.graphics.RectF;
+import com.androidplot.util.DisplayDimensions;
+
+/**
+ * Used by classes that depend on dimensional values to lay themselves out and draw.
+ * Consideration should be given to synchronizing with any draw routines that also
+ * exist within the class.
+ */
+public interface Resizable {
+
+ /**
+ * Called when a change to the class' dimensions is made. This method is responsible
+ * for cascading calls to update for any logical children of this class, for example
+ * the Plot class is responsible for updating the LayoutManager. Note that while dims
+ * is marked final in this interface, the compiler will not enforce it. Implementors of
+ * this method should take care not to make changes to dims as this will affect parent
+ * Resizables in likely undesired ways.
+ * @param dims
+ */
+ public void layout(final DisplayDimensions dims);
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/SeriesAndFormatterList.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/SeriesAndFormatterList.java
new file mode 100644
index 0000000..7d90e32
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/SeriesAndFormatterList.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.ui;
+
+import com.androidplot.Series;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Associates a Series with a Formatter.
+ * @param <SeriesType>
+ * @param <FormatterType>
+ */
+public class SeriesAndFormatterList<SeriesType extends Series, FormatterType> {
+ private LinkedList<SeriesType> seriesList;
+ private LinkedList<FormatterType> formatterList;
+ {
+ seriesList = new LinkedList<SeriesType>();
+ formatterList = new LinkedList<FormatterType>();
+ }
+
+ public boolean contains(SeriesType series) {
+ return seriesList.contains(series);
+ }
+
+ public int size() {
+ return seriesList.size();
+ }
+
+ public List<SeriesType> getSeriesList() {
+ return seriesList;
+ }
+
+ public List<FormatterType> getFormatterList() {
+ return formatterList;
+ }
+
+ public boolean add(SeriesType series, FormatterType formatter) {
+ if(series == null || formatter == null) {
+ throw new IllegalArgumentException("series and formatter must not be null.");
+ }
+ if(seriesList.contains(series)) {
+ return false;
+ }
+ seriesList.add(series);
+ formatterList.add(formatter);
+ return true;
+ }
+
+ public boolean remove(SeriesType series) {
+ int index = seriesList.indexOf(series);
+ if(index < 0) {
+ return false;
+ }
+ seriesList.remove(index);
+ formatterList.remove(index);
+ return true;
+ }
+
+
+ public FormatterType getFormatter(SeriesType series) {
+ return formatterList.get(seriesList.indexOf(series));
+ }
+
+ public FormatterType getFormatter(int index) {
+ return formatterList.get(index);
+ }
+
+ public SeriesType getSeries(int index) {
+ return seriesList.get(index);
+ }
+
+ public FormatterType setFormatter(SeriesType series, FormatterType formatter) {
+ return formatterList.set(seriesList.indexOf(series), formatter);
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/SeriesRenderer.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/SeriesRenderer.java
new file mode 100644
index 0000000..b32f12d
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/SeriesRenderer.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.ui;
+
+import android.graphics.Canvas;
+import android.graphics.RectF;
+import android.graphics.Region;
+import com.androidplot.Series;
+import com.androidplot.exception.PlotRenderException;
+import com.androidplot.Plot;
+
+public abstract class SeriesRenderer
+ <PlotType extends Plot, SeriesType extends Series, SeriesFormatterType extends Formatter> {
+ private PlotType plot;
+
+ public SeriesRenderer(PlotType plot) {
+ this.plot = plot;
+ }
+
+ public PlotType getPlot() {
+ return plot;
+ }
+
+ public void setPlot(PlotType plot) {
+ this.plot = plot;
+ }
+
+ public SeriesAndFormatterList<SeriesType,SeriesFormatterType> getSeriesAndFormatterList() {
+ return plot.getSeriesAndFormatterListForRenderer(getClass());
+ }
+
+ public SeriesFormatterType getFormatter(SeriesType series) {
+ return (SeriesFormatterType) plot.getFormatter(series, getClass());
+ }
+
+ public void render(Canvas canvas, RectF plotArea) throws PlotRenderException {
+ onRender(canvas, plotArea);
+ }
+ public abstract void onRender(Canvas canvas, RectF plotArea) throws PlotRenderException;
+
+ /**
+ * Draw the legend icon in the rect passed in.
+ * @param canvas
+ * @param rect
+ */
+ protected abstract void doDrawLegendIcon(Canvas canvas, RectF rect, SeriesFormatterType formatter);
+
+ public void drawSeriesLegendIcon(Canvas canvas, RectF rect, SeriesFormatterType formatter) {
+ //int state = canvas.save(Canvas.CLIP_SAVE_FLAG);
+ try {
+ canvas.save(Canvas.ALL_SAVE_FLAG);
+ canvas.clipRect(rect, Region.Op.INTERSECT);
+ doDrawLegendIcon(canvas, rect, formatter);
+ //canvas.restoreToCount(state);
+ } finally {
+ canvas.restore();
+ }
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/SizeLayoutType.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/SizeLayoutType.java
new file mode 100644
index 0000000..7cb6ff3
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/SizeLayoutType.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.ui;
+
+/**
+ * SizeLayoutType is an enumeration of algorithms available for calculating an arbitrary dimension of a widget.
+ * Each algorithm also takes a single value called "val" in this doc.
+ * ABSOLUTE - Val is treated as absolute. If val is 5 then the size of the widget along the associated axis is 5 pixels.
+ *
+ * RELATIVE - Val represents the percentage of the display that the widget should fill along the associated axis. For example,
+ * if the total size of the owning plot is 120 pixels and val is set to 50 then the size of the widget along the associated axis
+ * is 60; 50% of 120 = 60.
+ *
+ * FILL - Widget completely fills along the associated axis, minus
+ */
+public enum SizeLayoutType {
+ ABSOLUTE,
+ RELATIVE,
+ FILL
+}
\ No newline at end of file
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/SizeMetric.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/SizeMetric.java
new file mode 100644
index 0000000..57e57c0
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/SizeMetric.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.ui;
+
+/**
+ * Encapsulates a sizing algorithm and an associated value.
+ *
+ * The available algorithms list are stored in the {@link SizeLayoutType} enumeration.
+ *
+ */
+public class SizeMetric extends LayoutMetric<SizeLayoutType> {
+
+ public SizeMetric(float value, SizeLayoutType layoutType) {
+ super(value, layoutType);
+ }
+
+ protected void validatePair(float value, SizeLayoutType layoutType) {
+ switch(layoutType) {
+ case RELATIVE:
+ if(value < 0 || value > 1) {
+ throw new IllegalArgumentException("SizeMetric Relative and Hybrid layout values must be within the range of 0 to 1.");
+ }
+ case ABSOLUTE:
+ case FILL:
+ default:
+ break;
+ }
+ }
+
+ @Override
+ public float getPixelValue(float size) {
+ //switch(layoutType)
+ switch(getLayoutType()) {
+ case ABSOLUTE:
+ return getValue();
+ case RELATIVE:
+ return getValue() * size;
+ case FILL:
+ return size - getValue();
+ default:
+ throw new IllegalArgumentException("Unsupported LayoutType: " + this.getLayoutType());
+ }
+ }
+
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/SizeMetrics.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/SizeMetrics.java
new file mode 100644
index 0000000..28f0ce5
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/SizeMetrics.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.ui;
+
+import android.graphics.RectF;
+import com.androidplot.util.PixelUtils;
+
+/**
+ * Encapsulates sizing preferences associated with a Widget; how/if it scales etc.
+ */
+public class SizeMetrics {
+ private SizeMetric heightMetric;
+ private SizeMetric widthMetric;
+
+ /**
+ * Convenience constructor. Wraps {@link #SizeMetrics(SizeMetric, SizeMetric)}.
+ * @param height Height value used algorithm to calculate the height of the associated widget(s).
+ * @param heightLayoutType Algorithm used to calculate the height of the associated widget(s).
+ * @param width Width value used algorithm to calculate the width of the associated widget(s).
+ * @param widthLayoutType Algorithm used to calculate the width of the associated widget(s).
+ */
+ public SizeMetrics(float height, SizeLayoutType heightLayoutType, float width, SizeLayoutType widthLayoutType) {
+ heightMetric = new SizeMetric(height, heightLayoutType);
+ widthMetric = new SizeMetric(width, widthLayoutType);
+ }
+
+ /**
+ * Creates a new SizeMetrics instance using the specified size layout algorithm and value.
+ * See {@link SizeMetric} for details on what can be passed in.
+ * @param heightMetric
+ * @param widthMetric
+ */
+ public SizeMetrics(SizeMetric heightMetric, SizeMetric widthMetric) {
+ this.heightMetric = heightMetric;
+ this.widthMetric = widthMetric;
+ }
+
+ public SizeMetric getHeightMetric() {
+ return heightMetric;
+ }
+
+ public void setHeightMetric(SizeMetric heightMetric) {
+ this.heightMetric = heightMetric;
+ }
+
+ public SizeMetric getWidthMetric() {
+ return widthMetric;
+ }
+
+ /**
+ * Calculates a RectF with calculated width and height. The top-left corner is set to 0,0.
+ * @param canvasRect
+ * @return
+ */
+ public RectF getRectF(RectF canvasRect) {
+ return new RectF(
+ 0,
+ 0,
+ widthMetric.getPixelValue(canvasRect.width()),
+ heightMetric.getPixelValue(canvasRect.height()));
+ }
+
+ /**
+ * Same as getRectF but with edges rounded to the nearest full pixel.
+ * @param canvasRect
+ * @return
+ */
+ public RectF getRoundedRect(RectF canvasRect) {
+ return PixelUtils.nearestPixRect(0, 0, widthMetric.getPixelValue(canvasRect.width()),
+ heightMetric.getPixelValue(canvasRect.height()));
+ }
+
+ public void setWidthMetric(SizeMetric widthMetric) {
+ this.widthMetric = widthMetric;
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/TableModel.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/TableModel.java
new file mode 100644
index 0000000..96eaa7a
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/TableModel.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.ui;
+
+import android.graphics.RectF;
+
+import java.util.Iterator;
+
+public abstract class TableModel {
+ private TableOrder order;
+
+ protected TableModel(TableOrder order) {
+ setOrder(order);
+ }
+
+ public abstract Iterator<RectF> getIterator(RectF tableRect, int totalElements);
+
+ //public abstract RectF getCellRect(RectF tableRect, int numElements);
+
+ public TableOrder getOrder() {
+ return order;
+ }
+
+ public void setOrder(TableOrder order) {
+ this.order = order;
+ }
+
+ public enum Axis {
+ ROW,
+ COLUMN
+ }
+
+ public enum CellSizingMethod {
+ FIXED,
+ FILL
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/TableOrder.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/TableOrder.java
new file mode 100644
index 0000000..5af231e
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/TableOrder.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.ui;
+
+public enum TableOrder {
+ ROW_MAJOR, // standard c-style
+ COLUMN_MAJOR
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/TableSizingMethod.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/TableSizingMethod.java
new file mode 100644
index 0000000..501fb92
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/TableSizingMethod.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.ui;
+
+/**
+ * The sizing methods available to a table.
+ * AUTO: The table is divided evenly int tableSize/numElements sections.
+ * FIXED: Each element in the table has a predefined number of pixels
+ * regardless of what the table's dimensions actually are.
+ */
+public enum TableSizingMethod {
+ AUTO,
+ FIXED
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/TextOrientationType.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/TextOrientationType.java
new file mode 100644
index 0000000..0b78ddd
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/TextOrientationType.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.ui;
+
+public enum TextOrientationType {
+ HORIZONTAL,
+ VERTICAL_ASCENDING,
+ VERTICAL_DESCENDING
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/XLayoutStyle.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/XLayoutStyle.java
new file mode 100644
index 0000000..9264bf0
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/XLayoutStyle.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.ui;
+
+public enum XLayoutStyle {
+ ABSOLUTE_FROM_LEFT,
+ ABSOLUTE_FROM_RIGHT,
+ ABSOLUTE_FROM_CENTER,
+ RELATIVE_TO_LEFT,
+ RELATIVE_TO_RIGHT,
+ RELATIVE_TO_CENTER
+
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/XPositionMetric.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/XPositionMetric.java
new file mode 100644
index 0000000..fac02d2
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/XPositionMetric.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.ui;
+
+
+import com.androidplot.ui.PositionMetric;
+import com.androidplot.ui.XLayoutStyle;
+
+public class XPositionMetric extends PositionMetric<XLayoutStyle> {
+
+ //private XLayoutStyle layoutType;
+
+ public XPositionMetric(float value, XLayoutStyle layoutStyle) {
+ super(value, layoutStyle);
+ validatePair(value, layoutStyle);
+ //this.layoutStyle = layoutStyle;
+ }
+
+ /**
+ * Throws IllegalArgumentException if there is a problem.
+ * @param value
+ */
+ protected void validatePair(float value, XLayoutStyle layoutStyle) {
+ switch(layoutStyle) {
+ case ABSOLUTE_FROM_LEFT:
+ case ABSOLUTE_FROM_RIGHT:
+ case ABSOLUTE_FROM_CENTER:
+ validateValue(value, PositionMetric.LayoutMode.ABSOLUTE);
+ break;
+ case RELATIVE_TO_LEFT:
+ case RELATIVE_TO_RIGHT:
+ case RELATIVE_TO_CENTER:
+ validateValue(value, PositionMetric.LayoutMode.RELATIVE);
+ }
+ }
+
+ @Override
+ public float getPixelValue(float size) {
+ switch(getLayoutType()) {
+ case ABSOLUTE_FROM_LEFT:
+ return this.getAbsolutePosition(size, PositionMetric.Origin.FROM_BEGINING);
+ case ABSOLUTE_FROM_RIGHT:
+ return this.getAbsolutePosition(size, PositionMetric.Origin.FROM_END);
+ case ABSOLUTE_FROM_CENTER:
+ return this.getAbsolutePosition(size, PositionMetric.Origin.FROM_CENTER);
+ case RELATIVE_TO_LEFT:
+ return this.getRelativePosition(size, PositionMetric.Origin.FROM_BEGINING);
+ case RELATIVE_TO_RIGHT:
+ return this.getRelativePosition(size, PositionMetric.Origin.FROM_END);
+ case RELATIVE_TO_CENTER:
+ return this.getRelativePosition(size, PositionMetric.Origin.FROM_CENTER);
+ default:
+ throw new IllegalArgumentException("Unsupported LayoutType: " + this.getLayoutType());
+ }
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/YLayoutStyle.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/YLayoutStyle.java
new file mode 100644
index 0000000..64fb467
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/YLayoutStyle.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.ui;
+
+public enum YLayoutStyle {
+ ABSOLUTE_FROM_TOP,
+ ABSOLUTE_FROM_BOTTOM,
+ ABSOLUTE_FROM_CENTER,
+ RELATIVE_TO_TOP,
+ RELATIVE_TO_BOTTOM,
+ RELATIVE_TO_CENTER
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/YPositionMetric.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/YPositionMetric.java
new file mode 100644
index 0000000..1101c1e
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/YPositionMetric.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.ui;
+
+import com.androidplot.ui.PositionMetric;
+import com.androidplot.ui.YLayoutStyle;
+
+public class YPositionMetric extends PositionMetric<YLayoutStyle> {
+ /*
+ public enum YLayoutStyle {
+ ABSOLUTE_FROM_TOP,
+ ABSOLUTE_FROM_BOTTOM,
+ ABSOLUTE_FROM_CENTER,
+ RELATIVE_TO_TOP,
+ RELATIVE_TO_BOTTOM,
+ RELATIVE_TO_CENTER
+ }
+ */
+
+ //private YLayoutStyle layoutType;
+
+ public YPositionMetric(float value, YLayoutStyle layoutStyle) {
+ super(value, layoutStyle);
+ //this.layoutStyle = layoutStyle;
+
+
+ }
+
+ /*
+ @Override
+ public void set(float value, YLayoutStyle layoutType) {
+ validatePair(value, layoutType);
+ super.set(value, layoutType);
+ }
+
+ @Override
+ public void setLayoutType(YLayoutStyle layoutType) {
+ validatePair(getValue(), layoutType);
+ super.setLayoutType(layoutType);
+ }
+
+ @Override
+ public void setValue(float value) {
+ validatePair(value, getLayoutType());
+ super.setValue(value);
+ }
+ */
+
+ /**
+ * Throws IllegalArgumentException if there is a problem.
+ * @param value
+ */
+ protected void validatePair(float value, YLayoutStyle layoutStyle) {
+ switch(layoutStyle) {
+ case ABSOLUTE_FROM_TOP:
+ case ABSOLUTE_FROM_BOTTOM:
+ case ABSOLUTE_FROM_CENTER:
+ validateValue(value, PositionMetric.LayoutMode.ABSOLUTE);
+ break;
+ case RELATIVE_TO_TOP:
+ case RELATIVE_TO_BOTTOM:
+ case RELATIVE_TO_CENTER:
+ validateValue(value, PositionMetric.LayoutMode.RELATIVE);
+ }
+ }
+
+ @Override
+ public float getPixelValue(float size) {
+ switch(getLayoutType()) {
+ case ABSOLUTE_FROM_TOP:
+ return this.getAbsolutePosition(size, PositionMetric.Origin.FROM_BEGINING);
+ case ABSOLUTE_FROM_BOTTOM:
+ return this.getAbsolutePosition(size, PositionMetric.Origin.FROM_END);
+ case ABSOLUTE_FROM_CENTER:
+ return this.getAbsolutePosition(size, PositionMetric.Origin.FROM_CENTER);
+ case RELATIVE_TO_TOP:
+ return this.getRelativePosition(size, PositionMetric.Origin.FROM_BEGINING);
+ case RELATIVE_TO_BOTTOM:
+ return this.getRelativePosition(size, PositionMetric.Origin.FROM_END);
+ case RELATIVE_TO_CENTER:
+ return this.getRelativePosition(size, PositionMetric.Origin.FROM_CENTER);
+ default:
+ throw new IllegalArgumentException("Unsupported LayoutType: " + this.getLayoutType());
+ }
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/widget/EmptyWidget.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/widget/EmptyWidget.java
new file mode 100644
index 0000000..f3da9fc
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/widget/EmptyWidget.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.ui.widget;
+
+import android.graphics.Canvas;
+import android.graphics.RectF;
+import com.androidplot.exception.PlotRenderException;
+import com.androidplot.ui.LayoutManager;
+import com.androidplot.ui.SizeMetrics;
+
+public class EmptyWidget extends Widget {
+
+ public EmptyWidget(LayoutManager layoutManager, SizeMetrics sizeMetrics) {
+ super(layoutManager, sizeMetrics);
+ }
+ @Override
+ protected void doOnDraw(Canvas canvas, RectF widgetRect) throws PlotRenderException {
+ // nothing to do
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/widget/TextLabelWidget.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/widget/TextLabelWidget.java
new file mode 100644
index 0000000..96615b8
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/widget/TextLabelWidget.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.ui.widget;
+
+import android.graphics.*;
+import android.util.Log;
+import com.androidplot.ui.*;
+import com.androidplot.util.FontUtils;
+
+public class TextLabelWidget extends Widget {
+ private static final String TAG = TextLabelWidget.class.getName();
+
+ private String text;
+ private Paint labelPaint;
+
+ private TextOrientationType orientation;
+
+ private boolean autoPackEnabled = true;
+
+ {
+ labelPaint = new Paint();
+ labelPaint.setColor(Color.WHITE);
+ labelPaint.setAntiAlias(true);
+ labelPaint.setTextAlign(Paint.Align.CENTER);
+ }
+
+ public TextLabelWidget(LayoutManager layoutManager, SizeMetrics sizeMetrics) {
+ this(layoutManager, sizeMetrics, TextOrientationType.HORIZONTAL);
+ }
+
+ public TextLabelWidget(LayoutManager layoutManager, String title, SizeMetrics sizeMetrics, TextOrientationType orientation) {
+ this(layoutManager, sizeMetrics, orientation);
+ setText(title);
+ }
+
+ public TextLabelWidget(LayoutManager layoutManager, SizeMetrics sizeMetrics, TextOrientationType orientation) {
+ super(layoutManager, new SizeMetrics(0, SizeLayoutType.ABSOLUTE, 0, SizeLayoutType.ABSOLUTE));
+ //this.plot = plot;
+ //this.setWidth(labelPaint.measureText(plot.getTitle()));
+ //this.setHeight(labelPaint.getFontMetrics().top);
+ setSize(sizeMetrics);
+ this.orientation = orientation;
+ }
+
+ @Override
+ protected void onMetricsChanged(SizeMetrics olds, SizeMetrics news) {
+ if(autoPackEnabled) {
+ pack();
+ }
+ }
+
+ @Override
+ public void onPostInit() {
+ if(autoPackEnabled) {
+ pack();
+ }
+ }
+
+ //protected abstract String getText();
+
+ /**
+ * Sets the dimensions of the widget to exactly contain the text contents
+ */
+ public void pack() {
+ Log.d(TAG, "Packing...");
+ Rect size = FontUtils.getStringDimensions(text, getLabelPaint());
+ if(size == null) {
+ Log.w(TAG, "Attempt to pack empty text.");
+ return;
+ }
+ switch(orientation) {
+ case HORIZONTAL:
+ setSize(new SizeMetrics(size.height(), SizeLayoutType.ABSOLUTE, size.width()+2, SizeLayoutType.ABSOLUTE));
+ break;
+ case VERTICAL_ASCENDING:
+ case VERTICAL_DESCENDING:
+ setSize(new SizeMetrics(size.width(), SizeLayoutType.ABSOLUTE, size.height()+2, SizeLayoutType.ABSOLUTE));
+ break;
+ }
+ refreshLayout();
+
+ }
+
+ /**
+ * Do not call this method directly. It is indirectly invoked every time a plot is
+ * redrawn.
+ * @param canvas The Canvas to draw onto
+ * @param widgetRect the size and coordinates of this widget
+ */
+ @Override
+ public void doOnDraw(Canvas canvas, RectF widgetRect) {
+ if(text == null || text.length() == 0) {
+ return;
+ }
+ //FontUtils.getStringDimensions(text, labelPaint);
+ float vOffset = labelPaint.getFontMetrics().descent;
+ PointF start = getAnchorCoordinates(widgetRect,
+ AnchorPosition.CENTER);
+
+ // BEGIN ROTATION CALCULATION
+ //int canvasState = canvas.save(Canvas.ALL_SAVE_FLAG);
+
+ try {
+ canvas.save(Canvas.ALL_SAVE_FLAG);
+ canvas.translate(start.x, start.y);
+ switch (orientation) {
+ case HORIZONTAL:
+ break;
+ case VERTICAL_ASCENDING:
+ canvas.rotate(-90);
+ break;
+ case VERTICAL_DESCENDING:
+ canvas.rotate(90);
+ break;
+ default:
+
+ throw new UnsupportedOperationException("Orientation " + orientation + " not yet implemented for TextLabelWidget.");
+ }
+ canvas.drawText(text, 0, vOffset, labelPaint);
+ } finally {
+ //canvas.restoreToCount(canvasState);
+ canvas.restore();
+ }
+
+ // END ROTATION CALCULATION
+ }
+
+ public Paint getLabelPaint() {
+ return labelPaint;
+ }
+
+ public void setLabelPaint(Paint labelPaint) {
+ this.labelPaint = labelPaint;
+
+ // when paint changes, packing params change too so check
+ // to see if we need to resize:
+ if(autoPackEnabled) {
+ pack();
+ }
+ }
+
+ public TextOrientationType getOrientation() {
+ return orientation;
+ }
+
+ public void setOrientation(TextOrientationType orientation) {
+ this.orientation = orientation;
+ if(autoPackEnabled) {
+ pack();
+ }
+ }
+
+ public boolean isAutoPackEnabled() {
+ return autoPackEnabled;
+ }
+
+ public void setAutoPackEnabled(boolean autoPackEnabled) {
+ this.autoPackEnabled = autoPackEnabled;
+ if(autoPackEnabled) {
+ pack();
+ }
+ }
+
+ public void setText(String text) {
+ Log.d(TAG, "Setting textLabel to: " + text);
+ this.text = text;
+ if(autoPackEnabled) {
+ pack();
+ }
+ }
+
+ public String getText() {
+ return text;
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/widget/Widget.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/widget/Widget.java
new file mode 100644
index 0000000..1e6e663
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/widget/Widget.java
@@ -0,0 +1,403 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.ui.widget;
+
+import android.graphics.*;
+import com.androidplot.exception.PlotRenderException;
+import com.androidplot.ui.*;
+import com.androidplot.util.DisplayDimensions;
+import com.androidplot.ui.XLayoutStyle;
+import com.androidplot.ui.YLayoutStyle;
+import com.androidplot.util.PixelUtils;
+
+/**
+ * A Widget is a graphical sub-element of a Plot that can be positioned relative
+ * to the bounds of the Plot.
+ */
+public abstract class Widget implements BoxModelable, Resizable {
+
+ private Paint borderPaint;
+ private Paint backgroundPaint;
+ private boolean clippingEnabled = true;
+ private BoxModel boxModel = new BoxModel();
+ private SizeMetrics sizeMetrics;
+ private DisplayDimensions plotDimensions = new DisplayDimensions();
+ private DisplayDimensions widgetDimensions = new DisplayDimensions();
+ private boolean isVisible = true;
+ private PositionMetrics positionMetrics;
+ private LayoutManager layoutManager;
+
+ public Widget(LayoutManager layoutManager, SizeMetric heightMetric, SizeMetric widthMetric) {
+ this(layoutManager, new SizeMetrics(heightMetric, widthMetric));
+ }
+
+ public Widget(LayoutManager layoutManager, SizeMetrics sizeMetrics) {
+ this.layoutManager = layoutManager;
+ SizeMetrics oldSize = this.sizeMetrics;
+ setSize(sizeMetrics);
+ onMetricsChanged(oldSize, sizeMetrics);
+ }
+
+ public DisplayDimensions getWidgetDimensions() {
+ return widgetDimensions;
+ }
+
+ public AnchorPosition getAnchor() {
+ return getPositionMetrics().getAnchor();
+ }
+
+ public void setAnchor(AnchorPosition anchor) {
+ getPositionMetrics().setAnchor(anchor);
+ }
+
+
+ /**
+ * Same as {@link #position(float, com.androidplot.ui.XLayoutStyle, float, com.androidplot.ui.YLayoutStyle, com.androidplot.ui.AnchorPosition)}
+ * but with the anchor parameter defaulted to the upper left corner.
+ * @param x
+ * @param xLayoutStyle
+ * @param y
+ * @param yLayoutStyle
+ */
+ public void position(float x, XLayoutStyle xLayoutStyle, float y, YLayoutStyle yLayoutStyle) {
+ position(x, xLayoutStyle, y, yLayoutStyle, AnchorPosition.LEFT_TOP);
+ }
+
+ /**
+ * @param x X-Coordinate of the top left corner of element. When using RELATIVE, must be a value between 0 and 1.
+ * @param xLayoutStyle LayoutType to use when orienting this element's X-Coordinate.
+ * @param y Y_VALS_ONLY-Coordinate of the top-left corner of element. When using RELATIVE, must be a value between 0 and 1.
+ * @param yLayoutStyle LayoutType to use when orienting this element's Y_VALS_ONLY-Coordinate.
+ * @param anchor The point of reference used by this positioning call.
+ */
+ public void position(float x, XLayoutStyle xLayoutStyle, float y,
+ YLayoutStyle yLayoutStyle, AnchorPosition anchor) {
+ setPositionMetrics(new PositionMetrics(x, xLayoutStyle, y, yLayoutStyle, anchor));
+ layoutManager.addToTop(this);
+ }
+
+ /**
+ * Can be overridden by subclasses to respond to resizing events.
+ *
+ * @param oldSize
+ * @param newSize
+ */
+ protected void onMetricsChanged(SizeMetrics oldSize, SizeMetrics newSize) {
+ }
+
+ /**
+ * Can be overridden by subclasses to handle any final resizing etc. that
+ * can only be done after XML configuration etc. has completed.
+ */
+ public void onPostInit() {
+ }
+
+ /**
+ * Determines whether or not point lies within this Widget.
+ *
+ * @param point
+ * @return
+ */
+ public boolean containsPoint(PointF point) {
+ //return outlineRect != null && outlineRect.contains(point.x, point.y);
+ return widgetDimensions.canvasRect.contains(point.x, point.y);
+ }
+
+ public void setSize(SizeMetrics sizeMetrics) {
+ this.sizeMetrics = sizeMetrics;
+ }
+
+
+ public void setWidth(float width) {
+ sizeMetrics.getWidthMetric().setValue(width);
+ }
+
+ public void setWidth(float width, SizeLayoutType layoutType) {
+ sizeMetrics.getWidthMetric().set(width, layoutType);
+ }
+
+ public void setHeight(float height) {
+ sizeMetrics.getHeightMetric().setValue(height);
+ }
+
+ public void setHeight(float height, SizeLayoutType layoutType) {
+ sizeMetrics.getHeightMetric().set(height, layoutType);
+ }
+
+ public SizeMetric getWidthMetric() {
+ return sizeMetrics.getWidthMetric();
+ }
+
+ public SizeMetric getHeightMetric() {
+ return sizeMetrics.getHeightMetric();
+ }
+
+ public float getWidthPix(float size) {
+ return sizeMetrics.getWidthMetric().getPixelValue(size);
+ }
+
+ public float getHeightPix(float size) {
+ return sizeMetrics.getHeightMetric().getPixelValue(size);
+ }
+
+ public RectF getMarginatedRect(RectF widgetRect) {
+ return boxModel.getMarginatedRect(widgetRect);
+ }
+
+ public RectF getPaddedRect(RectF widgetMarginRect) {
+ return boxModel.getPaddedRect(widgetMarginRect);
+ }
+
+ public void setMarginRight(float marginRight) {
+ boxModel.setMarginRight(marginRight);
+ }
+
+ public void setMargins(float left, float top, float right, float bottom) {
+ boxModel.setMargins(left, top, right, bottom);
+ }
+
+ public void setPadding(float left, float top, float right, float bottom) {
+ boxModel.setPadding(left, top, right, bottom);
+ }
+
+ public float getMarginTop() {
+ return boxModel.getMarginTop();
+ }
+
+ public void setMarginTop(float marginTop) {
+ boxModel.setMarginTop(marginTop);
+ }
+
+ public float getMarginBottom() {
+ return boxModel.getMarginBottom();
+ }
+
+ @Override
+ public float getPaddingLeft() {
+ return boxModel.getPaddingLeft();
+ }
+
+ @Override
+ public void setPaddingLeft(float paddingLeft) {
+ boxModel.setPaddingLeft(paddingLeft);
+ }
+
+ @Override
+ public float getPaddingTop() {
+ return boxModel.getPaddingTop();
+ }
+
+ @Override
+ public void setPaddingTop(float paddingTop) {
+ boxModel.setPaddingTop(paddingTop);
+ }
+
+ @Override
+ public float getPaddingRight() {
+ return boxModel.getPaddingRight();
+ }
+
+ @Override
+ public void setPaddingRight(float paddingRight) {
+ boxModel.setPaddingRight(paddingRight);
+ }
+
+ @Override
+ public float getPaddingBottom() {
+ return boxModel.getPaddingBottom();
+ }
+
+ @Override
+ public void setPaddingBottom(float paddingBottom) {
+ boxModel.setPaddingBottom(paddingBottom);
+ }
+
+ @SuppressWarnings("SameParameterValue")
+ public void setMarginBottom(float marginBottom) {
+ boxModel.setMarginBottom(marginBottom);
+ }
+
+ public float getMarginLeft() {
+ return boxModel.getMarginLeft();
+ }
+
+ public void setMarginLeft(float marginLeft) {
+ boxModel.setMarginLeft(marginLeft);
+ }
+
+ public float getMarginRight() {
+ return boxModel.getMarginRight();
+ }
+
+ /**
+ * Causes the pixel dimensions used for rendering this Widget
+ * to be recalculated. Should be called any time a parameter that factors
+ * into this Widget's size or position is altered.
+ */
+ public synchronized void refreshLayout() {
+ if(positionMetrics == null) {
+ // make sure positionMetrics have been set. this method can be
+ // automatically called during xml configuration of certain params
+ // before the widget is fully configured.
+ return;
+ }
+ float elementWidth = getWidthPix(plotDimensions.paddedRect.width());
+ float elementHeight = getHeightPix(plotDimensions.paddedRect.height());
+ PointF coords = getElementCoordinates(elementHeight,
+ elementWidth, plotDimensions.paddedRect, positionMetrics);
+
+ RectF widgetRect = new RectF(coords.x, coords.y,
+ coords.x + elementWidth, coords.y + elementHeight);
+ RectF marginatedWidgetRect = getMarginatedRect(widgetRect);
+ RectF paddedWidgetRect = getPaddedRect(marginatedWidgetRect);
+ widgetDimensions = new DisplayDimensions(widgetRect,
+ marginatedWidgetRect, paddedWidgetRect);
+ }
+
+ @Override
+ public synchronized void layout(final DisplayDimensions plotDimensions) {
+ this.plotDimensions = plotDimensions;
+ refreshLayout();
+ }
+
+ public PointF getElementCoordinates(float height, float width, RectF viewRect, PositionMetrics metrics) {
+ float x = metrics.getXPositionMetric().getPixelValue(viewRect.width()) + viewRect.left;
+ float y = metrics.getYPositionMetric().getPixelValue(viewRect.height()) + viewRect.top;
+ PointF point = new PointF(x, y);
+ return PixelUtils.sub(point, getAnchorOffset(width, height, metrics.getAnchor()));
+ }
+
+ public static PointF getAnchorOffset(float width, float height, AnchorPosition anchorPosition) {
+ PointF point = new PointF();
+ switch (anchorPosition) {
+ case LEFT_TOP:
+ break;
+ case LEFT_MIDDLE:
+ point.set(0, height / 2);
+ break;
+ case LEFT_BOTTOM:
+ point.set(0, height);
+ break;
+ case RIGHT_TOP:
+ point.set(width, 0);
+ break;
+ case RIGHT_BOTTOM:
+ point.set(width, height);
+ break;
+ case RIGHT_MIDDLE:
+ point.set(width, height / 2);
+ break;
+ case TOP_MIDDLE:
+ point.set(width / 2, 0);
+ break;
+ case BOTTOM_MIDDLE:
+ point.set(width / 2, height);
+ break;
+ case CENTER:
+ point.set(width / 2, height / 2);
+ break;
+ default:
+ throw new IllegalArgumentException("Unsupported anchor location: " + anchorPosition);
+ }
+ return point;
+ }
+
+ public static PointF getAnchorCoordinates(RectF widgetRect, AnchorPosition anchorPosition) {
+ return PixelUtils.add(new PointF(widgetRect.left, widgetRect.top),
+ getAnchorOffset(widgetRect.width(), widgetRect.height(), anchorPosition));
+ }
+
+ public static PointF getAnchorCoordinates(float x, float y, float width, float height, AnchorPosition anchorPosition) {
+ return getAnchorCoordinates(new RectF(x, y, x+width, y+height), anchorPosition);
+ }
+
+ public void draw(Canvas canvas, RectF widgetRect) throws PlotRenderException {
+ //outlineRect = widgetRect;
+ if (isVisible()) {
+ if (backgroundPaint != null) {
+ drawBackground(canvas, widgetDimensions.canvasRect);
+ }
+
+ /* RectF marginatedRect = new RectF(outlineRect.left + marginLeft,
+ outlineRect.top + marginTop,
+ outlineRect.right - marginRight,
+ outlineRect.bottom - marginBottom);*/
+
+ /*RectF marginatedRect = boxModel.getMarginatedRect(widgetRect);
+ RectF paddedRect = boxModel.getPaddedRect(marginatedRect);*/
+ doOnDraw(canvas, widgetDimensions.paddedRect);
+
+ if (borderPaint != null) {
+ drawBorder(canvas, widgetDimensions.paddedRect);
+ }
+ }
+ }
+
+ protected void drawBorder(Canvas canvas, RectF paddedRect) {
+ canvas.drawRect(paddedRect, borderPaint);
+ }
+
+ protected void drawBackground(Canvas canvas, RectF widgetRect) {
+ canvas.drawRect(widgetRect, backgroundPaint);
+ }
+
+ /**
+ * @param canvas The Canvas to draw onto
+ * @param widgetRect the size and coordinates of this widget
+ */
+ protected abstract void doOnDraw(Canvas canvas, RectF widgetRect) throws PlotRenderException;
+
+ public Paint getBorderPaint() {
+ return borderPaint;
+ }
+
+ public void setBorderPaint(Paint borderPaint) {
+ this.borderPaint = borderPaint;
+ }
+
+ public Paint getBackgroundPaint() {
+ return backgroundPaint;
+ }
+
+ public void setBackgroundPaint(Paint backgroundPaint) {
+ this.backgroundPaint = backgroundPaint;
+ }
+
+ public boolean isClippingEnabled() {
+ return clippingEnabled;
+ }
+
+ public void setClippingEnabled(boolean clippingEnabled) {
+ this.clippingEnabled = clippingEnabled;
+ }
+
+ public boolean isVisible() {
+ return isVisible;
+ }
+
+ public void setVisible(boolean visible) {
+ isVisible = visible;
+ }
+
+ public PositionMetrics getPositionMetrics() {
+ return positionMetrics;
+ }
+
+ public void setPositionMetrics(PositionMetrics positionMetrics) {
+ this.positionMetrics = positionMetrics;
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/util/Configurator.java b/AndroidPlot-Core/src/main/java/com/androidplot/util/Configurator.java
new file mode 100644
index 0000000..a4f7ae0
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/util/Configurator.java
@@ -0,0 +1,346 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.util;
+
+import android.content.Context;
+import android.content.res.XmlResourceParser;
+import android.graphics.Color;
+import android.util.Log;
+import android.util.TypedValue;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.util.HashMap;
+
+/**
+ * Utility class for "configuring" objects via XML config files. Supports the following field types:
+ * String
+ * Enum
+ * int
+ * float
+ * boolean
+ * <p/>
+ * Config files should be stored in /res/xml. Given the XML configuration /res/xml/myConfig.xml, one can apply the
+ * configuration to an Object instance as follows:
+ * <p/>
+ * MyObject obj = new MyObject();
+ * Configurator.configure(obj, R.xml.myConfig);
+ * <p/>
+ * WHAT IT DOES:
+ * Given a series of parameters stored in an XML file, Configurator iterates through each parameter, using the name
+ * as a map to the field within a given object. For example:
+ * <p/>
+ * <pre>
+ * {@code
+ * <config car.engine.sparkPlug.condition="poor"/>
+ * }
+ * </pre>
+ * <p/>
+ * Given a Car instance car and assuming the method setCondition(String) exists within the SparkPlug class,
+ * Configurator does the following:
+ * <p/>
+ * <pre>
+ * {@code
+ * car.getEngine().getSparkPlug().setCondition("poor");
+ * }
+ * </pre>
+ * <p/>
+ * Now let's pretend that setCondition takes an instance of the Condition enum as it's argument.
+ * Configurator then does the following:
+ * <p/>
+ * car.getEngine().getSparkPlug().setCondition(Condition.valueOf("poor");
+ * <p/>
+ * Now let's look at how ints are handled. Given the following xml:
+ * <p/>
+ * <config car.engine.miles="100000"/>
+ * <p/>
+ * would result in:
+ * car.getEngine.setMiles(Integer.ParseInt("100000");
+ * <p/>
+ * That's pretty straight forward. But colors are expressed as ints too in Android
+ * but can be defined using hex values or even names of colors. When Configurator
+ * attempts to parse a parameter for a method that it knows takes an int as it's argument,
+ * Configurator will first attempt to parse the parameter as a color. Only after this
+ * attempt fails will Configurator resort to Integer.ParseInt. So:
+ * <p/>
+ * <config car.hood.paint.color="Red"/>
+ * <p/>
+ * would result in:
+ * car.getHood().getPaint().setColor(Color.parseColor("Red");
+ * <p/>
+ * Next lets talk about float. Floats can appear in XML a few different ways in Android,
+ * especially when it comes to defining dimensions:
+ * <p/>
+ * <config height="10dp" depth="2mm" width="5em"/>
+ * <p/>
+ * Configurator will correctly parse each of these into their corresponding real pixel value expressed as a float.
+ * <p/>
+ * One last thing to keep in mind when using Configurator:
+ * Values for Strings and ints can be assigned to localized values, allowing
+ * a cleaner solution for those developing apps to run on multiple form factors
+ * or in multiple languages:
+ * <p/>
+ * <config thingy.description="@string/thingyDescription"
+ * thingy.titlePaint.textSize=""/>
+ */
+@SuppressWarnings("WeakerAccess")
+public abstract class Configurator {
+
+ private static final String TAG = Configurator.class.getName();
+ protected static final String CFG_ELEMENT_NAME = "config";
+
+ protected static int parseResId(Context ctx, String prefix, String value) {
+ String[] split = value.split("/");
+ // is this a localized resource?
+ if (split.length > 1 && split[0].equalsIgnoreCase(prefix)) {
+ String pack = split[0].replace("@", "");
+ String name = split[1];
+ return ctx.getResources().getIdentifier(name, pack, ctx.getPackageName());
+ } else {
+ throw new IllegalArgumentException();
+ }
+ }
+
+ protected static int parseIntAttr(Context ctx, String value) {
+ try {
+ return ctx.getResources().getColor(parseResId(ctx, "@color", value));
+ } catch (IllegalArgumentException e1) {
+ try {
+ return Color.parseColor(value);
+ } catch (IllegalArgumentException e2) {
+ // wasn't a color so try parsing as a plain old int:
+ return Integer.parseInt(value);
+ }
+ }
+ }
+
+ /**
+ * Treats value as a float parameter. First value is tested to see whether
+ * it contains a resource identifier. Failing that, it is tested to see whether
+ * a dimension suffix (dp, em, mm etc.) exists. Failing that, it is evaluated as
+ * a plain old float.
+ * @param ctx
+ * @param value
+ * @return
+ */
+ protected static float parseFloatAttr(Context ctx, String value) {
+ try {
+ return ctx.getResources().getDimension(parseResId(ctx, "@dimen", value));
+ } catch (IllegalArgumentException e1) {
+ try {
+ return PixelUtils.stringToDimension(value);
+ } catch (Exception e2) {
+ return Float.parseFloat(value);
+ }
+ }
+ }
+
+ protected static String parseStringAttr(Context ctx, String value) {
+ try {
+ return ctx.getResources().getString(parseResId(ctx, "@string", value));
+ } catch (IllegalArgumentException e1) {
+ return value;
+ }
+ }
+
+
+ protected static Method getSetter(Class clazz, final String fieldId) throws NoSuchMethodException {
+ Method[] methods = clazz.getMethods();
+
+ String methodName = "set" + fieldId;
+ for (Method method : methods) {
+ if (method.getName().equalsIgnoreCase(methodName)) {
+ return method;
+ }
+ }
+ throw new NoSuchMethodException("No such public method (case insensitive): " +
+ methodName + " in " + clazz);
+ }
+
+ @SuppressWarnings("unchecked")
+ protected static Method getGetter(Class clazz, final String fieldId) throws NoSuchMethodException {
+ Log.d(TAG, "Attempting to find getter for " + fieldId + " in class " + clazz.getName());
+ String firstLetter = fieldId.substring(0, 1);
+ String methodName = "get" + firstLetter.toUpperCase() + fieldId.substring(1, fieldId.length());
+ return clazz.getMethod(methodName);
+ }
+
+ /**
+ * Returns the object containing the field specified by path.
+ * @param obj
+ * @param path Path through member hierarchy to the destination field.
+ * @return null if the object at path cannot be found.
+ * @throws java.lang.reflect.InvocationTargetException
+ *
+ * @throws IllegalAccessException
+ */
+ protected static Object getObjectContaining(Object obj, String path) throws
+ InvocationTargetException, IllegalAccessException, NoSuchMethodException {
+ if(obj == null) {
+ throw new NullPointerException("Attempt to call getObjectContaining(Object obj, String path) " +
+ "on a null Object instance. Path was: " + path);
+ }
+ Log.d(TAG, "Looking up object containing: " + path);
+ int separatorIndex = path.indexOf(".");
+
+ // not there yet, descend deeper:
+ if (separatorIndex > 0) {
+ String lhs = path.substring(0, separatorIndex);
+ String rhs = path.substring(separatorIndex + 1, path.length());
+
+ // use getter to retrieve the instance
+ Method m = getGetter(obj.getClass(), lhs);
+ if(m == null) {
+ throw new NullPointerException("No getter found for field: " + lhs + " within " + obj.getClass());
+ }
+ Log.d(TAG, "Invoking " + m.getName() + " on instance of " + obj.getClass().getName());
+ Object o = m.invoke(obj);
+ // delve into o
+ return getObjectContaining(o, rhs);
+ //} catch (NoSuchMethodException e) {
+ // TODO: log a warning
+ // return null;
+ //}
+ } else {
+ // found it!
+ return obj;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Object[] inflateParams(Context ctx, Class[] params, String[] vals) throws NoSuchMethodException,
+ InvocationTargetException, IllegalAccessException {
+ Object[] out = new Object[params.length];
+ int i = 0;
+ for (Class param : params) {
+ if (Enum.class.isAssignableFrom(param)) {
+ out[i] = param.getMethod("valueOf", String.class).invoke(null, vals[i].toUpperCase());
+ } else if (param.equals(Float.TYPE)) {
+ out[i] = parseFloatAttr(ctx, vals[i]);
+ } else if (param.equals(Integer.TYPE)) {
+ out[i] = parseIntAttr(ctx, vals[i]);
+ } else if (param.equals(Boolean.TYPE)) {
+ out[i] = Boolean.valueOf(vals[i]);
+ } else if (param.equals(String.class)) {
+ out[i] = parseStringAttr(ctx, vals[i]);
+ } else {
+ throw new IllegalArgumentException(
+ "Error inflating XML: Setter requires param of unsupported type: " + param);
+ }
+ i++;
+ }
+ return out;
+ }
+
+ /**
+ *
+ * @param ctx
+ * @param obj
+ * @param xmlFileId ID of the XML config file within /res/xml
+ */
+ public static void configure(Context ctx, Object obj, int xmlFileId) {
+ XmlResourceParser xrp = ctx.getResources().getXml(xmlFileId);
+ try {
+ HashMap<String, String> params = new HashMap<String, String>();
+ while (xrp.getEventType() != XmlResourceParser.END_DOCUMENT) {
+ xrp.next();
+ String name = xrp.getName();
+ if (xrp.getEventType() == XmlResourceParser.START_TAG) {
+ if (name.equalsIgnoreCase(CFG_ELEMENT_NAME))
+ for (int i = 0; i < xrp.getAttributeCount(); i++) {
+ params.put(xrp.getAttributeName(i), xrp.getAttributeValue(i));
+ }
+ break;
+ }
+ }
+ configure(ctx, obj, params);
+ } catch (XmlPullParserException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ xrp.close();
+ }
+ }
+
+ public static void configure(Context ctx, Object obj, HashMap<String, String> params) {
+ for (String key : params.keySet()) {
+ try {
+ configure(ctx, obj, key, params.get(key));
+ } catch (InvocationTargetException e) {
+ e.printStackTrace();
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ } catch (NoSuchMethodException e) {
+ Log.w(TAG, "Error inflating XML: Setter for field \"" + key + "\" does not exist. ");
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * Recursively descend into an object using key as the pathway and invoking the corresponding setter
+ * if one exists.
+ *
+ * @param key
+ * @param value
+ */
+ protected static void configure(Context ctx, Object obj, String key, String value)
+ throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
+ Object o = getObjectContaining(obj, key);
+ if (o != null) {
+ int idx = key.lastIndexOf(".");
+ String fieldId = idx > 0 ? key.substring(idx + 1, key.length()) : key;
+
+ Method m = getSetter(o.getClass(), fieldId);
+ Class[] paramTypes = m.getParameterTypes();
+ // TODO: add support for generic type params
+ if (paramTypes.length >= 1) {
+
+ // split on "|"
+ // TODO: add support for String args containing a |
+ String[] paramStrs = value.split("\\|");
+ if (paramStrs.length == paramTypes.length) {
+
+ Object[] oa = inflateParams(ctx, paramTypes, paramStrs);
+ Log.d(TAG, "Invoking " + m.getName() + " with arg(s) " + argArrToString(oa));
+ m.invoke(o, oa);
+ } else {
+ throw new IllegalArgumentException("Error inflating XML: Unexpected number of argments passed to \""
+ + m.getName() + "\". Expected: " + paramTypes.length + " Got: " + paramStrs.length);
+ }
+ } else {
+ // Obvious this is not a setter
+ throw new IllegalArgumentException("Error inflating XML: no setter method found for param \"" +
+ fieldId + "\".");
+ }
+ }
+ }
+
+ protected static String argArrToString(Object[] args) {
+ String out = "";
+ for(Object obj : args) {
+ out += (obj == null ? (out += "[null] ") :
+ ("[" + obj.getClass() + ": " + obj + "] "));
+ }
+ return out;
+ }
+}
+
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/util/DisplayDimensions.java b/AndroidPlot-Core/src/main/java/com/androidplot/util/DisplayDimensions.java
new file mode 100644
index 0000000..0ae5a7a
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/util/DisplayDimensions.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.util;
+
+import android.graphics.RectF;
+
+/**
+ * Convenience class for managing BoxModel data
+ */
+public class DisplayDimensions {
+
+ public final RectF canvasRect;
+ public final RectF marginatedRect;
+ public final RectF paddedRect;
+
+ // init to 1 to avoid potential divide by zero errors (yet to be observed in practice)
+ private static final RectF initRect;
+
+ static {
+ initRect = new RectF(1, 1, 1, 1);
+ }
+
+ public DisplayDimensions() {
+ this(initRect, initRect, initRect);
+ }
+ public DisplayDimensions(RectF canvasRect, RectF marginatedRect, RectF paddedRect) {
+ this.canvasRect = canvasRect;
+ this.marginatedRect = marginatedRect;
+ this.paddedRect = paddedRect;
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/util/FontUtils.java b/AndroidPlot-Core/src/main/java/com/androidplot/util/FontUtils.java
new file mode 100644
index 0000000..d4bbfd2
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/util/FontUtils.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.util;
+
+import android.graphics.Paint;
+import android.graphics.Rect;
+
+public class FontUtils {
+
+ /**
+ * Determines the height of the tallest character that can be drawn by paint.
+ * @param paint
+ * @return
+ */
+ public static float getFontHeight(Paint paint) {
+ Paint.FontMetrics metrics = paint.getFontMetrics();
+ return (-metrics.ascent) + metrics.descent;
+ //return (-metrics.top) + metrics.bottom;
+ }
+
+ /**
+ * Get the smallest rect that ecompasses the text to be drawn using paint.
+ * @param text
+ * @param paint
+ * @return
+ */
+ public static Rect getPackedStringDimensions(String text, Paint paint) {
+ Rect size = new Rect();
+ paint.getTextBounds(text, 0, text.length(), size);
+ return size;
+ }
+
+ /**
+ * Like getPackedStringDimensions except adds extra space to accommodate all
+ * characters that can be drawn regardless of whether or not they exist in text.
+ * This ensures a more uniform appearance for things that have dynamic text.
+ * @param text
+ * @param paint
+ * @return
+ */
+ public static Rect getStringDimensions(String text, Paint paint) {
+ Rect size = new Rect();
+ if(text == null || text.length() == 0) {
+ return null;
+ }
+ paint.getTextBounds(text, 0, text.length(), size);
+ size.bottom = size.top + (int) getFontHeight(paint);
+ return size;
+ }
+
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/util/ListOrganizer.java b/AndroidPlot-Core/src/main/java/com/androidplot/util/ListOrganizer.java
new file mode 100644
index 0000000..ea2cea6
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/util/ListOrganizer.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.util;
+
+import java.util.List;
+
+/**
+ * Utility class providing additional element organization operations.
+ * @param <ElementType>
+ */
+public class ListOrganizer<ElementType> implements ZIndexable<ElementType> {
+ private List<ElementType> list;
+
+ public ListOrganizer(List<ElementType> list) {
+ this.list = list;
+ }
+
+
+ public boolean moveToTop(ElementType element) {
+ if(list.remove(element)) {
+ list.add(list.size(), element);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public boolean moveAbove(ElementType objectToMove, ElementType reference) {
+ if(objectToMove == reference) {
+ throw new IllegalArgumentException("Illegal argument to moveAbove(A, B); A cannot be equal to B.");
+ }
+
+
+ list.remove(objectToMove);
+ int refIndex = list.indexOf(reference);
+ list.add(refIndex + 1, objectToMove);
+ return true;
+ //widgetOrder.remove(element);
+
+ }
+
+ public boolean moveBeneath(ElementType objectToMove, ElementType reference) {
+ if (objectToMove == reference) {
+ throw new IllegalArgumentException("Illegal argument to moveBeaneath(A, B); A cannot be equal to B.");
+ }
+
+ list.remove(objectToMove);
+ int refIndex = list.indexOf(reference);
+ list.add(refIndex, objectToMove);
+ return true;
+
+ }
+
+ public boolean moveToBottom(ElementType key) {
+
+ //int widgetIndex = widgetOrder.indexOf(key);
+ list.remove(key);
+ //list.add(list.size(), key);
+ list.add(0, key);
+ return true;
+ //widgetOrder.remove(key);
+ }
+
+ public boolean moveUp(ElementType key) {
+ int widgetIndex = list.indexOf(key);
+ if(widgetIndex == -1) {
+ // key not found:
+ return false;
+ }
+ if(widgetIndex >= list.size()-1) {
+ // already at the top:
+ return true;
+ }
+
+ ElementType widgetAbove = list.get(widgetIndex+1);
+ return moveAbove(key, widgetAbove);
+ }
+
+ public boolean moveDown(ElementType key) {
+ int widgetIndex = list.indexOf(key);
+ if(widgetIndex == -1) {
+ // key not found:
+ return false;
+ }
+ if(widgetIndex <= 0) {
+ // already at the bottom:
+ return true;
+ }
+
+ ElementType widgetBeneath = list.get(widgetIndex-1);
+ return moveBeneath(key, widgetBeneath);
+ }
+
+ @Override
+ public List<ElementType> elements() {
+ return list;
+ }
+
+ public void addToBottom(ElementType element) {
+ list.add(0, element);
+ }
+
+ public void addToTop(ElementType element) {
+ list.add(list.size(), element);
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/util/MultiSynch.java b/AndroidPlot-Core/src/main/java/com/androidplot/util/MultiSynch.java
new file mode 100644
index 0000000..2e2fa88
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/util/MultiSynch.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2013 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.util;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Utility class for obtaining synch lock across multiple objects.
+ */
+public abstract class MultiSynch {
+
+ /**
+ * Callback class for doing work from within a MultiSynch.
+ */
+ public interface Action {
+
+ /**
+ * Invoked by MultiSynch.run(...)
+ * @param params
+ */
+ public void run(Object[] params);
+ }
+
+
+ /**
+ *
+ * @param params
+ * @param synchSet Set of objects to be synchronized upon
+ * @param action Action to be invoked once full synchronization has been obtained.
+ */
+ public static void run(Object[] params, Set synchSet, Action action) {
+ run(params, synchSet.toArray(), action, 0);
+ }
+
+ /**
+ * @param params
+ * @param synchList List of objects to be synchronized upon
+ * @param action Action to be invoked once full synchronization has been obtained.
+ */
+ public static void run(Object[] params, List synchList, Action action) {
+ run(params, synchList.toArray(), action, 0);
+ }
+
+ /**
+ * @param params
+ * @param synchArr Array of objects to be synchronized upon
+ * @param action Action to be invoked once full synchronization has been obtained.
+ */
+ public static void run(Object[] params, Object[] synchArr, Action action) {
+ run(params, synchArr, action, 0);
+ }
+
+ /**
+ * Recursively synchs on each item in SynchList
+ * @param params
+ * @param synchArr
+ * @param action
+ * @param depth
+ */
+ private static void run(Object[] params, Object[] synchArr, Action action, int depth) {
+ if (synchArr != null) {
+ synchronized (synchArr[depth]) {
+ if (depth < synchArr.length - 1) {
+ run(params, synchArr, action, ++depth);
+ } else {
+ action.run(params);
+ }
+ }
+ }
+ action.run(params);
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/util/PaintUtils.java b/AndroidPlot-Core/src/main/java/com/androidplot/util/PaintUtils.java
new file mode 100644
index 0000000..b2cd713
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/util/PaintUtils.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.util;
+import android.graphics.Paint;
+
+/**
+ * Convenience methods that operate on Paint instances. These methods primarily deal with
+ * converting pixel values to/from dp values.
+ */
+public class PaintUtils {
+
+ /*public static Paint getPaint() {
+ Paint p = new Paint();
+ return p;
+ }*/
+
+ /**
+ * Sets a paint instance's line stroke size in dp
+ * @param paint
+ * @param lineSizeDp
+ */
+ public static void setLineSizeDp(Paint paint, float lineSizeDp){
+ paint.setStrokeWidth(PixelUtils.dpToPix(lineSizeDp));
+ }
+
+ /**
+ * Sets a paint instance's font size in dp
+ * @param paint
+ * @param fontSizeDp
+ */
+ public static void setFontSizeDp(Paint paint, float fontSizeDp){
+ paint.setTextSize(PixelUtils.dpToPix(fontSizeDp));
+ }
+
+ /**
+ * Set a paint instance's shadowing using dp values
+ * @param paint
+ * @param radiusDp
+ * @param dxDp
+ * @param dyDp
+ * @param color
+ */
+ public static void setShadowDp(Paint paint, float radiusDp, float dxDp, float dyDp, int color) {
+ float radius = PixelUtils.dpToPix(radiusDp);
+ float dx = PixelUtils.dpToPix(dxDp);
+ float dy = PixelUtils.dpToPix(dyDp);
+ paint.setShadowLayer(radius, dx, dy, color);
+ }
+
+
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/util/PixelUtils.java b/AndroidPlot-Core/src/main/java/com/androidplot/util/PixelUtils.java
new file mode 100644
index 0000000..ea6efed
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/util/PixelUtils.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.util;
+
+import android.content.Context;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class PixelUtils {
+ private static DisplayMetrics metrics;
+ private static final float FLOAT_INT_AVG_NUDGE = 0.5f;
+ //private static float SCALE = 1; // pix per dp
+ //private static int X_PIX = 1; // total display horizontal pix
+ //private static int Y_PIX = 1; // total display vertical pix
+
+ /**
+ * Recalculates scale value etc. Should be called when an application starts or
+ * whenever the screen is rotated.
+ */
+ public static void init(Context ctx) {
+ //DisplayMetrics dm = ctx.getResources().getDisplayMetrics();
+ //SCALE = dm.density;
+ //X_PIX = dm.widthPixels;
+ //Y_PIX = dm.heightPixels;
+ metrics = ctx.getResources().getDisplayMetrics();
+
+ }
+
+ public static PointF add(PointF lhs, PointF rhs) {
+ return new PointF(lhs.x + rhs.x, lhs.y + rhs.y);
+ }
+
+ public static PointF sub(PointF lhs, PointF rhs) {
+ return new PointF(lhs.x - rhs.x, lhs.y - rhs.y);
+ }
+
+ /**
+ * Converts a sub-pixel accurate RectF to a Rect
+ * using the closest matching full pixel vals. This is
+ * useful for clipping operations etc.
+ * @param rectIn The rect to be converted
+ * @return
+ */
+ /*public static Rect toRect(RectF rectIn) {
+ return new Rect(
+ (int) (rectIn.left + FLOAT_INT_AVG_NUDGE),
+ (int) (rectIn.top + FLOAT_INT_AVG_NUDGE),
+ (int) (rectIn.right + FLOAT_INT_AVG_NUDGE),
+ (int) (rectIn.bottom + FLOAT_INT_AVG_NUDGE));
+ }*/
+
+ /**
+ * Converts a sub-pixel accurate RectF to
+ * a single pixel accurate rect. This is helpful
+ * for clipping operations which dont do a good job with
+ * subpixel vals.
+ * @param in
+ * @return
+ */
+ public static RectF sink(RectF in) {
+ return nearestPixRect(in.left, in.top, in.right, in.bottom);
+ }
+
+ public static RectF nearestPixRect(float left, float top, float right, float bottom) {
+ return new RectF(
+ (int) (left + FLOAT_INT_AVG_NUDGE),
+ (int) (top + FLOAT_INT_AVG_NUDGE),
+ (int) (right + FLOAT_INT_AVG_NUDGE),
+ (int) (bottom + FLOAT_INT_AVG_NUDGE));
+ }
+
+ /**
+ * Converts a dp value to pixels.
+ * @param dp
+ * @return Pixel value of dp.
+ */
+ public static float dpToPix(float dp) {
+ //return SCALE * dp + FLOAT_INT_AVG_NUDGE;
+ //InternalDimension id = new InternalDimension(dp, TypedValue.COMPLEX_UNIT_DIP);
+ return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, metrics);
+
+ }
+
+ /**
+ * Converts an sp value to pixels.
+ * @param sp
+ * @return Pixel value of sp.
+ */
+ @SuppressWarnings("SameParameterValue")
+ public static float spToPix(float sp) {
+ return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, metrics);
+ }
+
+
+ /**
+ *
+ * @param fraction A float value between 0 and 1.
+ * @return Number of pixels fraction represents on the current device's display.
+ */
+ public static float fractionToPixH(float fraction) {
+ return metrics.heightPixels * fraction;
+
+ }
+
+ /**
+ *
+ * @param fraction A float value between 0 and 1.
+ * @return Number of pixels fraction represents on the current device's display.
+ */
+ public static float fractionToPixW(float fraction) {
+ return metrics.widthPixels * fraction;
+ }
+
+
+ /**
+ *
+ * CODE BELOW IS ADAPTED IN PART FROM MINDRIOT'S SAMPLE CODE HERE:
+ * http://stackoverflow.com/questions/8343971/how-to-parse-a-dimension-string-and-convert-it-to-a-dimension-value
+ */
+ // -- Initialize dimension string to constant lookup.
+ public static final Map<String, Integer> dimensionConstantLookup = initDimensionConstantLookup();
+
+ private static Map<String, Integer> initDimensionConstantLookup() {
+ Map<String, Integer> m = new HashMap<String, Integer>();
+ m.put("px", TypedValue.COMPLEX_UNIT_PX);
+ m.put("dip", TypedValue.COMPLEX_UNIT_DIP);
+ m.put("dp", TypedValue.COMPLEX_UNIT_DIP);
+ m.put("sp", TypedValue.COMPLEX_UNIT_SP);
+ m.put("pt", TypedValue.COMPLEX_UNIT_PT);
+ m.put("in", TypedValue.COMPLEX_UNIT_IN);
+ m.put("mm", TypedValue.COMPLEX_UNIT_MM);
+ return Collections.unmodifiableMap(m);
+ }
+
+ // -- Initialize pattern for dimension string.
+ private static final Pattern DIMENSION_PATTERN = Pattern.compile("^\\s*(\\d+(\\.\\d+)*)\\s*([a-zA-Z]+)\\s*$");
+
+ /*public static int stringToDimensionPixelSize(String dimension, DisplayMetrics metrics) {
+ // -- Mimics TypedValue.complexToDimensionPixelSize(int data, DisplayMetrics metrics).
+ InternalDimension internalDimension = stringToInternalDimension(dimension);
+ final float value = internalDimension.value;
+ final float f = TypedValue.applyDimension(internalDimension.unit, value, metrics);
+ final int res = (int) (f + 0.5f);
+ if (res != 0) return res;
+ if (value == 0) return 0;
+ if (value > 0) return 1;
+ return -1;
+ }*/
+
+ public static float stringToDimension(String dimension) {
+ // -- Mimics TypedValue.complexToDimension(int data, DisplayMetrics metrics).
+ InternalDimension internalDimension = stringToInternalDimension(dimension);
+ return TypedValue.applyDimension(internalDimension.unit, internalDimension.value, metrics);
+ }
+
+ private static InternalDimension stringToInternalDimension(String dimension) {
+ // -- Match target against pattern.
+ Matcher matcher = DIMENSION_PATTERN.matcher(dimension);
+ if (matcher.matches()) {
+ // -- Match found.
+ // -- Extract value.
+ float value = Float.valueOf(matcher.group(1));
+ // -- Extract dimension units.
+ String unit = matcher.group(3).toLowerCase();
+ // -- Get Android dimension constant.
+ Integer dimensionUnit = dimensionConstantLookup.get(unit);
+ if (dimensionUnit == null) {
+ // -- Invalid format.
+ throw new NumberFormatException();
+ } else {
+ // -- Return valid dimension.
+ return new InternalDimension(value, dimensionUnit);
+ }
+ } else {
+ // -- Invalid format.
+ throw new NumberFormatException();
+ }
+ }
+
+ private static class InternalDimension {
+ float value;
+ int unit;
+
+ public InternalDimension(float value, int unit) {
+ this.value = value;
+ this.unit = unit;
+ }
+ }
+
+
+}
+
+
+
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/util/PlotStatistics.java b/AndroidPlot-Core/src/main/java/com/androidplot/util/PlotStatistics.java
new file mode 100644
index 0000000..9703aee
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/util/PlotStatistics.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.util;
+
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import com.androidplot.Plot;
+import com.androidplot.PlotListener;
+
+/**
+ * !!! THIS CLASS IS STILL UNDER DEVELOPMENT AND MAY CONTAIN BUGS !!!
+ * Gathers performance statistics from a Plot. Instances of PlotStatistics
+ * should never be added to more than one Plot, otherwise the statiscs will
+ * be invalid.
+ */
+public class PlotStatistics implements PlotListener {
+ long minRenderTimeMs;
+ long maxRenderTimeMs;
+ long avgRenderTimeMs;
+ long fps;
+ long updateDelayMs;
+
+
+ long longestRenderMs = 0;
+ long shortestRenderMs = 0;
+ long lastStart = 0;
+ long lastLatency = 0;
+ long lastAnnotation;
+ long latencySamples = 0;
+ long latencySum = 0;
+ String annotationString = "";
+
+ private Paint paint;
+ {
+ paint = new Paint();
+ paint.setTextAlign(Paint.Align.CENTER);
+ paint.setColor(Color.WHITE);
+ paint.setTextSize(30);
+ resetCounters();
+ }
+
+
+ private boolean annotatePlotEnabled;
+
+
+
+ public PlotStatistics(long updateDelayMs, boolean annotatePlotEnabled) {
+ this.updateDelayMs = updateDelayMs;
+ this.annotatePlotEnabled = annotatePlotEnabled;
+ }
+
+ public void setAnnotatePlotEnabled(boolean enabled) {
+ this.annotatePlotEnabled = enabled;
+ }
+
+ private void resetCounters() {
+ longestRenderMs = 0;
+ shortestRenderMs = 999999999;
+ latencySamples = 0;
+ latencySum = 0;
+ }
+
+ private void annotatePlot(Plot source, Canvas canvas) {
+ long nowMs = System.currentTimeMillis();
+ // throttle the update frequency:
+ long msSinceUpdate = (nowMs - lastAnnotation);
+ if(msSinceUpdate >= updateDelayMs) {
+
+ float avgLatency = latencySamples > 0 ? latencySum/latencySamples : 0;
+ String overallFPS = String.format("%.2f", latencySamples > 0 ? (1000f/msSinceUpdate) * latencySamples : 0);
+ String potentialFPS = String.format("%.2f", latencySamples > 0 ? 1000f/avgLatency : 0);
+ annotationString = "FPS (potential): " + potentialFPS + " FPS (actual): " + overallFPS + " Latency (ms) Avg: " + lastLatency + " \nMin: " + shortestRenderMs +
+ " Max: " + longestRenderMs;
+ lastAnnotation = nowMs;
+ resetCounters();
+ }
+ RectF r = source.getDisplayDimensions().canvasRect;
+ if(annotatePlotEnabled) {
+ canvas.drawText(annotationString, r.centerX(), r.centerY(), paint);
+ }
+ }
+
+ @Override
+ public void onBeforeDraw(Plot source, Canvas canvas) {
+ lastStart = System.currentTimeMillis();
+ }
+
+ @Override
+ public void onAfterDraw(Plot source, Canvas canvas) {
+ lastLatency = System.currentTimeMillis() - lastStart;
+ if(lastLatency < shortestRenderMs) {
+ shortestRenderMs = lastLatency;
+ }
+
+ if(lastLatency > longestRenderMs) {
+ longestRenderMs = lastLatency;
+ }
+ latencySum += lastLatency;
+ latencySamples++;
+ annotatePlot(source, canvas);
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/util/Redrawer.java b/AndroidPlot-Core/src/main/java/com/androidplot/util/Redrawer.java
new file mode 100644
index 0000000..6602dbb
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/util/Redrawer.java
@@ -0,0 +1,112 @@
+package com.androidplot.util;
+
+import android.util.Log;
+import com.androidplot.Plot;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Utility class for invoking Plot.redraw() on a backgorund thread
+ * at a set frequency.
+ */
+public class Redrawer implements Runnable {
+
+ private static final String TAG = Redrawer.class.getName();
+
+ private List<Plot> plots;
+ private long sleepTime;
+ private boolean keepRunning;
+ private boolean keepAlive;
+
+ /**
+ *
+ * @param plots List of Plot instances to be redrawn
+ * @param maxRefreshRate Desired frequency at which to redraw plots.
+ * @param startImmediately If true, invokes run() immediately after construction.
+ */
+ public Redrawer(List<Plot> plots, float maxRefreshRate, boolean startImmediately) {
+ this.plots = plots;
+ setMaxRefreshRate(maxRefreshRate);
+ new Thread(this).start();
+ if(startImmediately) {
+ run();
+ }
+ }
+
+ public Redrawer(Plot plot, float maxRefreshRate, boolean startImmediately) {
+ this(Arrays.asList(new Plot[]{plot}), maxRefreshRate, startImmediately);
+ }
+
+ /**
+ * Temporarily stop redrawing the plot.
+ */
+ public synchronized void pause() {
+ keepRunning = false;
+ notify();
+ Log.d(TAG, "Redrawer paused.");
+ }
+
+ /**
+ * Start/resume redrawing the plot.
+ */
+ public synchronized void start() {
+ keepRunning = true;
+ notify();
+ Log.d(TAG, "Redrawer started.");
+ }
+
+ /**
+ * Internally, this causes
+ * the refresh thread to exit. Should always be called
+ * before exiting the application.
+ */
+ public synchronized void finish() {
+ keepRunning = false;
+ keepAlive = false;
+ notify();
+ }
+
+ @Override
+ public void run() {
+ keepAlive = true;
+ try {
+ while(keepAlive) {
+ if(keepRunning) {
+ // redraw plot(s) and sleep in an interruptible state for a
+ // max of sleepTime ms.
+ // TODO: record start and end timestamps and
+ // TODO: calculate sleepTime from that, in order to more accurately
+ // TODO: meet desired refresh rate.
+ for(Plot plot : plots) {
+ plot.redraw();
+ }
+ synchronized (this) {
+ wait(sleepTime);
+ }
+ } else {
+ // sleep until notified
+ synchronized (this) {
+ wait();
+ }
+ }
+ }
+ } catch(InterruptedException e) {
+
+ } finally {
+ Log.d(TAG, "Redrawer thread exited.");
+ }
+ }
+
+ /**
+ * Set the maximum refresh rate that Redrawer should use. Actual
+ * refresh rate could be slower.
+ * @param refreshRate Refresh rate in Hz.
+ */
+ public void setMaxRefreshRate(float refreshRate) {
+ sleepTime = (long)(1000 / refreshRate);
+ Log.d(TAG, "Set Redrawer refresh rate to " +
+ refreshRate + "( " + sleepTime + " ms)");
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/util/ValPixConverter.java b/AndroidPlot-Core/src/main/java/com/androidplot/util/ValPixConverter.java
new file mode 100644
index 0000000..d7a0d5a
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/util/ValPixConverter.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.util;
+
+import android.graphics.PointF;
+import android.graphics.RectF;
+
+/**
+ * Utility methods for converting pixel coordinates into real values and vice versa.
+ */
+public class ValPixConverter {
+ private static final int ZERO = 0;
+
+
+ public static float valToPix(double val, double min, double max, float lengthPix, boolean flip) {
+ if(lengthPix <= ZERO) {
+ throw new IllegalArgumentException("Length in pixels must be greater than 0.");
+ }
+ double range = range(min, max);
+ double scale = lengthPix / range;
+ double raw = val - min;
+ float pix = (float)(raw * scale);
+
+ if(flip) {
+ pix = (lengthPix - pix);
+ }
+ return pix;
+ }
+
+ public static double range(double min, double max) {
+ return (max-min);
+ }
+
+
+ public static double valPerPix(double min, double max, float lengthPix) {
+ double valRange = range(min, max);
+ return valRange/lengthPix;
+ }
+
+ /**
+ * Convert a value in pixels to the type passed into min/max
+ * @param pix
+ * @param min
+ * @param max
+ * @param lengthPix
+ * @param flip True if the axis should be reversed before calculated. This is the case
+ * with the y axis for screen coords.
+ * @return
+ */
+ public static double pixToVal(float pix, double min, double max, float lengthPix, boolean flip) {
+ if(pix < ZERO) {
+ throw new IllegalArgumentException("pixel values cannot be negative.");
+ }
+
+ if(lengthPix <= ZERO) {
+ throw new IllegalArgumentException("Length in pixels must be greater than 0.");
+ }
+ float pMult = pix;
+ if(flip) {
+ pMult = lengthPix - pix;
+ }
+ double range = range(min, max);
+ return ((range / lengthPix) * pMult) + min;
+ }
+
+ /**
+ * Converts a real value into a pixel value.
+ * @param x Real d (domain) component of the point to convert.
+ * @param y Real y (range) component of the point to convert.
+ * @param plotArea
+ * @param minX Minimum visible real value on the d (domain) axis.
+ * @param maxX Maximum visible real value on the y (domain) axis.
+ * @param minY Minimum visible real value on the y (range) axis.
+ * @param maxY Maximum visible real value on the y (range axis.
+ * @return
+ */
+ public static PointF valToPix(Number x, Number y, RectF plotArea, Number minX, Number maxX, Number minY, Number maxY) {
+ float pixX = ValPixConverter.valToPix(x.doubleValue(), minX.doubleValue(), maxX.doubleValue(), plotArea.width(), false) + (plotArea.left);
+ float pixY = ValPixConverter.valToPix(y.doubleValue(), minY.doubleValue(), maxY.doubleValue(), plotArea.height(), true) + plotArea.top;
+ return new PointF(pixX, pixY);
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/util/ZHash.java b/AndroidPlot-Core/src/main/java/com/androidplot/util/ZHash.java
new file mode 100644
index 0000000..56d4665
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/util/ZHash.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.util;
+
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Concrete implementation of ZIndexable. Provides fast element retrieval via hash key. Also provides
+ * mutable ordering (z indexing) of elements.
+ */
+public class ZHash<KeyType, ValueType> implements ZIndexable<KeyType> {
+
+ private HashMap<KeyType, ValueType> hash;
+ //private LinkedList<KeyType> zlist;
+ //private ListOrganizer<KeyType> listOrganizer;
+ private ZLinkedList<KeyType> zlist;
+
+ {
+ hash = new HashMap<KeyType, ValueType>();
+ zlist = new ZLinkedList<KeyType>();
+ //listOrganizer = new ListOrganizer<KeyType>(zlist);
+ }
+
+ public int size() {
+ return zlist.size();
+ }
+
+
+ public ValueType get(KeyType key) {
+ return hash.get(key);
+ }
+
+ public List<KeyType> getKeysAsList() {
+ return zlist;
+ }
+
+ /**
+ * If key already exists within the structure, it's value is replaced with the new value and
+ * it's existing order is maintained.
+ * @param key
+ * @param value
+ */
+ public synchronized void addToTop(KeyType key, ValueType value) {
+ if(hash.containsKey(key)) {
+ hash.put(key, value);
+ //throw new IllegalArgumentException("Key already exists in series structure...duplicates not permitted.");
+ } else {
+ hash.put(key, value);
+ zlist.addToTop(key);
+ //zlist.addToTop(key);
+ }
+ }
+
+ /**
+ * If key already exists within the structure, it's value is replaced with the new value and
+ * it's existing order is maintained.
+ * @param key
+ * @param value
+ */
+ public synchronized void addToBottom(KeyType key, ValueType value) {
+ if(hash.containsKey(key)) {
+ hash.put(key, value);
+ //throw new IllegalArgumentException("Key already exists in series structure...duplicates not permitted.");
+ } else {
+ hash.put(key, value);
+ zlist.addToBottom(key);
+ //zlist.addToBottom(key);
+ }
+ }
+
+ public synchronized boolean moveToTop(KeyType element) {
+ if(!hash.containsKey(element)) {
+ return false;
+ } else {
+ return zlist.moveToTop(element);
+ }
+ }
+
+ public synchronized boolean moveAbove(KeyType objectToMove, KeyType reference) {
+ if(objectToMove == reference) {
+ throw new IllegalArgumentException("Illegal argument to moveAbove(A, B); A cannot be equal to B.");
+ }
+ if(!hash.containsKey(reference) || !hash.containsKey(objectToMove)) {
+ return false;
+ } else {
+ return zlist.moveAbove(objectToMove, reference);
+ }
+ }
+
+ public synchronized boolean moveBeneath(KeyType objectToMove, KeyType reference) {
+ if(objectToMove == reference) {
+ throw new IllegalArgumentException("Illegal argument to moveBeaneath(A, B); A cannot be equal to B.");
+ }
+ if(!hash.containsKey(reference) || !hash.containsKey(objectToMove)) {
+ return false;
+ } else {
+ return zlist.moveBeneath(objectToMove, reference);
+ }
+ }
+
+ public synchronized boolean moveToBottom(KeyType key) {
+ if(!hash.containsKey(key)) {
+ return false;
+ } else {
+ return zlist.moveToBottom(key);
+ }
+ }
+
+ public synchronized boolean moveUp(KeyType key) {
+ if (!hash.containsKey(key)) {
+ return false;
+ } else {
+ return zlist.moveUp(key);
+ }
+ }
+
+ public synchronized boolean moveDown(KeyType key) {
+ if (!hash.containsKey(key)) {
+ return false;
+ } else {
+ return zlist.moveDown(key);
+ }
+ }
+
+ @Override
+ public List<KeyType> elements() {
+ return zlist;
+ }
+
+ /**
+ *
+ * @return Ordered list of keys.
+ */
+ public List<KeyType> keys() {
+ return elements();
+ }
+
+
+ public synchronized boolean remove(KeyType key) {
+ if(hash.containsKey(key)) {
+ hash.remove(key);
+ zlist.remove(key);
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/util/ZIndexable.java b/AndroidPlot-Core/src/main/java/com/androidplot/util/ZIndexable.java
new file mode 100644
index 0000000..44e9f88
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/util/ZIndexable.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.util;
+
+import java.util.List;
+
+/**
+ * Encapsulates the concept of z-indexable objects; Each object is stored above or below each other object and may
+ * be moved up and down in the queue relative to other elements in the hash or absolutely to the front or back of the queue.
+ *
+ * Note that the method names correspond to the order of items drawn directly on top of one another using an iterator;
+ * the first element drawn (lowest z-index) is effectively the "bottom" element.
+ * @param <ElementType>
+ */
+public interface ZIndexable<ElementType> {
+
+ /**
+ * Move above all other elements
+ * @param element
+ * @return
+ */
+ public boolean moveToTop(ElementType element);
+
+
+ /**
+ * Move above the specified element
+ * @param objectToMove
+ * @param reference
+ * @return
+ */
+ public boolean moveAbove(ElementType objectToMove, ElementType reference);
+
+
+ /**
+ * Move beneath the specified element
+ *
+ * @param objectToMove
+ * @param reference
+ * @return
+ */
+ public boolean moveBeneath(ElementType objectToMove, ElementType reference);
+
+ /**
+ * Move beneath all other elements
+ * @param key
+ * @return
+ */
+ public boolean moveToBottom(ElementType key);
+
+
+ /**
+ * Move up by one element
+ * @param key
+ * @return
+ */
+ public boolean moveUp(ElementType key);
+
+ /**
+ * Move down by one element
+ * @param key
+ * @return
+ */
+ public boolean moveDown(ElementType key);
+
+ public List<ElementType> elements();
+
+
+ /**
+ * Add beneath all other elements
+ * @param element
+ */
+ //public void addToBottom(ElementType element);
+
+ /**
+ * Add above all other elements
+ * @param element
+ */
+ //public void addToTop(ElementType element);
+}
\ No newline at end of file
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/util/ZLinkedList.java b/AndroidPlot-Core/src/main/java/com/androidplot/util/ZLinkedList.java
new file mode 100644
index 0000000..8924a25
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/util/ZLinkedList.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.util;
+
+import java.util.LinkedList;
+import java.util.List;
+
+public class ZLinkedList<Type> extends LinkedList<Type> implements ZIndexable<Type> {
+
+ //private LinkedList<Type> list;
+ private ListOrganizer<Type> organizer;
+
+ {
+ //list = new LinkedList<Type>();
+ organizer = new ListOrganizer<Type>(this);
+ }
+
+
+ @Override
+ public boolean moveToTop(Type element) {
+ return organizer.moveToTop(element);
+ }
+
+ @Override
+ public boolean moveAbove(Type objectToMove, Type reference) {
+ return organizer.moveAbove(objectToMove, reference);
+ }
+
+ @Override
+ public boolean moveBeneath(Type objectToMove, Type reference) {
+ return organizer.moveBeneath(objectToMove, reference);
+ }
+
+ @Override
+ public boolean moveToBottom(Type key) {
+ return organizer.moveToBottom(key);
+ }
+
+ @Override
+ public boolean moveUp(Type key) {
+ return organizer.moveUp(key);
+ }
+
+ @Override
+ public boolean moveDown(Type key) {
+ return organizer.moveDown(key);
+ }
+
+ @Override
+ public List<Type> elements() {
+ return organizer.elements();
+ }
+
+ public void addToBottom(Type element) {
+ organizer.addToBottom(element);
+ }
+
+ public void addToTop(Type element) {
+ organizer.addToTop(element);
+ }
+
+
+
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/AxisValueLabelFormatter.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/AxisValueLabelFormatter.java
new file mode 100644
index 0000000..d61d74b
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/AxisValueLabelFormatter.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.xy;
+
+public class AxisValueLabelFormatter {
+ //private Paint textPaint;
+ private int color;
+
+ public AxisValueLabelFormatter(int color) {
+ this.color = color;
+ }
+
+ public int getColor() {
+ return color;
+ }
+
+ public void setColor(int color) {
+ this.color = color;
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/BarFormatter.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/BarFormatter.java
new file mode 100644
index 0000000..fff7720
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/BarFormatter.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.xy;
+import android.graphics.Paint;
+import com.androidplot.ui.SeriesRenderer;
+
+public class BarFormatter extends LineAndPointFormatter {
+
+ public Paint getFillPaint() {
+ return fillPaint;
+ }
+
+ public void setFillPaint(Paint fillPaint) {
+ this.fillPaint = fillPaint;
+ }
+
+ public Paint getBorderPaint() {
+ return borderPaint;
+ }
+
+ public void setBorderPaint(Paint borderPaint) {
+ this.borderPaint = borderPaint;
+ }
+
+ private Paint fillPaint;
+ private Paint borderPaint;
+
+ {
+ fillPaint = new Paint();
+ //fillPaint.setColor(Color.RED);
+ fillPaint.setStyle(Paint.Style.FILL);
+ fillPaint.setAlpha(100);
+ borderPaint = new Paint();
+ borderPaint.setStyle(Paint.Style.STROKE);
+ borderPaint.setAlpha(100);
+ }
+
+ /**
+ * Should only be used in conjunction with calls to configure()...
+ */
+ public BarFormatter() {
+ }
+
+ public BarFormatter(int fillColor, int borderColor) {
+ fillPaint.setColor(fillColor);
+ borderPaint.setColor(borderColor);
+ }
+
+ @Override
+ public Class<? extends SeriesRenderer> getRendererClass() {
+ return BarRenderer.class;
+ }
+
+ @Override
+ public SeriesRenderer getRendererInstance(XYPlot plot) {
+ return new BarRenderer(plot);
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/BarRenderer.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/BarRenderer.java
new file mode 100644
index 0000000..22fc38e
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/BarRenderer.java
@@ -0,0 +1,369 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.xy;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.TreeMap;
+
+import android.graphics.Canvas;
+import android.graphics.RectF;
+
+import com.androidplot.exception.PlotRenderException;
+import com.androidplot.util.ValPixConverter;
+
+/**
+ * Renders a point as a Bar
+ */
+public class BarRenderer<T extends BarFormatter> extends XYSeriesRenderer<T> {
+
+ private BarRenderStyle renderStyle = BarRenderStyle.OVERLAID; // default Render Style
+ private BarWidthStyle widthStyle = BarWidthStyle.FIXED_WIDTH; // default Width Style
+ private float barWidth = 5;
+ private float barGap = 1;
+
+ public enum BarRenderStyle {
+ OVERLAID, // bars are overlaid in descending y-val order (largest val in back)
+ STACKED, // bars are drawn stacked vertically on top of each other
+ SIDE_BY_SIDE // bars are drawn horizontally next to each-other
+ }
+
+ public enum BarWidthStyle {
+ FIXED_WIDTH, // bar width is always barWidth
+ VARIABLE_WIDTH // bar width is calculated so that there is only barGap between each bar
+ }
+
+ public BarRenderer(XYPlot plot) {
+ super(plot);
+ }
+
+ /**
+ * Sets the width of the bars when using the FIXED_WIDTH render style
+ * @param barWidth
+ */
+ public void setBarWidth(float barWidth) {
+ this.barWidth = barWidth;
+ }
+
+ /**
+ * Sets the size of the gap between the bar (or bar groups) when using the VARIABLE_WIDTH render style
+ * @param barGap
+ */
+ public void setBarGap(float barGap) {
+ this.barGap = barGap;
+ }
+
+ public void setBarRenderStyle(BarRenderStyle renderStyle) {
+ this.renderStyle = renderStyle;
+ }
+
+ public void setBarWidthStyle(BarWidthStyle widthStyle) {
+ this.widthStyle = widthStyle;
+ }
+
+ public void setBarWidthStyle(BarWidthStyle style, float value) {
+ setBarWidthStyle(style);
+ switch (style) {
+ case FIXED_WIDTH:
+ setBarWidth(value);
+ break;
+ case VARIABLE_WIDTH:
+ setBarGap(value);
+ break;
+ default:
+ break;
+ }
+ }
+
+ @Override
+ public void doDrawLegendIcon(Canvas canvas, RectF rect, BarFormatter formatter) {
+ canvas.drawRect(rect, formatter.getFillPaint());
+ canvas.drawRect(rect, formatter.getBorderPaint());
+ }
+
+ /**
+ * Retrieves the BarFormatter instance that corresponds with the series passed in.
+ * Can be overridden to return other BarFormatters as a result of touch events etc.
+ * @param index index of the point being rendered.
+ * @param series XYSeries to which the point being rendered belongs.
+ * @return
+ */
+ @SuppressWarnings("UnusedParameters")
+ protected T getFormatter(int index, XYSeries series) {
+ return getFormatter(series);
+ }
+
+ public void onRender(Canvas canvas, RectF plotArea) throws PlotRenderException {
+
+ List<XYSeries> sl = getPlot().getSeriesListForRenderer(this.getClass());
+
+ TreeMap<Number, BarGroup> axisMap = new TreeMap<Number, BarGroup>();
+
+ // dont try to render anything if there's nothing to render.
+ if(sl == null) return;
+
+ /*
+ * Build the axisMap (yVal,BarGroup)... a TreeMap of BarGroups
+ * BarGroups represent a point on the X axis where a single or group of bars need to be drawn.
+ */
+
+ // For each Series
+ for(XYSeries series : sl) {
+ BarGroup barGroup;
+
+ // For each value in the series
+ for(int i = 0; i < series.size(); i++) {
+
+ if (series.getX(i) != null) {
+
+ // get a new bar object
+ Bar b = new Bar(series,i,plotArea);
+
+ // Find or create the barGroup
+ if (axisMap.containsKey(b.intX)) {
+ barGroup = axisMap.get(b.intX);
+ } else {
+ barGroup = new BarGroup(b.intX,plotArea);
+ axisMap.put(b.intX, barGroup);
+ }
+ barGroup.addBar(b);
+ }
+
+ }
+ }
+
+ // Loop through the axisMap linking up prev pointers
+ BarGroup prev, current;
+ prev = null;
+ for(Entry<Number, BarGroup> mapEntry : axisMap.entrySet()) {
+ current = mapEntry.getValue();
+ current.prev = prev;
+ prev = current;
+ }
+
+
+ // The default gap between each bar section
+ int gap = (int) barGap;
+
+ // Determine roughly how wide (rough_width) this bar should be. This is then used as a default width
+ // when there are gaps in the data or for the first/last bars.
+ float f_rough_width = ((plotArea.width() - ((axisMap.size() - 1) * gap)) / (axisMap.size() - 1));
+ int rough_width = (int) f_rough_width;
+ if (rough_width < 0) rough_width = 0;
+ if (gap > rough_width) {
+ gap = rough_width / 2;
+ }
+
+ //Log.d("PARAMTER","PLOT_WIDTH=" + plotArea.width());
+ //Log.d("PARAMTER","BAR_GROUPS=" + axisMap.size());
+ //Log.d("PARAMTER","ROUGH_WIDTH=" + rough_width);
+ //Log.d("PARAMTER","GAP=" + gap);
+
+ /*
+ * Calculate the dimensions of each barGroup and then draw each bar within it according to
+ * the Render Style and Width Style.
+ */
+
+ for(Number key : axisMap.keySet()) {
+
+ BarGroup barGroup = axisMap.get(key);
+
+ // Determine the exact left and right X for the Bar Group
+ switch (widthStyle) {
+ case FIXED_WIDTH:
+ // use intX and go halfwidth either side.
+ barGroup.leftX = barGroup.intX - (int) (barWidth / 2);
+ barGroup.width = (int) barWidth;
+ barGroup.rightX = barGroup.leftX + barGroup.width;
+ break;
+ case VARIABLE_WIDTH:
+ if (barGroup.prev != null) {
+ if (barGroup.intX - barGroup.prev.intX - gap - 1 > (int)(rough_width * 1.5)) {
+ // use intX and go halfwidth either side.
+ barGroup.leftX = barGroup.intX - (rough_width / 2);
+ barGroup.width = rough_width;
+ barGroup.rightX = barGroup.leftX + barGroup.width;
+ } else {
+ // base left off prev right to get the gap correct.
+ barGroup.leftX = barGroup.prev.rightX + gap + 1;
+ if (barGroup.leftX > barGroup.intX) barGroup.leftX = barGroup.intX;
+ // base right off intX + halfwidth.
+ barGroup.rightX = barGroup.intX + (rough_width / 2);
+ // calculate the width
+ barGroup.width = barGroup.rightX - barGroup.leftX;
+ }
+ } else {
+ // use intX and go halfwidth either side.
+ barGroup.leftX = barGroup.intX - (rough_width / 2);
+ barGroup.width = rough_width;
+ barGroup.rightX = barGroup.leftX + barGroup.width;
+ }
+ break;
+ default:
+ break;
+ }
+
+ //Log.d("BAR_GROUP", "rough_width=" + rough_width + " width=" + barGroup.width + " <" + barGroup.leftX + "|" + barGroup.intX + "|" + barGroup.rightX + ">");
+
+ /*
+ * Draw the bars within the barGroup area.
+ */
+ switch (renderStyle) {
+ case OVERLAID:
+ Collections.sort(barGroup.bars, new BarComparator());
+ for (Bar b : barGroup.bars) {
+ BarFormatter formatter = b.formatter();
+ PointLabelFormatter plf = formatter.getPointLabelFormatter();
+ PointLabeler pointLabeler = null;
+ if (formatter != null) {
+ pointLabeler = formatter.getPointLabeler();
+ }
+ //Log.d("BAR", b.series.getTitle() + " <" + b.barGroup.leftX + "|" + b.barGroup.intX + "|" + b.barGroup.rightX + "> " + b.intY);
+ if (b.barGroup.width >= 2) {
+ canvas.drawRect(b.barGroup.leftX, b.intY, b.barGroup.rightX, b.barGroup.plotArea.bottom, formatter.getFillPaint());
+ }
+ canvas.drawRect(b.barGroup.leftX, b.intY, b.barGroup.rightX, b.barGroup.plotArea.bottom, formatter.getBorderPaint());
+ if(plf != null && pointLabeler != null) {
+ canvas.drawText(pointLabeler.getLabel(b.series, b.seriesIndex), b.intX + plf.hOffset, b.intY + plf.vOffset, plf.getTextPaint());
+ }
+ }
+ break;
+ case SIDE_BY_SIDE:
+ int width = (int) barGroup.width / barGroup.bars.size();
+ int leftX = barGroup.leftX;
+ Collections.sort(barGroup.bars, new BarComparator());
+ for (Bar b : barGroup.bars) {
+ BarFormatter formatter = b.formatter();
+ PointLabelFormatter plf = formatter.getPointLabelFormatter();
+ PointLabeler pointLabeler = null;
+ if (formatter != null) {
+ pointLabeler = formatter.getPointLabeler();
+ }
+ //Log.d("BAR", "width=" + width + " <" + leftX + "|" + b.intX + "|" + (leftX + width) + "> " + b.intY);
+ if (b.barGroup.width >= 2) {
+ canvas.drawRect(leftX, b.intY, leftX + width, b.barGroup.plotArea.bottom, formatter.getFillPaint());
+ }
+ canvas.drawRect(leftX, b.intY, leftX + width, b.barGroup.plotArea.bottom, formatter.getBorderPaint());
+ if(plf != null && pointLabeler != null) {
+ canvas.drawText(pointLabeler.getLabel(b.series, b.seriesIndex), leftX + width/2 + plf.hOffset, b.intY + plf.vOffset, plf.getTextPaint());
+ }
+ leftX = leftX + width;
+ }
+ break;
+ case STACKED:
+ int bottom = (int) barGroup.plotArea.bottom;
+ Collections.sort(barGroup.bars, new BarComparator());
+ for (Bar b : barGroup.bars) {
+ BarFormatter formatter = b.formatter();
+ PointLabelFormatter plf = formatter.getPointLabelFormatter();
+ PointLabeler pointLabeler = null;
+ if (formatter != null) {
+ pointLabeler = formatter.getPointLabeler();
+ }
+ int height = (int) b.barGroup.plotArea.bottom - b.intY;
+ int top = bottom - height;
+ //Log.d("BAR", "top=" + top + " bottom=" + bottom + " height=" + height);
+ if (b.barGroup.width >= 2) {
+ canvas.drawRect(b.barGroup.leftX, top, b.barGroup.rightX, bottom, formatter.getFillPaint());
+ }
+ canvas.drawRect(b.barGroup.leftX, top, b.barGroup.rightX, bottom, formatter.getBorderPaint());
+ if(plf != null && pointLabeler != null) {
+ canvas.drawText(pointLabeler.getLabel(b.series, b.seriesIndex), b.intX + plf.hOffset, b.intY + plf.vOffset, plf.getTextPaint());
+ }
+ bottom = top;
+ }
+ break;
+ default:
+ break;
+ }
+
+ }
+
+ }
+
+ private class Bar {
+ public XYSeries series;
+ public int seriesIndex;
+ public double yVal, xVal;
+ public int intX, intY;
+ public float pixX, pixY;
+ public BarGroup barGroup;
+
+ public Bar(XYSeries series, int seriesIndex, RectF plotArea) {
+ this.series = series;
+ this.seriesIndex = seriesIndex;
+
+ this.xVal = series.getX(seriesIndex).doubleValue();
+ this.pixX = ValPixConverter.valToPix(xVal, getPlot().getCalculatedMinX().doubleValue(), getPlot().getCalculatedMaxX().doubleValue(), plotArea.width(), false) + (plotArea.left);
+ this.intX = (int) pixX;
+
+ if (series.getY(seriesIndex) != null) {
+ this.yVal = series.getY(seriesIndex).doubleValue();
+ this.pixY = ValPixConverter.valToPix(yVal, getPlot().getCalculatedMinY().doubleValue(), getPlot().getCalculatedMaxY().doubleValue(), plotArea.height(), true) + plotArea.top;
+ this.intY = (int) pixY;
+ } else {
+ this.yVal = 0;
+ this.pixY = plotArea.bottom;
+ this.intY = (int) pixY;
+ }
+ }
+ public BarFormatter formatter() {
+ return getFormatter(seriesIndex, series);
+ }
+ }
+
+ private class BarGroup {
+ public ArrayList<Bar> bars;
+ public int intX;
+ public int width, leftX, rightX;
+ public RectF plotArea;
+ public BarGroup prev;
+
+ public BarGroup(int intX, RectF plotArea) {
+ // Setup the TreeMap with the required comparator
+ this.bars = new ArrayList<Bar>(); // create a comparator that compares series title given the index.
+ this.intX = intX;
+ this.plotArea = plotArea;
+ }
+
+ public void addBar(Bar bar) {
+ bar.barGroup = this;
+ this.bars.add(bar);
+ }
+ }
+
+ @SuppressWarnings("WeakerAccess")
+ public class BarComparator implements Comparator<Bar>{
+ @Override
+ public int compare(Bar bar1, Bar bar2) {
+ switch (renderStyle) {
+ case OVERLAID:
+ return Integer.valueOf(bar1.intY).compareTo(bar2.intY);
+ case SIDE_BY_SIDE:
+ return bar1.series.getTitle().compareToIgnoreCase(bar2.series.getTitle());
+ case STACKED:
+ return bar1.series.getTitle().compareToIgnoreCase(bar2.series.getTitle());
+ default:
+ return 0;
+ }
+ }
+ }
+
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/BezierLineAndPointFormatter.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/BezierLineAndPointFormatter.java
new file mode 100644
index 0000000..1935a83
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/BezierLineAndPointFormatter.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.xy;
+
+import android.content.Context;
+import com.androidplot.ui.SeriesRenderer;
+
+/**
+ * This is an experimental class and should not currently be used in production apps.
+ */
+public class BezierLineAndPointFormatter extends LineAndPointFormatter {
+
+ /**
+ * Should only be used in conjunction with calls to configure()...
+ */
+ public BezierLineAndPointFormatter() {
+ }
+
+ public BezierLineAndPointFormatter(Integer lineColor, Integer vertexColor, Integer fillColor) {
+ super(lineColor, vertexColor, fillColor, null);
+ }
+
+ public BezierLineAndPointFormatter(Integer lineColor, Integer vertexColor, Integer fillColor, FillDirection fillDir) {
+ super(lineColor, vertexColor, fillColor, null, fillDir);
+ }
+
+ @Override
+ public Class<? extends SeriesRenderer> getRendererClass() {
+ return BezierLineAndPointRenderer.class;
+ }
+
+ @Override
+ public SeriesRenderer getRendererInstance(XYPlot plot) {
+ return new BezierLineAndPointRenderer(plot);
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/BezierLineAndPointRenderer.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/BezierLineAndPointRenderer.java
new file mode 100644
index 0000000..432abe5
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/BezierLineAndPointRenderer.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.xy;
+
+import android.graphics.Path;
+import android.graphics.PointF;
+
+/**
+ * WARNING: This is an unfinished class. Series drawn by this implementation may look nice
+ * but they are not yet accurate representations of the control points from which they are derived.
+ */
+public class BezierLineAndPointRenderer extends LineAndPointRenderer<BezierLineAndPointFormatter> {
+ public BezierLineAndPointRenderer(XYPlot plot) {
+ super(plot);
+ }
+
+ @Override
+ protected void appendToPath(Path path, PointF thisPoint, PointF lastPoint) {
+ //path.lineTo(thisPoint.x, thisPoint.y);
+
+ // bezier curve version:
+ PointF mid = new PointF();
+ mid.set((lastPoint.x + thisPoint.x) / 2, (lastPoint.y + thisPoint.y) / 2);
+ path.quadTo((lastPoint.x + mid.x) / 2, lastPoint.y, mid.x, mid.y);
+ //path.quadTo((mid.x + thisPoint.x) / 2, thisPoint.y, thisPoint.x, thisPoint.y);
+ path.quadTo((mid.x + thisPoint.x) / 2, lastPoint.y, thisPoint.x, thisPoint.y);
+
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/BoundaryMode.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/BoundaryMode.java
new file mode 100644
index 0000000..4d717f8
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/BoundaryMode.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.xy;
+
+public enum BoundaryMode {
+ FIXED,
+ AUTO,
+ GROW,
+ SHRINNK
+}
+
+
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/FillDirection.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/FillDirection.java
new file mode 100644
index 0000000..e1e1845
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/FillDirection.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.xy;
+
+/**
+ * Defines which edge is used to close a fill path for drawing lines.
+ *
+ * TOP - Use the top edge of the plot.
+ * BOTTOM - Use the bottom edge of the plot.
+ * LEFT - (Not implemented) Use the left edge of the plot.
+ * RIGHT - (Not implemented) Use the right edge of the plot.
+ * DOMAIN_ORIGIN - (Not implemented) Use the domain origin line.
+ * RANGE_ORIGIN - Use the range origin line.
+ */
+public enum FillDirection {
+ TOP,
+ BOTTOM,
+ LEFT,
+ RIGHT,
+ DOMAIN_ORIGIN,
+ RANGE_ORIGIN
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/LineAndPointFormatter.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/LineAndPointFormatter.java
new file mode 100644
index 0000000..9f9b377
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/LineAndPointFormatter.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.xy;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.Paint;
+import com.androidplot.ui.SeriesRenderer;
+import com.androidplot.util.Configurator;
+import com.androidplot.util.PixelUtils;
+
+/**
+ * Defines the visual aesthetics of an XYSeries; outline color and width, fill style,
+ * vertex size and color, shadowing, etc.
+ */
+public class LineAndPointFormatter extends XYSeriesFormatter<XYRegionFormatter> {
+
+ private static final float DEFAULT_LINE_STROKE_WIDTH_DP = 1.5f;
+ private static final float DEFAULT_VERTEX_STROKE_WIDTH_DP = 4.5f;
+
+ // default implementation prints point's yVal:
+ private PointLabeler pointLabeler = new PointLabeler() {
+ @Override
+ public String getLabel(XYSeries series, int index) {
+ return series.getY(index) + "";
+ }
+ };
+
+ public FillDirection getFillDirection() {
+ return fillDirection;
+ }
+
+ /**
+ * Sets which edge to use to close the line's path for filling purposes.
+ * See {@link FillDirection}.
+ * @param fillDirection
+ */
+ public void setFillDirection(FillDirection fillDirection) {
+ this.fillDirection = fillDirection;
+ }
+
+ protected FillDirection fillDirection = FillDirection.BOTTOM;
+ protected Paint linePaint;
+ protected Paint vertexPaint;
+ protected Paint fillPaint;
+ private PointLabelFormatter pointLabelFormatter;
+
+ {
+ initLinePaint(Color.BLACK);
+ }
+
+ /**
+ * Should only be used in conjunction with calls to configure()...
+ */
+ public LineAndPointFormatter() {
+ this(Color.RED, Color.GREEN, Color.BLUE, null);
+ }
+
+ public LineAndPointFormatter(Integer lineColor, Integer vertexColor, Integer fillColor, PointLabelFormatter plf) {
+ this(lineColor, vertexColor, fillColor, plf, FillDirection.BOTTOM);
+ }
+
+ public LineAndPointFormatter(Integer lineColor, Integer vertexColor, Integer fillColor, PointLabelFormatter plf, FillDirection fillDir) {
+ initLinePaint(lineColor);
+ initVertexPaint(vertexColor);
+ initFillPaint(fillColor);
+ setFillDirection(fillDir);
+ this.setPointLabelFormatter(plf);
+ }
+
+ @Override
+ public Class<? extends SeriesRenderer> getRendererClass() {
+ return LineAndPointRenderer.class;
+ }
+
+ @Override
+ public SeriesRenderer getRendererInstance(XYPlot plot) {
+ return new LineAndPointRenderer(plot);
+ }
+
+ protected void initLinePaint(Integer lineColor) {
+ if (lineColor == null) {
+ linePaint = null;
+ } else {
+ linePaint = new Paint();
+ linePaint.setAntiAlias(true);
+ linePaint.setStrokeWidth(PixelUtils.dpToPix(DEFAULT_LINE_STROKE_WIDTH_DP));
+ linePaint.setColor(lineColor);
+ linePaint.setStyle(Paint.Style.STROKE);
+ }
+ }
+
+ protected void initVertexPaint(Integer vertexColor) {
+ if (vertexColor == null) {
+ vertexPaint = null;
+ } else {
+ vertexPaint = new Paint();
+ vertexPaint.setAntiAlias(true);
+ vertexPaint.setStrokeWidth(PixelUtils.dpToPix(DEFAULT_VERTEX_STROKE_WIDTH_DP));
+ vertexPaint.setColor(vertexColor);
+ vertexPaint.setStrokeCap(Paint.Cap.ROUND);
+ }
+ }
+
+ protected void initFillPaint(Integer fillColor) {
+ if (fillColor == null) {
+ fillPaint = null;
+ } else {
+ fillPaint = new Paint();
+ fillPaint.setAntiAlias(true);
+ fillPaint.setColor(fillColor);
+ }
+ }
+
+ /**
+ * Enables the shadow layer on linePaint and shadowPaint by calling
+ * setShadowLayer() with preset values.
+ */
+ public void enableShadows() {
+ linePaint.setShadowLayer(1, 3, 3, Color.BLACK);
+ vertexPaint.setShadowLayer(1, 3, 3, Color.BLACK);
+ }
+
+ public void disableShadows() {
+ linePaint.setShadowLayer(0, 0, 0, Color.BLACK);
+ vertexPaint.setShadowLayer(0, 0, 0, Color.BLACK);
+ }
+
+ public Paint getLinePaint() {
+ return linePaint;
+ }
+
+ public void setLinePaint(Paint linePaint) {
+ this.linePaint = linePaint;
+ }
+
+ public Paint getVertexPaint() {
+ return vertexPaint;
+ }
+
+ public void setVertexPaint(Paint vertexPaint) {
+ this.vertexPaint = vertexPaint;
+ }
+
+ public Paint getFillPaint() {
+ return fillPaint;
+ }
+
+ public void setFillPaint(Paint fillPaint) {
+ this.fillPaint = fillPaint;
+ }
+
+ public PointLabelFormatter getPointLabelFormatter() {
+ return pointLabelFormatter;
+ }
+
+ public void setPointLabelFormatter(PointLabelFormatter pointLabelFormatter) {
+ this.pointLabelFormatter = pointLabelFormatter;
+ }
+
+ public PointLabeler getPointLabeler() {
+ return pointLabeler;
+ }
+
+ public void setPointLabeler(PointLabeler pointLabeler) {
+ this.pointLabeler = pointLabeler;
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/LineAndPointRenderer.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/LineAndPointRenderer.java
new file mode 100644
index 0000000..14595c4
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/LineAndPointRenderer.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.xy;
+
+import android.graphics.*;
+import android.util.Pair;
+import com.androidplot.exception.PlotRenderException;
+import com.androidplot.util.ValPixConverter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Renders a point as a line with the vertices marked. Requires 2 or more points to
+ * be rendered.
+ */
+public class LineAndPointRenderer<FormatterType extends LineAndPointFormatter> extends XYSeriesRenderer<FormatterType> {
+
+ public LineAndPointRenderer(XYPlot plot) {
+ super(plot);
+ }
+
+ @Override
+ public void onRender(Canvas canvas, RectF plotArea) throws PlotRenderException {
+
+
+ List<XYSeries> seriesList = getPlot().getSeriesListForRenderer(this.getClass());
+ if (seriesList != null) {
+ for (XYSeries series : seriesList) {
+ //synchronized(series) {
+ drawSeries(canvas, plotArea, series, getFormatter(series));
+ //}
+ }
+ }
+ }
+
+ @Override
+ public void doDrawLegendIcon(Canvas canvas, RectF rect, LineAndPointFormatter formatter) {
+ // horizontal icon:
+ float centerY = rect.centerY();
+ float centerX = rect.centerX();
+
+ if(formatter.getFillPaint() != null) {
+ canvas.drawRect(rect, formatter.getFillPaint());
+ }
+ if(formatter.getLinePaint() != null) {
+ canvas.drawLine(rect.left, rect.bottom, rect.right, rect.top, formatter.getLinePaint());
+ }
+
+ if(formatter.getVertexPaint() != null) {
+ canvas.drawPoint(centerX, centerY, formatter.getVertexPaint());
+ }
+ }
+
+ /**
+ * This method exists for StepRenderer to override without having to duplicate any
+ * additional code.
+ */
+ protected void appendToPath(Path path, PointF thisPoint, PointF lastPoint) {
+
+ path.lineTo(thisPoint.x, thisPoint.y);
+ }
+
+
+ protected void drawSeries(Canvas canvas, RectF plotArea, XYSeries series, LineAndPointFormatter formatter) {
+ PointF thisPoint;
+ PointF lastPoint = null;
+ PointF firstPoint = null;
+ Paint linePaint = formatter.getLinePaint();
+
+ //PointF lastDrawn = null;
+ Path path = null;
+ ArrayList<Pair<PointF, Integer>> points = new ArrayList<Pair<PointF, Integer>>(series.size());
+ for (int i = 0; i < series.size(); i++) {
+ Number y = series.getY(i);
+ Number x = series.getX(i);
+
+ if (y != null && x != null) {
+ thisPoint = ValPixConverter.valToPix(
+ x,
+ y,
+ plotArea,
+ getPlot().getCalculatedMinX(),
+ getPlot().getCalculatedMaxX(),
+ getPlot().getCalculatedMinY(),
+ getPlot().getCalculatedMaxY());
+ points.add(new Pair<PointF, Integer>(thisPoint, i));
+ //appendToPath(path, thisPoint, lastPoint);
+ } else {
+ thisPoint = null;
+ }
+
+ if(linePaint != null && thisPoint != null) {
+
+ // record the first point of the new Path
+ if(firstPoint == null) {
+ path = new Path();
+ firstPoint = thisPoint;
+ // create our first point at the bottom/x position so filling
+ // will look good
+ path.moveTo(firstPoint.x, firstPoint.y);
+ }
+
+ if(lastPoint != null) {
+ appendToPath(path, thisPoint, lastPoint);
+ }
+
+ lastPoint = thisPoint;
+ } else {
+ if(lastPoint != null) {
+ renderPath(canvas, plotArea, path, firstPoint, lastPoint, formatter);
+ }
+ firstPoint = null;
+ lastPoint = null;
+ }
+ }
+ if(linePaint != null && firstPoint != null) {
+ renderPath(canvas, plotArea, path, firstPoint, lastPoint, formatter);
+ }
+
+ // TODO: benchmark this against drawPoints(float[]);
+ Paint vertexPaint = formatter.getVertexPaint();
+ PointLabelFormatter plf = formatter.getPointLabelFormatter();
+ if (vertexPaint != null || plf != null) {
+ for (Pair<PointF, Integer> p : points) {
+ PointLabeler pointLabeler = formatter.getPointLabeler();
+
+ // if vertexPaint is available, draw vertex:
+ if(vertexPaint != null) {
+ canvas.drawPoint(p.first.x, p.first.y, formatter.getVertexPaint());
+ }
+
+ // if textPaint and pointLabeler are available, draw point's text label:
+ if(plf != null && pointLabeler != null) {
+ canvas.drawText(pointLabeler.getLabel(series, p.second), p.first.x + plf.hOffset, p.first.y + plf.vOffset, plf.getTextPaint());
+ }
+ }
+ }
+ }
+
+ protected void renderPath(Canvas canvas, RectF plotArea, Path path, PointF firstPoint, PointF lastPoint, LineAndPointFormatter formatter) {
+ Path outlinePath = new Path(path);
+
+ // determine how to close the path for filling purposes:
+ // We always need to calculate this path because it is also used for
+ // masking off for region highlighting.
+ switch (formatter.getFillDirection()) {
+ case BOTTOM:
+ path.lineTo(lastPoint.x, plotArea.bottom);
+ path.lineTo(firstPoint.x, plotArea.bottom);
+ path.close();
+ break;
+ case TOP:
+ path.lineTo(lastPoint.x, plotArea.top);
+ path.lineTo(firstPoint.x, plotArea.top);
+ path.close();
+ break;
+ case RANGE_ORIGIN:
+ float originPix = ValPixConverter.valToPix(
+ getPlot().getRangeOrigin().doubleValue(),
+ getPlot().getCalculatedMinY().doubleValue(),
+ getPlot().getCalculatedMaxY().doubleValue(),
+ plotArea.height(),
+ true);
+ originPix += plotArea.top;
+
+ path.lineTo(lastPoint.x, originPix);
+ path.lineTo(firstPoint.x, originPix);
+ path.close();
+ break;
+ default:
+ throw new UnsupportedOperationException("Fill direction not yet implemented: " + formatter.getFillDirection());
+ }
+
+ if (formatter.getFillPaint() != null) {
+ canvas.drawPath(path, formatter.getFillPaint());
+ }
+
+
+ //}
+
+ // draw any visible regions on top of the base region:
+ double minX = getPlot().getCalculatedMinX().doubleValue();
+ double maxX = getPlot().getCalculatedMaxX().doubleValue();
+ double minY = getPlot().getCalculatedMinY().doubleValue();
+ double maxY = getPlot().getCalculatedMaxY().doubleValue();
+
+ // draw each region:
+ for (RectRegion r : RectRegion.regionsWithin(formatter.getRegions().elements(), minX, maxX, minY, maxY)) {
+ XYRegionFormatter f = formatter.getRegionFormatter(r);
+ RectF regionRect = r.getRectF(plotArea, minX, maxX, minY, maxY);
+ if (regionRect != null) {
+ try {
+ canvas.save(Canvas.ALL_SAVE_FLAG);
+ canvas.clipPath(path);
+ canvas.drawRect(regionRect, f.getPaint());
+ } finally {
+ canvas.restore();
+ }
+ }
+ }
+
+ // finally we draw the outline path on top of everything else:
+ if(formatter.getLinePaint() != null) {
+ canvas.drawPath(outlinePath, formatter.getLinePaint());
+ }
+
+ path.rewind();
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/PointLabelFormatter.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/PointLabelFormatter.java
new file mode 100644
index 0000000..411a8fe
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/PointLabelFormatter.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.xy;
+
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.PointF;
+import com.androidplot.util.PixelUtils;
+
+public class PointLabelFormatter {
+ private static final float DEFAULT_H_OFFSET_DP = 0;
+ private static final float DEFAULT_V_OFFSET_DP = -4;
+ private static final float DEFAULT_TEXT_SIZE_SP = 12;
+ private Paint textPaint;
+ public float hOffset;
+ public float vOffset;
+
+ public PointLabelFormatter() {
+ this(Color.WHITE);
+ }
+
+ public PointLabelFormatter(int textColor) {
+ this(textColor, PixelUtils.dpToPix(DEFAULT_H_OFFSET_DP),
+ PixelUtils.dpToPix(DEFAULT_V_OFFSET_DP));
+ }
+
+ /**
+ *
+ * @param textColor
+ * @param hOffset Horizontal offset of text in pixels.
+ * @param vOffset Vertical offset of text in pixels. Offset is in screen coordinates;
+ * positive values shift the text further down the screen.
+ */
+ public PointLabelFormatter(int textColor, float hOffset, float vOffset) {
+ initTextPaint(textColor);
+ this.hOffset = hOffset;
+ this.vOffset = vOffset;
+ }
+
+ public Paint getTextPaint() {
+ return textPaint;
+ }
+
+ public void setTextPaint(Paint textPaint) {
+ this.textPaint = textPaint;
+ }
+
+ protected void initTextPaint(Integer textColor) {
+ if (textColor == null) {
+ setTextPaint(null);
+ } else {
+ setTextPaint(new Paint());
+ getTextPaint().setAntiAlias(true);
+ getTextPaint().setColor(textColor);
+ getTextPaint().setTextAlign(Paint.Align.CENTER);
+ getTextPaint().setTextSize(PixelUtils.spToPix(DEFAULT_TEXT_SIZE_SP));
+ //textPaint.setStyle(Paint.Style.STROKE);
+ }
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/PointLabeler.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/PointLabeler.java
new file mode 100644
index 0000000..8814ba7
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/PointLabeler.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.xy;
+
+public interface PointLabeler {
+
+ public String getLabel(XYSeries series, int index);
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/RectRegion.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/RectRegion.java
new file mode 100644
index 0000000..bee4a4f
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/RectRegion.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.xy;
+
+import android.graphics.PointF;
+import android.graphics.RectF;
+import com.androidplot.LineRegion;
+import com.androidplot.util.ValPixConverter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * RectRegion is just a rectangle with additional methods for determining
+ * intersections with other RectRegion instances.
+ */
+public class RectRegion {
+
+ LineRegion xLineRegion;
+ LineRegion yLineRegion;
+ private String label;
+
+ /**
+ *
+ * @param minX
+ * @param maxX
+ * @param minY
+ * @param maxY
+ */
+ public RectRegion(Number minX, Number maxX, Number minY, Number maxY, String label) {
+ xLineRegion = new LineRegion(minX, maxX);
+ yLineRegion = new LineRegion(minY, maxY);
+ this.setLabel(label);
+ }
+
+ @SuppressWarnings("SameParameterValue")
+ public RectRegion(Number minX, Number maxX, Number minY, Number maxY) {
+ this(minX, maxX, minY, maxY, null);
+ }
+
+ public boolean containsPoint(PointF point) {
+ throw new UnsupportedOperationException("Not yet implemented.");
+ }
+
+ public boolean containsValue(Number x, Number y) {
+ throw new UnsupportedOperationException("Not yet implemented.");
+ }
+
+ public boolean containsDomainValue(Number value) {
+ //return RectRegion.isBetween(value, minX, maxX);
+ return xLineRegion.contains(value);
+ }
+
+ public boolean containsRangeValue(Number value) {
+ //return RectRegion.isBetween(value, minY, maxY);
+ return yLineRegion.contains(value);
+ }
+
+ public boolean intersects(RectRegion region) {
+ return intersects(region.getMinX(), region.getMaxX(), region.getMinY(), region.getMaxY());
+ }
+
+
+ /**
+ * Tests whether this region intersects the region defined by params. Use
+ * null to represent infinity. Negative and positive infinity is implied by
+ * the boundary edge, ie. a maxX of null equals positive infinity while a
+ * minX of null equals negative infinity.
+ * @param minX
+ * @param maxX
+ * @param minY
+ * @param maxY
+ * @return
+ */
+ public boolean intersects(Number minX, Number maxX, Number minY, Number maxY) {
+ return xLineRegion.intersects(minX, maxX) && yLineRegion.intersects(minY, maxY);
+ }
+
+ public boolean intersects(RectF region, Number visMinX, Number visMaxX, Number visMinY, Number visMaxY) {
+
+ RectF thisRegion = getRectF(region, visMinX.doubleValue(), visMaxX.doubleValue(),
+ visMinY.doubleValue(), visMaxY.doubleValue());
+ return RectF.intersects(thisRegion, region);
+ }
+
+ public RectF getRectF(RectF plotRect, Number visMinX, Number visMaxX, Number visMinY, Number visMaxY) {
+ PointF topLeftPoint = ValPixConverter.valToPix(
+ xLineRegion.getMinVal().doubleValue() != Double.NEGATIVE_INFINITY ? xLineRegion.getMinVal() : visMinX,
+ yLineRegion.getMaxVal().doubleValue() != Double.POSITIVE_INFINITY ? yLineRegion.getMaxVal() : visMaxY,
+ plotRect,
+ visMinX,
+ visMaxX,
+ visMinY,
+ visMaxY);
+ PointF bottomRightPoint = ValPixConverter.valToPix(
+ xLineRegion.getMaxVal().doubleValue() != Double.POSITIVE_INFINITY ? xLineRegion.getMaxVal() : visMaxX,
+ yLineRegion.getMinVal().doubleValue() != Double.NEGATIVE_INFINITY ? yLineRegion.getMinVal() : visMinY,
+ plotRect,
+ visMinX,
+ visMaxX,
+ visMinY,
+ visMaxY);
+ // TODO: figure out why the y-values are inverted
+ return new RectF(topLeftPoint.x, topLeftPoint.y, bottomRightPoint.x, bottomRightPoint.y);
+ }
+
+ /**
+ * Returns a list of XYRegions that either completely or partially intersect the area
+ * defined by params. A null value for any parameter represents infinity / no boundary.
+ * @param regions The list of regions to search through
+ * @param minX
+ * @param maxX
+ * @param minY
+ * @param maxY
+ * @return
+ */
+ public static List<RectRegion> regionsWithin(List<RectRegion> regions, Number minX, Number maxX, Number minY, Number maxY) {
+ ArrayList<RectRegion> intersectingRegions = new ArrayList<RectRegion>();
+ for(RectRegion r : regions) {
+ if(r.intersects(minX, maxX, minY, maxY)) {
+ intersectingRegions.add(r);
+ }
+ }
+ return intersectingRegions;
+ }
+
+
+ public Number getMinX() {
+ return xLineRegion.getMinVal();
+ }
+
+ public void setMinX(double minX) {
+ xLineRegion.setMinVal(minX);
+ }
+
+ public Number getMaxX() {
+ return xLineRegion.getMaxVal();
+ }
+
+ public void setMaxX(Number maxX) {
+ xLineRegion.setMaxVal(maxX);
+ }
+
+ public Number getMinY() {
+ return yLineRegion.getMinVal();
+ }
+
+ public void setMinY(Number minY) {
+ yLineRegion.setMinVal(minY);
+ }
+
+ public Number getMaxY() {
+ return yLineRegion.getMaxVal();
+ }
+
+ public void setMaxY(Number maxY) {
+ yLineRegion.setMaxVal(maxY);
+ }
+
+ public String getLabel() {
+ return label;
+ }
+
+ public void setLabel(String label) {
+ this.label = label;
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/SimpleXYSeries.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/SimpleXYSeries.java
new file mode 100644
index 0000000..0416103
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/SimpleXYSeries.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.xy;
+
+import android.graphics.Canvas;
+import android.util.Log;
+import android.util.Pair;
+import com.androidplot.Plot;
+import com.androidplot.PlotListener;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+
+/**
+ * A convenience class used to create instances of XYPlot generated from Lists of Numbers.
+ */
+public class SimpleXYSeries implements XYSeries, PlotListener {
+
+ private static final String TAG = SimpleXYSeries.class.getName();
+
+ @Override
+ public void onBeforeDraw(Plot source, Canvas canvas) {
+ lock.readLock().lock();
+ }
+
+ @Override
+ public void onAfterDraw(Plot source, Canvas canvas) {
+ lock.readLock().unlock();
+ }
+
+ public enum ArrayFormat {
+ Y_VALS_ONLY,
+ XY_VALS_INTERLEAVED
+ }
+
+ private volatile LinkedList<Number> xVals = new LinkedList<Number>();
+ private volatile LinkedList<Number> yVals = new LinkedList<Number>();
+ private volatile String title = null;
+ private ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
+
+
+ public SimpleXYSeries(String title) {
+ this.title = title;
+ }
+ /**
+ * Generates an XYSeries instance from the List of numbers passed in. This is a convenience class
+ * and should only be used for static data models; it is not suitable for representing dynamically
+ * changing data.
+ *
+ * @param model A List of Number elements comprising the data model.
+ * @param format Format of the model. A format of Y_VALS_ONLY means that the array only contains y-values.
+ * For this format x values are autogenerated using values of 0 through n-1 where n is the size of the model.
+ * @param title Title of the series
+ */
+ public SimpleXYSeries(List<? extends Number> model, ArrayFormat format, String title) {
+ this(title);
+ setModel(model, format);
+ }
+
+ public SimpleXYSeries(List<? extends Number> xVals, List<? extends Number> yVals, String title) {
+ this(title);
+ if(xVals == null || yVals == null) {
+ throw new IllegalArgumentException("Neither the xVals nor the yVals parameters may be null.");
+ }
+
+ if(xVals.size() != yVals.size()) {
+ throw new IllegalArgumentException("xVals and yVals List parameters must be of the same size.");
+ }
+
+ this.xVals.addAll(xVals);
+ this.yVals.addAll(yVals);
+ }
+
+ /**
+ * Use index value as xVal, instead of explicit, user provided xVals.
+ */
+ public void useImplicitXVals() {
+ lock.writeLock().lock();
+ try {
+ xVals = null;
+ } finally {
+ lock.writeLock().unlock();
+ }
+ }
+
+ /**
+ * Use the provided list of Numbers as yVals and their corresponding indexes as xVals.
+ * @param model A List of Number elements comprising the data model.
+ * @param format Format of the model. A format of Y_VALS_ONLY means that the array only contains y-values.
+ * For this format x values are autogenerated using values of 0 through n-1 where n is the size of the model.
+ */
+ public void setModel(List<? extends Number> model, ArrayFormat format) {
+
+ lock.writeLock().lock();
+ try {
+ // empty the current values:
+ //xVals.clear();
+ xVals = null;
+ yVals.clear();
+
+ // make sure the new model has data:
+ if (model == null || model.size() == 0) {
+ return;
+ }
+
+ switch (format) {
+
+ // array containing only y-vals. assume x = index:
+ case Y_VALS_ONLY:
+ for(Number n : model) {
+ yVals.add(n);
+ }
+ /*for (int i = 0; i < model.size(); i++) {
+ //xVals.add(i);
+ yVals.add(model.get(i));
+ }*/
+ break;
+
+ // xy interleaved array:
+ case XY_VALS_INTERLEAVED:
+ if (xVals == null) {
+ xVals = new LinkedList<Number>();
+ }
+ if (model.size() % 2 != 0) {
+ throw new IndexOutOfBoundsException("Cannot auto-generate series from odd-sized xy List.");
+ }
+ // always need an x and y array so init them now:
+ int sz = model.size() / 2;
+ for (int i = 0, j = 0; i < sz; i++, j += 2) {
+ xVals.add(model.get(j));
+ yVals.add(model.get(j + 1));
+ }
+ break;
+ default:
+ throw new IllegalArgumentException("Unexpected enum value: " + format);
+ }
+ } finally {
+ lock.writeLock().unlock();
+ }
+ }
+
+ /**
+ * Sets individual x value based on index
+ * @param value
+ * @param index
+ */
+ public void setX(Number value, int index) {
+ lock.writeLock().lock();
+ try {
+ xVals.set(index, value);
+ } finally {
+ lock.writeLock().unlock();
+ }
+ }
+
+ /**
+ * Sets individual y value based on index
+ * @param value
+ * @param index
+ */
+ public void setY(Number value, int index) {
+ lock.writeLock().lock();
+ try {
+ yVals.set(index, value);
+ } finally {
+ lock.writeLock().unlock();
+ }
+ }
+
+ /**
+ * Sets xy values based on index
+ * @param xVal
+ * @param yVal
+ * @param index
+ */
+ public void setXY(Number xVal, Number yVal, int index) {
+ lock.writeLock().lock();
+ try {
+ yVals.set(index, yVal);
+ xVals.set(index, xVal);
+ } finally {lock.writeLock().unlock();}
+ }
+
+ public void addFirst(Number x, Number y) {
+ lock.writeLock().lock();
+ try {
+ if (xVals != null) {
+ xVals.addFirst(x);
+ }
+ yVals.addFirst(y);
+ } finally {
+ lock.writeLock().unlock();
+ }
+ }
+
+ /**
+ *
+ * @return Pair<Number, Number> with first equal to x-val and second equal to y-val.
+ */
+ public Pair<Number, Number> removeFirst() {
+ lock.writeLock().lock();
+ try {
+ if (size() <= 0) {
+ throw new NoSuchElementException();
+ }
+ return new Pair<Number, Number>(xVals != null ? xVals.removeFirst() : 0, yVals.removeFirst());
+ } finally {
+ lock.writeLock().unlock();
+ }
+ }
+
+ public void addLast(Number x, Number y) {
+ lock.writeLock().lock();
+ try {
+ if (xVals != null) {
+ xVals.addLast(x);
+ }
+ yVals.addLast(y);
+ } finally {
+ lock.writeLock().unlock();
+ }
+ }
+
+ /**
+ *
+ * @return Pair<Number, Number> with first equal to x-val and second equal to y-val.
+ */
+ public Pair<Number, Number> removeLast() {
+ lock.writeLock().lock();
+ try {
+ if (size() <= 0) {
+ throw new NoSuchElementException();
+ }
+ return new Pair<Number, Number>(xVals != null ? xVals.removeLast() : yVals.size() - 1, yVals.removeLast());
+ } finally {
+ lock.writeLock().unlock();
+ }
+ }
+
+ @Override
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ lock.writeLock().lock();
+ try {
+ this.title = title;
+ } finally {lock.writeLock().unlock();}
+ }
+
+ @Override
+ public int size() {
+ return yVals != null ? yVals.size() : 0;
+ }
+
+ @Override
+ public Number getX(int index) {
+ return xVals != null ? xVals.get(index) : index;
+ }
+
+ @Override
+ public Number getY(int index) {
+ return yVals.get(index);
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/StepFormatter.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/StepFormatter.java
new file mode 100644
index 0000000..8c29277
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/StepFormatter.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.xy;
+
+import android.content.Context;
+import com.androidplot.ui.SeriesRenderer;
+import com.androidplot.util.Configurator;
+
+public class StepFormatter extends LineAndPointFormatter {
+
+ /**
+ * Should only be used in conjunction with calls to configure()...
+ */
+ public StepFormatter() {}
+
+ public StepFormatter(Integer lineColor, Integer fillColor) {
+ initLinePaint(lineColor);
+ initFillPaint(fillColor);
+ }
+
+ @Override
+ public Class<? extends SeriesRenderer> getRendererClass() {
+ return StepRenderer.class;
+ }
+
+ @Override
+ public SeriesRenderer getRendererInstance(XYPlot plot) {
+ return new StepRenderer(plot);
+ }
+
+}
\ No newline at end of file
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/StepRenderer.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/StepRenderer.java
new file mode 100644
index 0000000..17f01c3
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/StepRenderer.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.xy;
+
+import android.graphics.Path;
+import android.graphics.PointF;
+
+/**
+ * Renders a point as a line with the vertices marked. Requires 2 or more points to
+ * be rendered.
+ */
+
+public class StepRenderer extends LineAndPointRenderer<StepFormatter> {
+
+ public StepRenderer(XYPlot plot) {
+ super(plot);
+ }
+
+ @Override
+ protected void appendToPath(Path path, PointF thisPoint, PointF lastPoint) {
+ //path.lineTo(thisPoint.x, thisPoint.y);
+
+ path.lineTo(thisPoint.x, lastPoint.y);
+ path.lineTo(thisPoint.x, thisPoint.y);
+ //canvas.drawPoint(point.x, lastPoint.y, format.getVertexPaint());
+ // next the vertical:
+ //canvas.drawLine(point.x, lastPoint.y, point.x, point.y, format.getLinePaint());
+ }
+}
+/*
+public class StepRenderer extends XYSeriesRenderer<StepFormatter> {
+
+ private PointF lastPoint;
+
+ private boolean drawLinesEnabled = true;
+ private boolean drawPointsEnabled = true;
+
+ private XYAxisType stepAxis = XYAxisType.DOMAIN;
+
+
+
+ public StepRenderer(XYPlot plot) {
+ super(plot);
+ }
+
+ @Override
+ public void onRender(Canvas canvas, RectF plotArea) throws PlotRenderException {
+
+
+
+ for(XYSeries series : getPlot().getSeriesListForRenderer(this.getClass())) {
+
+ drawSeries(canvas, plotArea, series, getFormatter(series));
+ }
+ //foreach(this.)
+ //foreach()
+ }
+
+ @Override
+ public void doDrawLegendIcon(Canvas canvas, RectF rect, String text, StepFormatter formatter) {
+ // horizontal icon:
+ float centerY = rect.centerY();
+ float centerX = rect.centerX();
+ canvas.drawLine(rect.left, rect.top, rect.right, rect.bottom, formatter.getLinePaint());
+ canvas.drawPoint(centerX, centerY, formatter.getVertexPaint());
+ //canvas.drawRect(rect, formatter.getLinePaint());
+
+ }
+
+
+ private void drawSeries(Canvas canvas, RectF plotArea, XYSeries series, StepFormatter formatter) throws PlotRenderException {
+ beginSeries(canvas, plotArea, formatter);
+ //XYDataset series = bundle.getDataset();
+ //int seriesIndex = bundle.getSeriesIndex();
+ PointF thisPoint;
+ for (int i = 0; i < series.size(); i++) {
+ Number y = series.getY(i);
+ Number x = series.getD(i);
+
+ if (y != null && x != null) {
+
+ thisPoint = ValPixConverter.valToPix(
+ x,
+ y,
+ plotArea,
+ getPlot().getCalculatedMinX(),
+ getPlot().getCalculatedMaxX(),
+ getPlot().getCalculatedMinY(),
+ getPlot().getCalculatedMaxY());
+ //float pixX = ValPixConverter.valToPix(x.doubleValue(), getPlot().getCalculatedMinX().doubleValue(), getPlot().getCalculatedMaxX().doubleValue(), plotArea.width(), false) + (plotArea.left);
+ //float pixY = ValPixConverter.valToPix(y.doubleValue(), getPlot().getCalculatedMinY().doubleValue(), getPlot().getCalculatedMaxY().doubleValue(), plotArea.height(), true) + plotArea.top;
+
+ //thisPoint = new PointF(pixX, pixY);
+ } else {
+ thisPoint = null;
+ }
+ drawPoint(canvas, thisPoint, plotArea, formatter);
+ }
+ endSeries(canvas, plotArea, formatter);
+ }
+
+ private void beginSeries(Canvas canvas, RectF plotArea, StepFormatter format) throws PlotRenderException {
+ lastPoint = null;
+ }
+
+ private void drawPoint(Canvas canvas, PointF point, RectF plotArea, StepFormatter format) throws PlotRenderException {
+ if (lastPoint != null) {
+ if (point != null) {
+
+ switch(stepAxis) {
+ case DOMAIN:
+ // first draw the horizontal line:
+ canvas.drawLine(lastPoint.x, lastPoint.y, point.x, lastPoint.y, format.getLinePaint());
+ canvas.drawPoint(point.x, lastPoint.y, format.getVertexPaint());
+ // next the vertical:
+ canvas.drawLine(point.x, lastPoint.y, point.x, point.y, format.getLinePaint());
+ break;
+ case RANGE:
+ break;
+ }
+ //doDrawLine(canvas, lastPoint, point, plotArea, format);
+
+
+ }
+ drawLastPoint(canvas, plotArea, format);
+ }
+
+ lastPoint = point;
+ }
+
+ private void endSeries(Canvas canvas, RectF plotArea, StepFormatter format) throws PlotRenderException {
+ if(lastPoint != null) {
+ drawLastPoint(canvas, plotArea, format);
+ }
+ }
+
+ protected void drawLastPoint(Canvas canvas, RectF plotArea, StepFormatter format) throws PlotRenderException {
+ canvas.drawPoint(lastPoint.x, lastPoint.y, format.getVertexPaint());
+ }
+
+
+ public XYAxisType getStepAxis() {
+ return stepAxis;
+ }
+
+ public void setStepAxis(XYAxisType stepAxis) {
+ this.stepAxis = stepAxis;
+ }
+}
+*/
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/ValueMarker.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/ValueMarker.java
new file mode 100644
index 0000000..648cf64
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/ValueMarker.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.xy;
+
+import android.graphics.Color;
+import android.graphics.Paint;
+import com.androidplot.ui.PositionMetric;
+
+/**
+ * Encapsulates a single axis line marker drawn onto an XYPlot at a specified value.
+ * @param <PositionMetricType>
+ */
+public abstract class ValueMarker<PositionMetricType extends PositionMetric> {
+
+ public String getText() {
+ return text;
+ }
+
+ public void setText(String text) {
+ this.text = text;
+ }
+
+ public enum TextOrientation {
+ HORIZONTAL,
+ VERTICAL
+ }
+ private Number value;
+ private Paint linePaint;
+ private Paint textPaint;
+ //private Paint backgroundPaint;
+ private TextOrientation textOrientation;
+ private int textMargin = 2;
+ private PositionMetricType textPosition;
+ private String text;
+
+ {
+ linePaint = new Paint();
+ linePaint.setColor(Color.RED);
+ linePaint.setAntiAlias(true);
+ linePaint.setStyle(Paint.Style.STROKE);
+ textPaint = new Paint();
+ textPaint.setAntiAlias(true);
+ textPaint.setColor(Color.RED);
+ //backgroundPaint = new Paint();
+ //backgroundPaint.setColor(Color.argb(100, 100, 100, 100));
+ //backgroundPaint.setColor(Color.DKGRAY);
+
+ }
+
+ public ValueMarker(Number value, String text, PositionMetricType textPosition) {
+ this.value = value;
+ this.textPosition = textPosition;
+ this.text = text;
+ }
+
+ /**
+ *
+ * @param value
+ * @param text
+ * @param textPosition
+ * @param linePaint
+ * @param textPaint
+ */
+ public ValueMarker(Number value, String text, PositionMetricType textPosition, Paint linePaint, Paint textPaint) {
+ this(value, text, textPosition);
+
+ this.linePaint = linePaint;
+ this.textPaint = textPaint;
+ //this.backgroundPaint = backgroundPaint;
+ }
+
+ public ValueMarker(Number value, String text, PositionMetricType textPosition, int linePaint, int textPaint) {
+ this(value, text, textPosition);
+ this.linePaint.setColor(linePaint);
+ this.textPaint.setColor(textPaint);
+ }
+
+ public Number getValue() {
+ return value;
+ }
+
+ public void setValue(Number value) {
+ this.value = value;
+ }
+
+ public Paint getLinePaint() {
+ return linePaint;
+ }
+
+ public void setLinePaint(Paint linePaint) {
+ this.linePaint = linePaint;
+ }
+
+ public Paint getTextPaint() {
+ return textPaint;
+ }
+
+ public void setTextPaint(Paint textPaint) {
+ this.textPaint = textPaint;
+ }
+
+ /*public Paint getBackgroundPaint() {
+ return backgroundPaint;
+ }
+
+ public void setBackgroundPaint(Paint backgroundPaint) {
+ this.backgroundPaint = backgroundPaint;
+ }*/
+
+ public TextOrientation getTextOrientation() {
+ return textOrientation;
+ }
+
+ /**
+ * Currently not implemented. Sets the orientation of the text portion of this
+ * ValueMarker.
+ * @param textOrientation
+ */
+ public void setTextOrientation(TextOrientation textOrientation) {
+ this.textOrientation = textOrientation;
+ }
+
+ /**
+ * Currently not implemented.
+ * @return
+ */
+ public int getTextMargin() {
+ return textMargin;
+ }
+
+ public void setTextMargin(int textMargin) {
+ this.textMargin = textMargin;
+ }
+
+ public PositionMetricType getTextPosition() {
+ return textPosition;
+ }
+
+ public void setTextPosition(PositionMetricType textPosition) {
+ this.textPosition = textPosition;
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/XValueMarker.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XValueMarker.java
new file mode 100644
index 0000000..84ddea4
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XValueMarker.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.xy;
+
+import android.graphics.Paint;
+import com.androidplot.ui.YLayoutStyle;
+import com.androidplot.ui.YPositionMetric;
+
+public class XValueMarker extends ValueMarker<YPositionMetric> {
+
+ /**
+ *
+ * @param value
+ * @param text Set to null to use the plot's default formatter.
+ */
+ public XValueMarker(Number value, String text) {
+ super(value, text, new YPositionMetric(3, YLayoutStyle.ABSOLUTE_FROM_TOP));
+ }
+
+ /**
+ *
+ * @param value
+ * @param text Set to null to use the plot's default formatter.
+ * @param textPosition
+ * @param linePaint
+ * @param textPaint
+ */
+ public XValueMarker(Number value, String text, YPositionMetric textPosition, Paint linePaint, Paint textPaint) {
+ super(value, text, textPosition, linePaint, textPaint);
+ }
+
+
+ /**
+ *
+ * @param value
+ * @param text Set to null to use the plot's default formatter.
+ * @param textPosition
+ * @param linePaint
+ * @param textPaint
+ */
+ public XValueMarker(Number value, String text, YPositionMetric textPosition, int linePaint, int textPaint) {
+ super(value, text, textPosition, linePaint, textPaint);
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYAxisType.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYAxisType.java
new file mode 100644
index 0000000..2077d6c
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYAxisType.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.xy;
+
+public enum XYAxisType {
+ DOMAIN,
+ RANGE
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYFramingModel.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYFramingModel.java
new file mode 100644
index 0000000..7d1220c
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYFramingModel.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.xy;
+
+public enum XYFramingModel {
+ ORIGIN,
+ EDGE,
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYGraphBounds.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYGraphBounds.java
new file mode 100644
index 0000000..8089185
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYGraphBounds.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.xy;
+
+public class XYGraphBounds {
+
+
+ private Number minX;
+ private Number maxX;
+ private Number minY;
+ private Number maxY;
+
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYGraphWidget.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYGraphWidget.java
new file mode 100644
index 0000000..c6ce620
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYGraphWidget.java
@@ -0,0 +1,1252 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.xy;
+
+import android.graphics.*;
+
+import com.androidplot.exception.PlotRenderException;
+import com.androidplot.ui.LayoutManager;
+import com.androidplot.ui.SizeMetrics;
+import com.androidplot.ui.widget.Widget;
+import com.androidplot.util.FontUtils;
+import com.androidplot.util.ValPixConverter;
+import com.androidplot.util.ZHash;
+import com.androidplot.util.ZIndexable;
+
+import java.text.DecimalFormat;
+import java.text.Format;
+
+/**
+ * Displays graphical data annotated with domain and range tick markers.
+ */
+public class XYGraphWidget extends Widget {
+
+ public float getRangeLabelOrientation() {
+ return rangeLabelOrientation;
+ }
+
+ public void setRangeLabelOrientation(float rangeLabelOrientation) {
+ this.rangeLabelOrientation = rangeLabelOrientation;
+ }
+
+ public float getDomainLabelOrientation() {
+ return domainLabelOrientation;
+ }
+
+ public void setDomainLabelOrientation(float domainLabelOrientation) {
+ this.domainLabelOrientation = domainLabelOrientation;
+ }
+
+ /**
+ * Will be used in a future version.
+ */
+ public enum XYPlotOrientation {
+ HORIZONTAL, VERTICAL
+ }
+
+ private static final int MARKER_LABEL_SPACING = 2;
+ private static final int CURSOR_LABEL_SPACING = 2; // space between cursor
+ private static final String TAG = "AndroidPlot";
+ // lines and label in
+ // pixels
+ private float domainLabelWidth = 15; // how many pixels is the area
+ // allocated for domain labels
+ private float rangeLabelWidth = 41; // ...
+ private float domainLabelVerticalOffset = -5;
+ private float domainLabelHorizontalOffset = 0.0f;
+ private float rangeLabelHorizontalOffset = 1.0f; // allows tweaking of text position
+ private float rangeLabelVerticalOffset = 0.0f; // allows tweaking of text position
+
+ private int ticksPerRangeLabel = 1;
+ private int ticksPerDomainLabel = 1;
+ private float gridPaddingTop = 0;
+ private float gridPaddingBottom = 0;
+ private float gridPaddingLeft = 0;
+ private float gridPaddingRight = 0;
+ private int domainLabelTickExtension = 5;
+ private int rangeLabelTickExtension = 5;
+ private Paint gridBackgroundPaint;
+ private Paint rangeGridLinePaint;
+ private Paint rangeSubGridLinePaint;
+ private Paint domainGridLinePaint;
+ private Paint domainSubGridLinePaint;
+ private Paint domainLabelPaint;
+ private Paint rangeLabelPaint;
+ private Paint domainCursorPaint;
+ private Paint rangeCursorPaint;
+ private Paint cursorLabelPaint;
+ private Paint cursorLabelBackgroundPaint;
+ private XYPlot plot;
+ private Format rangeValueFormat;
+ private Format domainValueFormat;
+ private Paint domainOriginLinePaint;
+ private Paint rangeOriginLinePaint;
+ private Paint domainOriginLabelPaint;
+ private Paint rangeOriginLabelPaint;
+ private RectF gridRect;
+ private RectF paddedGridRect;
+ private float domainCursorPosition;
+ private float rangeCursorPosition;
+ @SuppressWarnings("FieldCanBeLocal")
+ private boolean drawCursorLabelEnabled = true;
+ private boolean drawMarkersEnabled = true;
+
+ private boolean rangeAxisLeft = true;
+ private boolean domainAxisBottom = true;
+
+ private float rangeLabelOrientation;
+ private float domainLabelOrientation;
+
+ // TODO: consider typing this manager with a special
+ // axisLabelRegionFormatter
+ // private ZHash<LineRegion, AxisValueLabelFormatter> domainLabelRegions;
+ // private ZHash<LineRegion, AxisValueLabelFormatter> rangeLabelRegions;
+ private ZHash<RectRegion, AxisValueLabelFormatter> axisValueLabelRegions;
+
+ {
+ gridBackgroundPaint = new Paint();
+ gridBackgroundPaint.setColor(Color.rgb(140, 140, 140));
+ gridBackgroundPaint.setStyle(Paint.Style.FILL);
+ rangeGridLinePaint = new Paint();
+ rangeGridLinePaint.setColor(Color.rgb(180, 180, 180));
+ rangeGridLinePaint.setAntiAlias(true);
+ rangeGridLinePaint.setStyle(Paint.Style.STROKE);
+ domainGridLinePaint = new Paint(rangeGridLinePaint);
+ domainSubGridLinePaint = new Paint(domainGridLinePaint);
+ rangeSubGridLinePaint = new Paint(rangeGridLinePaint);
+ domainOriginLinePaint = new Paint();
+ domainOriginLinePaint.setColor(Color.WHITE);
+ domainOriginLinePaint.setAntiAlias(true);
+ rangeOriginLinePaint = new Paint();
+ rangeOriginLinePaint.setColor(Color.WHITE);
+ rangeOriginLinePaint.setAntiAlias(true);
+ domainOriginLabelPaint = new Paint();
+ domainOriginLabelPaint.setColor(Color.WHITE);
+ domainOriginLabelPaint.setAntiAlias(true);
+ domainOriginLabelPaint.setTextAlign(Paint.Align.CENTER);
+ rangeOriginLabelPaint = new Paint();
+ rangeOriginLabelPaint.setColor(Color.WHITE);
+ rangeOriginLabelPaint.setAntiAlias(true);
+ rangeOriginLabelPaint.setTextAlign(Paint.Align.RIGHT);
+ domainLabelPaint = new Paint();
+ domainLabelPaint.setColor(Color.LTGRAY);
+ domainLabelPaint.setAntiAlias(true);
+ domainLabelPaint.setTextAlign(Paint.Align.CENTER);
+ rangeLabelPaint = new Paint();
+ rangeLabelPaint.setColor(Color.LTGRAY);
+ rangeLabelPaint.setAntiAlias(true);
+ rangeLabelPaint.setTextAlign(Paint.Align.RIGHT);
+ domainCursorPaint = new Paint();
+ domainCursorPaint.setColor(Color.YELLOW);
+ rangeCursorPaint = new Paint();
+ rangeCursorPaint.setColor(Color.YELLOW);
+ cursorLabelPaint = new Paint();
+ cursorLabelPaint.setColor(Color.YELLOW);
+ cursorLabelBackgroundPaint = new Paint();
+ cursorLabelBackgroundPaint.setColor(Color.argb(100, 50, 50, 50));
+ setMarginTop(7);
+ setMarginRight(4);
+ setMarginBottom(4);
+ rangeValueFormat = new DecimalFormat("0.0");
+ domainValueFormat = new DecimalFormat("0.0");
+ // domainLabelRegions = new ZHash<LineRegion,
+ // AxisValueLabelFormatter>();
+ // rangeLabelRegions = new ZHash<LineRegion, AxisValueLabelFormatter>();
+ axisValueLabelRegions = new ZHash<RectRegion, AxisValueLabelFormatter>();
+ }
+
+ public XYGraphWidget(LayoutManager layoutManager, XYPlot plot, SizeMetrics sizeMetrics) {
+ super(layoutManager, sizeMetrics);
+ this.plot = plot;
+ }
+
+ public ZIndexable<RectRegion> getAxisValueLabelRegions() {
+ return axisValueLabelRegions;
+ }
+
+ /**
+ * Add a new Region used for rendering axis valuelabels. Note that it is
+ * possible to add multiple Region instances which overlap, in which cast
+ * the last region to be added will be used. It is up to the developer to
+ * guard against this often undesireable situation.
+ *
+ * @param region
+ * @param formatter
+ */
+ public void addAxisValueLabelRegion(RectRegion region,
+ AxisValueLabelFormatter formatter) {
+ axisValueLabelRegions.addToTop(region, formatter);
+ }
+
+ /**
+ * Convenience method - wraps addAxisValueLabelRegion, using
+ * Double.POSITIVE_INFINITY and Double.NEGATIVE_INFINITY to mask off range
+ * axis value labels.
+ *
+ * @param min
+ * @param max
+ * @param formatter
+ *
+ */
+ public void addDomainAxisValueLabelRegion(double min, double max,
+ AxisValueLabelFormatter formatter) {
+ addAxisValueLabelRegion(new RectRegion(min, max,
+ Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, null),
+ formatter);
+ }
+
+ /**
+ * Convenience method - wraps addAxisValueLabelRegion, using
+ * Double.POSITIVE_INFINITY and Double.NEGATIVE_INFINITY to mask off domain
+ * axis value labels.
+ *
+ * @param min
+ * @param max
+ * @param formatter
+ */
+ public void addRangeAxisValueLabelRegion(double min, double max,
+ AxisValueLabelFormatter formatter) {
+ addAxisValueLabelRegion(new RectRegion(Double.POSITIVE_INFINITY,
+ Double.NEGATIVE_INFINITY, min, max, null), formatter);
+ }
+
+ /*
+ * public void addRangeLabelRegion(LineRegion region,
+ * AxisValueLabelFormatter formatter) { rangeLabelRegions.addToTop(region,
+ * formatter); }
+ *
+ * public boolean removeRangeLabelRegion(LineRegion region) { return
+ * rangeLabelRegions.remove(region); }
+ */
+
+ /**
+ * Returns the formatter associated with the first (bottom) Region
+ * containing x and y.
+ *
+ * @param x
+ * @param y
+ * @return the formatter associated with the first (bottom) region
+ * containing x and y. null otherwise.
+ */
+ public AxisValueLabelFormatter getAxisValueLabelFormatterForVal(double x,
+ double y) {
+ for (RectRegion r : axisValueLabelRegions.elements()) {
+ if (r.containsValue(x, y)) {
+ return axisValueLabelRegions.get(r);
+ }
+ }
+ return null;
+ }
+
+ public AxisValueLabelFormatter getAxisValueLabelFormatterForDomainVal(
+ double val) {
+ for (RectRegion r : axisValueLabelRegions.elements()) {
+ if (r.containsDomainValue(val)) {
+ return axisValueLabelRegions.get(r);
+ }
+ }
+ return null;
+ }
+
+ public AxisValueLabelFormatter getAxisValueLabelFormatterForRangeVal(
+ double val) {
+ for (RectRegion r : axisValueLabelRegions.elements()) {
+ if (r.containsRangeValue(val)) {
+ return axisValueLabelRegions.get(r);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the formatter associated with the first (bottom-most) Region
+ * containing value.
+ *
+ * @param value
+ * @return
+ */
+ /*
+ * public AxisValueLabelFormatter getXYAxisFormatterForRangeVal(double
+ * value) { return getRegionContainingVal(rangeLabelRegions, value); }
+ *//**
+ * Returns the formatter associated with the first (bottom-most) Region
+ * containing value.
+ *
+ * @param value
+ * @return
+ */
+ /*
+ * public AxisValueLabelFormatter getXYAxisFormatterForDomainVal(double
+ * value) { return getRegionContainingVal(domainLabelRegions, value); }
+ */
+
+ /*
+ * private AxisValueLabelFormatter getRegionContainingVal(ZHash<LineRegion,
+ * AxisValueLabelFormatter> zhash, double val) { for (LineRegion r :
+ * zhash.elements()) { if (r.contains(val)) { return
+ * rangeLabelRegions.get(r); } } // nothing found return null; }
+ */
+
+ /**
+ * Returns a RectF representing the grid area last drawn by this plot.
+ *
+ * @return
+ */
+ public RectF getGridRect() {
+ return paddedGridRect;
+ }
+ private String getFormattedRangeValue(Number value) {
+ return rangeValueFormat.format(value);
+ }
+
+ private String getFormattedDomainValue(Number value) {
+ return domainValueFormat.format(value);
+ }
+
+ /**
+ * Convenience method. Wraps getYVal(float)
+ *
+ * @param point
+ * @return
+ */
+ public Double getYVal(PointF point) {
+ return getYVal(point.y);
+ }
+
+ /**
+ * Converts a y pixel to a y value.
+ *
+ * @param yPix
+ * @return
+ */
+ public Double getYVal(float yPix) {
+ if (plot.getCalculatedMinY() == null
+ || plot.getCalculatedMaxY() == null) {
+ return null;
+ }
+ return ValPixConverter.pixToVal(yPix - paddedGridRect.top, plot
+ .getCalculatedMinY().doubleValue(), plot.getCalculatedMaxY()
+ .doubleValue(), paddedGridRect.height(), true);
+ }
+
+ /**
+ * Convenience method. Wraps getXVal(float)
+ *
+ * @param point
+ * @return
+ */
+ public Double getXVal(PointF point) {
+ return getXVal(point.x);
+ }
+
+ /**
+ * Converts an x pixel into an x value.
+ *
+ * @param xPix
+ * @return
+ */
+ public Double getXVal(float xPix) {
+ if (plot.getCalculatedMinX() == null
+ || plot.getCalculatedMaxX() == null) {
+ return null;
+ }
+ return ValPixConverter.pixToVal(xPix - paddedGridRect.left, plot
+ .getCalculatedMinX().doubleValue(), plot.getCalculatedMaxX()
+ .doubleValue(), paddedGridRect.width(), false);
+ }
+
+ @Override
+ protected void doOnDraw(Canvas canvas, RectF widgetRect)
+ throws PlotRenderException {
+ gridRect = getGridRect(widgetRect); // used for drawing the background
+ // of the grid
+ paddedGridRect = getPaddedGridRect(gridRect); // used for drawing lines
+ // etc.
+ //Log.v(TAG, "gridRect :" + gridRect);
+ //Log.v(TAG, "paddedGridRect :" + paddedGridRect);
+ // if (!plot.isEmpty()) {
+ // don't draw if we have no space to draw into
+ if ((paddedGridRect.height() > 0.0f) && (paddedGridRect.width() > 0.0f)) {
+ if (plot.getCalculatedMinX() != null
+ && plot.getCalculatedMaxX() != null
+ && plot.getCalculatedMinY() != null
+ && plot.getCalculatedMaxY() != null) {
+ drawGrid(canvas);
+ drawData(canvas);
+ drawCursors(canvas);
+ if (isDrawMarkersEnabled()) {
+ drawMarkers(canvas);
+ }
+ }
+ }
+ // }
+ }
+
+ private RectF getGridRect(RectF widgetRect) {
+ return new RectF(widgetRect.left + ((rangeAxisLeft)?rangeLabelWidth:1),
+ widgetRect.top + ((domainAxisBottom)?1:domainLabelWidth),
+ widgetRect.right - ((rangeAxisLeft)?1:rangeLabelWidth),
+ widgetRect.bottom - ((domainAxisBottom)?domainLabelWidth:1));
+ }
+
+ private RectF getPaddedGridRect(RectF gridRect) {
+ return new RectF(gridRect.left + gridPaddingLeft, gridRect.top
+ + gridPaddingTop, gridRect.right - gridPaddingRight,
+ gridRect.bottom - gridPaddingBottom);
+ }
+
+ private void drawTickText(Canvas canvas, XYAxisType axis, Number value,
+ float xPix, float yPix, Paint labelPaint) {
+ AxisValueLabelFormatter rf = null;
+ String txt = null;
+ double v = value.doubleValue();
+
+ int canvasState = canvas.save();
+ try {
+ switch (axis) {
+ case DOMAIN:
+ rf = getAxisValueLabelFormatterForDomainVal(v);
+ txt = getFormattedDomainValue(value);
+ canvas.rotate(getDomainLabelOrientation(), xPix, yPix);
+ break;
+ case RANGE:
+ rf = getAxisValueLabelFormatterForRangeVal(v);
+ txt = getFormattedRangeValue(value);
+ canvas.rotate(getRangeLabelOrientation(), xPix, yPix);
+ break;
+ }
+
+ // if a matching region formatter was found, create a clone
+ // of labelPaint and use the formatter's color. Otherwise
+ // just use labelPaint:
+ Paint p;
+ if (rf != null) {
+ // p = rf.getPaint();
+ p = new Paint(labelPaint);
+ p.setColor(rf.getColor());
+ // p.setColor(Color.RED);
+ } else {
+ p = labelPaint;
+ }
+ canvas.drawText(txt, xPix, yPix, p);
+ } finally {
+ canvas.restoreToCount(canvasState);
+ }
+ }
+
+ private void drawDomainTick(Canvas canvas, float xPix, Number xVal,
+ Paint labelPaint, Paint linePaint, boolean drawLineOnly) {
+ if (!drawLineOnly) {
+ if (linePaint != null) {
+ if (domainAxisBottom){
+ canvas.drawLine(xPix, gridRect.top, xPix, gridRect.bottom
+ + domainLabelTickExtension, linePaint);
+ } else {
+ canvas.drawLine(xPix, gridRect.top - domainLabelTickExtension, xPix,
+ gridRect.bottom , linePaint);
+ }
+ }
+ if (labelPaint != null) {
+ float fontHeight = FontUtils.getFontHeight(labelPaint);
+ float yPix;
+ if (domainAxisBottom){
+ yPix = gridRect.bottom + domainLabelTickExtension
+ + domainLabelVerticalOffset + fontHeight;
+ } else {
+ yPix = gridRect.top - domainLabelTickExtension
+ - domainLabelVerticalOffset;
+ }
+ drawTickText(canvas, XYAxisType.DOMAIN, xVal, xPix + domainLabelHorizontalOffset, yPix,
+ labelPaint);
+ }
+ } else if (linePaint != null) {
+
+ canvas.drawLine(xPix, gridRect.top, xPix, gridRect.bottom,
+ linePaint);
+
+ }
+ }
+
+ public void drawRangeTick(Canvas canvas, float yPix, Number yVal,
+ Paint labelPaint, Paint linePaint, boolean drawLineOnly) {
+ if (!drawLineOnly) {
+ if (linePaint != null) {
+ if (rangeAxisLeft){
+ canvas.drawLine(gridRect.left - rangeLabelTickExtension, yPix,
+ gridRect.right, yPix, linePaint);
+ } else {
+ canvas.drawLine(gridRect.left, yPix,
+ gridRect.right + rangeLabelTickExtension, yPix, linePaint);
+ }
+ }
+ if (labelPaint != null) {
+ float xPix;
+ if (rangeAxisLeft){
+ xPix = gridRect.left
+ - (rangeLabelTickExtension + rangeLabelHorizontalOffset);
+ } else {
+ xPix = gridRect.right
+ + (rangeLabelTickExtension + rangeLabelHorizontalOffset);
+ }
+ drawTickText(canvas, XYAxisType.RANGE, yVal, xPix, yPix - rangeLabelVerticalOffset,
+ labelPaint);
+ }
+ } else if (linePaint != null) {
+ canvas.drawLine(gridRect.left, yPix, gridRect.right, yPix,
+ linePaint);
+ }
+ }
+
+ /**
+ * Draws the drid and domain/range labels for the plot.
+ *
+ * @param canvas
+ */
+ protected void drawGrid(Canvas canvas) {
+
+ if (gridBackgroundPaint != null) {
+ canvas.drawRect(gridRect, gridBackgroundPaint);
+ }
+
+ float domainOriginF;
+ if (plot.getDomainOrigin() != null) {
+ double domainOriginVal = plot.getDomainOrigin().doubleValue();
+ domainOriginF = ValPixConverter.valToPix(domainOriginVal, plot
+ .getCalculatedMinX().doubleValue(), plot
+ .getCalculatedMaxX().doubleValue(), paddedGridRect.width(),
+ false);
+ domainOriginF += paddedGridRect.left;
+ // if no origin is set, use the leftmost value visible on the grid:
+ } else {
+ domainOriginF = paddedGridRect.left;
+ }
+
+ XYStep domainStep = XYStepCalculator.getStep(plot, XYAxisType.DOMAIN,
+ paddedGridRect, plot.getCalculatedMinX().doubleValue(), plot
+ .getCalculatedMaxX().doubleValue());
+
+ // draw domain origin:
+ if (domainOriginF >= paddedGridRect.left
+ && domainOriginF <= paddedGridRect.right) {
+ if (domainOriginLinePaint != null){
+ domainOriginLinePaint.setTextAlign(Paint.Align.CENTER);
+ }
+ drawDomainTick(canvas, domainOriginF, plot.getDomainOrigin()
+ .doubleValue(), domainOriginLabelPaint,
+ domainOriginLinePaint, false);
+ }
+
+ // draw ticks LEFT of origin:
+ {
+ int i = 1;
+ double xVal;
+ float xPix = domainOriginF - domainStep.getStepPix();
+ for (; xPix >= paddedGridRect.left; xPix = domainOriginF
+ - (i * domainStep.getStepPix())) {
+ xVal = plot.getDomainOrigin().doubleValue() - i
+ * domainStep.getStepVal();
+ if (xPix >= paddedGridRect.left && xPix <= paddedGridRect.right) {
+ if (i % getTicksPerDomainLabel() == 0) {
+ drawDomainTick(canvas, xPix, xVal, domainLabelPaint,
+ domainGridLinePaint, false);
+ } else {
+ drawDomainTick(canvas, xPix, xVal, domainLabelPaint,
+ domainSubGridLinePaint, true);
+ }
+ }
+ i++;
+ }
+ }
+
+ // draw ticks RIGHT of origin:
+ {
+ int i = 1;
+ double xVal;
+ float xPix = domainOriginF + domainStep.getStepPix();
+ for (; xPix <= paddedGridRect.right; xPix = domainOriginF
+ + (i * domainStep.getStepPix())) {
+ xVal = plot.getDomainOrigin().doubleValue() + i
+ * domainStep.getStepVal();
+ if (xPix >= paddedGridRect.left && xPix <= paddedGridRect.right) {
+
+ if (i % getTicksPerDomainLabel() == 0) {
+ drawDomainTick(canvas, xPix, xVal, domainLabelPaint,
+ domainGridLinePaint, false);
+ } else {
+ drawDomainTick(canvas, xPix, xVal, domainLabelPaint,
+ domainSubGridLinePaint, true);
+ }
+ }
+ i++;
+ }
+ }
+
+ // draw range origin:
+
+ float rangeOriginF;
+ if (plot.getRangeOrigin() != null) {
+ // --------- NEW WAY ------
+ double rangeOriginD = plot.getRangeOrigin().doubleValue();
+ rangeOriginF = ValPixConverter.valToPix(rangeOriginD, plot
+ .getCalculatedMinY().doubleValue(), plot
+ .getCalculatedMaxY().doubleValue(),
+ paddedGridRect.height(), true);
+ rangeOriginF += paddedGridRect.top;
+ // if no origin is set, use the leftmost value visible on the grid
+ } else {
+ rangeOriginF = paddedGridRect.bottom;
+ }
+
+ XYStep rangeStep = XYStepCalculator.getStep(plot, XYAxisType.RANGE,
+ paddedGridRect, plot.getCalculatedMinY().doubleValue(), plot
+ .getCalculatedMaxY().doubleValue());
+
+ // draw range origin:
+ if (rangeOriginF >= paddedGridRect.top
+ && rangeOriginF <= paddedGridRect.bottom) {
+ if (rangeOriginLinePaint != null){
+ rangeOriginLinePaint.setTextAlign(Paint.Align.RIGHT);
+ }
+ drawRangeTick(canvas, rangeOriginF, plot.getRangeOrigin()
+ .doubleValue(), rangeOriginLabelPaint,
+ rangeOriginLinePaint, false);
+ }
+ // draw ticks ABOVE origin:
+ {
+ int i = 1;
+ double yVal;
+ float yPix = rangeOriginF - rangeStep.getStepPix();
+ for (; yPix >= paddedGridRect.top; yPix = rangeOriginF
+ - (i * rangeStep.getStepPix())) {
+ yVal = plot.getRangeOrigin().doubleValue() + i
+ * rangeStep.getStepVal();
+ if (yPix >= paddedGridRect.top && yPix <= paddedGridRect.bottom) {
+ if (i % getTicksPerRangeLabel() == 0) {
+ drawRangeTick(canvas, yPix, yVal, rangeLabelPaint,
+ rangeGridLinePaint, false);
+ } else {
+ drawRangeTick(canvas, yPix, yVal, rangeLabelPaint,
+ rangeSubGridLinePaint, true);
+ }
+ }
+ i++;
+ }
+ }
+
+ // draw ticks BENEATH origin:
+ {
+ int i = 1;
+ double yVal;
+ float yPix = rangeOriginF + rangeStep.getStepPix();
+ for (; yPix <= paddedGridRect.bottom; yPix = rangeOriginF
+ + (i * rangeStep.getStepPix())) {
+ yVal = plot.getRangeOrigin().doubleValue() - i
+ * rangeStep.getStepVal();
+ if (yPix >= paddedGridRect.top && yPix <= paddedGridRect.bottom) {
+ if (i % getTicksPerRangeLabel() == 0) {
+ drawRangeTick(canvas, yPix, yVal, rangeLabelPaint,
+ rangeGridLinePaint, false);
+ } else {
+ drawRangeTick(canvas, yPix, yVal, rangeLabelPaint,
+ rangeSubGridLinePaint, true);
+ }
+ }
+ i++;
+ }
+ }
+ }
+
+ /**
+ * Renders the text associated with user defined markers
+ *
+ * @param canvas
+ * @param text
+ * @param marker
+ * @param x
+ * @param y
+ */
+ private void drawMarkerText(Canvas canvas, String text, ValueMarker marker,
+ float x, float y) {
+ x += MARKER_LABEL_SPACING;
+ y -= MARKER_LABEL_SPACING;
+ RectF textRect = new RectF(FontUtils.getStringDimensions(text,
+ marker.getTextPaint()));
+ textRect.offsetTo(x, y - textRect.height());
+
+ if (textRect.right > paddedGridRect.right) {
+ textRect.offset(-(textRect.right - paddedGridRect.right), 0);
+ }
+
+ if (textRect.top < paddedGridRect.top) {
+ textRect.offset(0, paddedGridRect.top - textRect.top);
+ }
+
+ canvas.drawText(text, textRect.left, textRect.bottom,
+ marker.getTextPaint());
+
+ }
+
+ protected void drawMarkers(Canvas canvas) {
+ for (YValueMarker marker : plot.getYValueMarkers()) {
+
+ if (marker.getValue() != null) {
+ double yVal = marker.getValue().doubleValue();
+ float yPix = ValPixConverter.valToPix(yVal, plot
+ .getCalculatedMinY().doubleValue(), plot
+ .getCalculatedMaxY().doubleValue(), paddedGridRect
+ .height(), true);
+ yPix += paddedGridRect.top;
+ canvas.drawLine(paddedGridRect.left, yPix,
+ paddedGridRect.right, yPix, marker.getLinePaint());
+
+ // String text = getFormattedRangeValue(yVal);
+ float xPix = marker.getTextPosition().getPixelValue(
+ paddedGridRect.width());
+ xPix += paddedGridRect.left;
+
+ if (marker.getText() != null) {
+ drawMarkerText(canvas, marker.getText(), marker, xPix, yPix);
+ } else {
+ drawMarkerText(canvas,
+ getFormattedRangeValue(marker.getValue()), marker,
+ xPix, yPix);
+ }
+ }
+ }
+
+ for (XValueMarker marker : plot.getXValueMarkers()) {
+ if (marker.getValue() != null) {
+ double xVal = marker.getValue().doubleValue();
+ float xPix = ValPixConverter.valToPix(xVal, plot
+ .getCalculatedMinX().doubleValue(), plot
+ .getCalculatedMaxX().doubleValue(), paddedGridRect
+ .width(), false);
+ xPix += paddedGridRect.left;
+ canvas.drawLine(xPix, paddedGridRect.top, xPix,
+ paddedGridRect.bottom, marker.getLinePaint());
+
+ // String text = getFormattedDomainValue(xVal);
+ float yPix = marker.getTextPosition().getPixelValue(
+ paddedGridRect.height());
+ yPix += paddedGridRect.top;
+ if (marker.getText() != null) {
+ drawMarkerText(canvas, marker.getText(), marker, xPix, yPix);
+ } else {
+ drawMarkerText(canvas,
+ getFormattedDomainValue(marker.getValue()), marker,
+ xPix, yPix);
+ }
+ }
+ }
+ }
+
+ protected void drawCursors(Canvas canvas) {
+ boolean hasDomainCursor = false;
+ // draw the domain cursor:
+ if (domainCursorPaint != null
+ && domainCursorPosition <= paddedGridRect.right
+ && domainCursorPosition >= paddedGridRect.left) {
+ hasDomainCursor = true;
+ canvas.drawLine(domainCursorPosition, paddedGridRect.top,
+ domainCursorPosition, paddedGridRect.bottom,
+ domainCursorPaint);
+ }
+
+ boolean hasRangeCursor = false;
+ // draw the range cursor:
+ if (rangeCursorPaint != null
+ && rangeCursorPosition >= paddedGridRect.top
+ && rangeCursorPosition <= paddedGridRect.bottom) {
+ hasRangeCursor = true;
+ canvas.drawLine(paddedGridRect.left, rangeCursorPosition,
+ paddedGridRect.right, rangeCursorPosition, rangeCursorPaint);
+ }
+
+ if (drawCursorLabelEnabled && cursorLabelPaint != null
+ && hasRangeCursor && hasDomainCursor) {
+
+ String label = "X="
+ + getDomainValueFormat().format(getDomainCursorVal());
+ label += " Y=" + getRangeValueFormat().format(getRangeCursorVal());
+
+ // convert the label dimensions rect into floating-point:
+ RectF cursorRect = new RectF(FontUtils.getPackedStringDimensions(
+ label, cursorLabelPaint));
+ cursorRect.offsetTo(domainCursorPosition, rangeCursorPosition
+ - cursorRect.height());
+
+ // if we are too close to the right edge of the plot, we will move
+ // the
+ // label to the left side of our cursor:
+ if (cursorRect.right >= paddedGridRect.right) {
+ cursorRect.offsetTo(domainCursorPosition - cursorRect.width(),
+ cursorRect.top);
+ }
+
+ // same thing for the top edge of the plot:
+ // dunno why but these rects can have negative values for top and
+ // bottom.
+ if (cursorRect.top <= paddedGridRect.top) {
+ cursorRect.offsetTo(cursorRect.left, rangeCursorPosition);
+ }
+
+ if (cursorLabelBackgroundPaint != null) {
+ canvas.drawRect(cursorRect, cursorLabelBackgroundPaint);
+ }
+
+ canvas.drawText(label, cursorRect.left, cursorRect.bottom,
+ cursorLabelPaint);
+ }
+ }
+
+ /**
+ * Draws lines and points for each element in the series.
+ *
+ * @param canvas
+ * @throws PlotRenderException
+ */
+ protected void drawData(Canvas canvas) throws PlotRenderException {
+ // TODO: iterate through a XYSeriesRenderer list
+
+ // int canvasState = canvas.save();
+ try {
+ canvas.save(Canvas.ALL_SAVE_FLAG);
+ canvas.clipRect(gridRect, android.graphics.Region.Op.INTERSECT);
+ for (XYSeriesRenderer renderer : plot.getRendererList()) {
+ renderer.render(canvas, paddedGridRect);
+ }
+ // canvas.restoreToCount(canvasState);
+ } finally {
+ canvas.restore();
+ }
+ }
+
+ protected void drawPoint(Canvas canvas, PointF point, Paint paint) {
+ canvas.drawPoint(point.x, point.y, paint);
+ }
+
+ public float getDomainLabelWidth() {
+ return domainLabelWidth;
+ }
+
+ public void setDomainLabelWidth(float domainLabelWidth) {
+ this.domainLabelWidth = domainLabelWidth;
+ }
+
+ public float getRangeLabelWidth() {
+ return rangeLabelWidth;
+ }
+
+ public void setRangeLabelWidth(float rangeLabelWidth) {
+ this.rangeLabelWidth = rangeLabelWidth;
+ }
+
+ public float getDomainLabelVerticalOffset() {
+ return domainLabelVerticalOffset;
+ }
+
+ public void setDomainLabelVerticalOffset(float domainLabelVerticalOffset) {
+ this.domainLabelVerticalOffset = domainLabelVerticalOffset;
+ }
+
+ public float getDomainLabelHorizontalOffset() {
+ return domainLabelHorizontalOffset;
+ }
+
+ public void setDomainLabelHorizontalOffset(float domainLabelHorizontalOffset) {
+ this.domainLabelHorizontalOffset = domainLabelHorizontalOffset;
+ }
+
+ public float getRangeLabelHorizontalOffset() {
+ return rangeLabelHorizontalOffset;
+ }
+
+ public void setRangeLabelHorizontalOffset(float rangeLabelHorizontalOffset) {
+ this.rangeLabelHorizontalOffset = rangeLabelHorizontalOffset;
+ }
+
+ public float getRangeLabelVerticalOffset() {
+ return rangeLabelVerticalOffset;
+ }
+
+ public void setRangeLabelVerticalOffset(float rangeLabelVerticalOffset) {
+ this.rangeLabelVerticalOffset = rangeLabelVerticalOffset;
+ }
+
+ public Paint getGridBackgroundPaint() {
+ return gridBackgroundPaint;
+ }
+
+ public void setGridBackgroundPaint(Paint gridBackgroundPaint) {
+ this.gridBackgroundPaint = gridBackgroundPaint;
+ }
+
+ public Paint getDomainLabelPaint() {
+ return domainLabelPaint;
+ }
+
+ public void setDomainLabelPaint(Paint domainLabelPaint) {
+ this.domainLabelPaint = domainLabelPaint;
+ }
+
+ public Paint getRangeLabelPaint() {
+ return rangeLabelPaint;
+ }
+
+ public void setRangeLabelPaint(Paint rangeLabelPaint) {
+ this.rangeLabelPaint = rangeLabelPaint;
+ }
+
+ /**
+ * Get the paint used to draw the domain grid line.
+ */
+ public Paint getDomainGridLinePaint() {
+ return domainGridLinePaint;
+ }
+
+ /**
+ * Set the paint used to draw the domain grid line.
+ * @param gridLinePaint
+ */
+ public void setDomainGridLinePaint(Paint gridLinePaint) {
+ this.domainGridLinePaint = gridLinePaint;
+ }
+
+ /**
+ * Get the paint used to draw the range grid line.
+ */
+ public Paint getRangeGridLinePaint() {
+ return rangeGridLinePaint;
+ }
+
+ /**
+ * Get the paint used to draw the domain grid line.
+ */
+ public Paint getDomainSubGridLinePaint() {
+ return domainSubGridLinePaint;
+ }
+
+ /**
+ * Set the paint used to draw the domain grid line.
+ * @param gridLinePaint
+ */
+ public void setDomainSubGridLinePaint(Paint gridLinePaint) {
+ this.domainSubGridLinePaint = gridLinePaint;
+ }
+
+ /**
+ * Set the Paint used to draw the range grid line.
+ * @param gridLinePaint
+ */
+ public void setRangeGridLinePaint(Paint gridLinePaint) {
+ this.rangeGridLinePaint = gridLinePaint;
+ }
+
+ /**
+ * Get the paint used to draw the range grid line.
+ */
+ public Paint getRangeSubGridLinePaint() {
+ return rangeSubGridLinePaint;
+ }
+
+ /**
+ * Set the Paint used to draw the range grid line.
+ * @param gridLinePaint
+ */
+ public void setRangeSubGridLinePaint(Paint gridLinePaint) {
+ this.rangeSubGridLinePaint = gridLinePaint;
+ }
+
+ // TODO: make a generic renderer queue.
+
+ public Format getRangeValueFormat() {
+ return rangeValueFormat;
+ }
+
+ public void setRangeValueFormat(Format rangeValueFormat) {
+ this.rangeValueFormat = rangeValueFormat;
+ }
+
+ public Format getDomainValueFormat() {
+ return domainValueFormat;
+ }
+
+ public void setDomainValueFormat(Format domainValueFormat) {
+ this.domainValueFormat = domainValueFormat;
+ }
+
+ public int getDomainLabelTickExtension() {
+ return domainLabelTickExtension;
+ }
+
+ public void setDomainLabelTickExtension(int domainLabelTickExtension) {
+ this.domainLabelTickExtension = domainLabelTickExtension;
+ }
+
+ public int getRangeLabelTickExtension() {
+ return rangeLabelTickExtension;
+ }
+
+ public void setRangeLabelTickExtension(int rangeLabelTickExtension) {
+ this.rangeLabelTickExtension = rangeLabelTickExtension;
+ }
+
+ public int getTicksPerRangeLabel() {
+ return ticksPerRangeLabel;
+ }
+
+ public void setTicksPerRangeLabel(int ticksPerRangeLabel) {
+ this.ticksPerRangeLabel = ticksPerRangeLabel;
+ }
+
+ public int getTicksPerDomainLabel() {
+ return ticksPerDomainLabel;
+ }
+
+ public void setTicksPerDomainLabel(int ticksPerDomainLabel) {
+ this.ticksPerDomainLabel = ticksPerDomainLabel;
+ }
+
+ public void setGridPaddingTop(float gridPaddingTop) {
+ this.gridPaddingTop = gridPaddingTop;
+ }
+
+ public float getGridPaddingBottom() {
+ return gridPaddingBottom;
+ }
+
+ public void setGridPaddingBottom(float gridPaddingBottom) {
+ this.gridPaddingBottom = gridPaddingBottom;
+ }
+
+ public float getGridPaddingLeft() {
+ return gridPaddingLeft;
+ }
+
+ public void setGridPaddingLeft(float gridPaddingLeft) {
+ this.gridPaddingLeft = gridPaddingLeft;
+ }
+
+ public float getGridPaddingRight() {
+ return gridPaddingRight;
+ }
+
+ public void setGridPaddingRight(float gridPaddingRight) {
+ this.gridPaddingRight = gridPaddingRight;
+ }
+
+ public float getGridPaddingTop() {
+ return gridPaddingTop;
+ }
+
+ public void setGridPadding(float left, float top, float right, float bottom) {
+ setGridPaddingLeft(left);
+ setGridPaddingTop(top);
+ setGridPaddingRight(right);
+ setGridPaddingBottom(bottom);
+ }
+
+ public Paint getDomainOriginLinePaint() {
+ return domainOriginLinePaint;
+ }
+
+ public void setDomainOriginLinePaint(Paint domainOriginLinePaint) {
+ this.domainOriginLinePaint = domainOriginLinePaint;
+ }
+
+ public Paint getRangeOriginLinePaint() {
+ return rangeOriginLinePaint;
+ }
+
+ public void setRangeOriginLinePaint(Paint rangeOriginLinePaint) {
+ this.rangeOriginLinePaint = rangeOriginLinePaint;
+ }
+
+ public Paint getDomainOriginLabelPaint() {
+ return domainOriginLabelPaint;
+ }
+
+ public void setDomainOriginLabelPaint(Paint domainOriginLabelPaint) {
+ this.domainOriginLabelPaint = domainOriginLabelPaint;
+ }
+
+ public Paint getRangeOriginLabelPaint() {
+ return rangeOriginLabelPaint;
+ }
+
+ public void setRangeOriginLabelPaint(Paint rangeOriginLabelPaint) {
+ this.rangeOriginLabelPaint = rangeOriginLabelPaint;
+ }
+
+ public void setCursorPosition(float x, float y) {
+ setDomainCursorPosition(x);
+ setRangeCursorPosition(y);
+ }
+
+ public void setCursorPosition(PointF point) {
+ setCursorPosition(point.x, point.y);
+ }
+
+ public float getDomainCursorPosition() {
+ return domainCursorPosition;
+ }
+
+ public Double getDomainCursorVal() {
+ return getXVal(getDomainCursorPosition());
+ }
+
+ public void setDomainCursorPosition(float domainCursorPosition) {
+ this.domainCursorPosition = domainCursorPosition;
+ }
+
+ public float getRangeCursorPosition() {
+ return rangeCursorPosition;
+ }
+
+ public Double getRangeCursorVal() {
+ return getYVal(getRangeCursorPosition());
+ }
+
+ public void setRangeCursorPosition(float rangeCursorPosition) {
+ this.rangeCursorPosition = rangeCursorPosition;
+ }
+
+ public Paint getCursorLabelPaint() {
+ return cursorLabelPaint;
+ }
+
+ public void setCursorLabelPaint(Paint cursorLabelPaint) {
+ this.cursorLabelPaint = cursorLabelPaint;
+ }
+
+ public Paint getCursorLabelBackgroundPaint() {
+ return cursorLabelBackgroundPaint;
+ }
+
+ public void setCursorLabelBackgroundPaint(Paint cursorLabelBackgroundPaint) {
+ this.cursorLabelBackgroundPaint = cursorLabelBackgroundPaint;
+ }
+
+ public boolean isDrawMarkersEnabled() {
+ return drawMarkersEnabled;
+ }
+
+ public void setDrawMarkersEnabled(boolean drawMarkersEnabled) {
+ this.drawMarkersEnabled = drawMarkersEnabled;
+ }
+
+ public boolean isRangeAxisLeft() {
+ return rangeAxisLeft;
+ }
+
+ public void setRangeAxisLeft(boolean rangeAxisLeft) {
+ this.rangeAxisLeft = rangeAxisLeft;
+ }
+
+ public boolean isDomainAxisBottom() {
+ return domainAxisBottom;
+ }
+
+ public void setDomainAxisBottom(boolean domainAxisBottom) {
+ this.domainAxisBottom = domainAxisBottom;
+ }
+
+ /*
+ * set the position of the range axis labels. Set the labelPaint textSizes before setting this.
+ * This call sets the various vertical and horizontal offsets and widths to good defaults.
+ *
+ * @param rangeAxisLeft axis labels are on the left hand side not the right hand side.
+ * @param rangeAxisOverlay axis labels are overlaid on the plot, not external to it.
+ * @param tickSize the size of the tick extensions for none overlaid axis.
+ * @param maxLableString Sample label representing the biggest size space needs to be allocated for.
+ */
+ public void setRangeAxisPosition(boolean rangeAxisLeft, boolean rangeAxisOverlay, int tickSize, String maxLableString){
+ setRangeAxisLeft(rangeAxisLeft);
+
+ if (rangeAxisOverlay) {
+ setRangeLabelWidth(1); // needs to be at least 1 to display grid line.
+ setRangeLabelHorizontalOffset(-2.0f);
+ setRangeLabelVerticalOffset(2.0f); // get above the line
+ Paint p = getRangeLabelPaint();
+ if (p != null) {
+ p.setTextAlign(((rangeAxisLeft)?Paint.Align.LEFT:Paint.Align.RIGHT));
+ }
+ Paint po = getRangeOriginLabelPaint();
+ if (po != null) {
+ po.setTextAlign(((rangeAxisLeft)?Paint.Align.LEFT:Paint.Align.RIGHT));
+ }
+ setRangeLabelTickExtension(0);
+ } else {
+ setRangeLabelWidth(1); // needs to be at least 1 to display grid line.
+ // if we have a paint this gets bigger.
+ setRangeLabelHorizontalOffset(1.0f);
+ setRangeLabelTickExtension(tickSize);
+ Paint p = getRangeLabelPaint();
+ if (p != null) {
+ p.setTextAlign(((!rangeAxisLeft)?Paint.Align.LEFT:Paint.Align.RIGHT));
+ Rect r = FontUtils.getPackedStringDimensions(maxLableString,p);
+ setRangeLabelVerticalOffset(r.top/2);
+ setRangeLabelWidth(r.right + getRangeLabelTickExtension());
+ }
+ Paint po = getRangeOriginLabelPaint();
+ if (po != null) {
+ po.setTextAlign(((!rangeAxisLeft)?Paint.Align.LEFT:Paint.Align.RIGHT));
+ }
+ }
+ }
+
+ /*
+ * set the position of the domain axis labels. Set the labelPaint textSizes before setting this.
+ * This call sets the various vertical and horizontal offsets and widths to good defaults.
+ *
+ * @param domainAxisBottom axis labels are on the bottom not the top of the plot.
+ * @param domainAxisOverlay axis labels are overlaid on the plot, not external to it.
+ * @param tickSize the size of the tick extensions for non overlaid axis.
+ * @param maxLableString Sample label representing the biggest size space needs to be allocated for.
+ */
+ public void setDomainAxisPosition(boolean domainAxisBottom, boolean domainAxisOverlay, int tickSize, String maxLabelString){
+ setDomainAxisBottom(domainAxisBottom);
+ if (domainAxisOverlay) {
+ setDomainLabelWidth(1); // needs to be at least 1 to display grid line.
+ setDomainLabelVerticalOffset(2.0f); // get above the line
+ setDomainLabelTickExtension(0);
+ Paint p = getDomainLabelPaint();
+ if (p != null) {
+ Rect r = FontUtils.getPackedStringDimensions(maxLabelString,p);
+ if (domainAxisBottom){
+ setDomainLabelVerticalOffset(2 * r.top);
+ } else {
+ setDomainLabelVerticalOffset(r.top - 1.0f);
+ }
+ }
+ } else {
+ setDomainLabelWidth(1); // needs to be at least 1 to display grid line.
+ // if we have a paint this gets bigger.
+ setDomainLabelTickExtension(tickSize);
+ Paint p = getDomainLabelPaint();
+ if (p != null) {
+ float fontHeight = FontUtils.getFontHeight(p);
+ if (domainAxisBottom){
+ setDomainLabelVerticalOffset(-4.0f);
+ } else {
+ setDomainLabelVerticalOffset(+1.0f);
+ }
+ setDomainLabelWidth(fontHeight + getDomainLabelTickExtension());
+ }
+ }
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYLegendWidget.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYLegendWidget.java
new file mode 100644
index 0000000..3350406
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYLegendWidget.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.xy;
+
+import android.graphics.*;
+import com.androidplot.ui.LayoutManager;
+import com.androidplot.ui.SeriesAndFormatterList;
+import com.androidplot.ui.SizeMetrics;
+import com.androidplot.ui.TableModel;
+import com.androidplot.ui.widget.Widget;
+import com.androidplot.util.FontUtils;
+
+import java.util.*;
+
+public class XYLegendWidget extends Widget {
+
+ /**
+ * This class is of no use outside of XYLegendWidget. It's just used to alphabetically sort
+ * Region legend entries.
+ */
+ private static class RegionEntryComparator implements Comparator<Map.Entry<XYRegionFormatter, String>> {
+ @Override
+ public int compare(Map.Entry<XYRegionFormatter, String> o1, Map.Entry<XYRegionFormatter, String> o2) {
+ return o1.getValue().compareTo(o2.getValue());
+ }
+ }
+
+ private enum CellType {
+ SERIES,
+ REGION
+ }
+
+ private XYPlot plot;
+ //private float iconWidth = 12;
+ private Paint textPaint;
+ private Paint iconBorderPaint;
+ private TableModel tableModel;
+ private boolean drawIconBackgroundEnabled = true;
+ private boolean drawIconBorderEnabled = true;
+
+ private SizeMetrics iconSizeMetrics;
+ private static final RegionEntryComparator regionEntryComparator = new RegionEntryComparator();
+ //private RectF iconRect = new RectF(0, 0, ICON_WIDTH_DEFAULT, ICON_HEIGHT_DEFAULT);
+
+ {
+ textPaint = new Paint();
+ textPaint.setColor(Color.LTGRAY);
+ textPaint.setAntiAlias(true);
+
+ iconBorderPaint = new Paint();
+ iconBorderPaint.setStyle(Paint.Style.STROKE);
+ //regionEntryComparator = new RegionEntryComparator();
+ }
+
+ public XYLegendWidget(LayoutManager layoutManager, XYPlot plot,
+ SizeMetrics widgetSizeMetrics,
+ TableModel tableModel,
+ SizeMetrics iconSizeMetrics) {
+ super(layoutManager, widgetSizeMetrics);
+ this.plot = plot;
+ setTableModel(tableModel);
+ this.iconSizeMetrics = iconSizeMetrics;
+ }
+
+ public synchronized void setTableModel(TableModel tableModel) {
+ this.tableModel = tableModel;
+ }
+
+ private RectF getIconRect(RectF cellRect) {
+ float cellRectCenterY = cellRect.top + (cellRect.height()/2);
+ RectF iconRect = iconSizeMetrics.getRectF(cellRect);
+
+ // center the icon rect vertically
+ float centeredIconOriginY = cellRectCenterY - (iconRect.height()/2);
+ iconRect.offsetTo(cellRect.left + 1, centeredIconOriginY);
+ return iconRect;
+ }
+
+ private static float getRectCenterY(RectF cellRect) {
+ return cellRect.top + (cellRect.height()/2);
+ }
+
+ private void beginDrawingCell(Canvas canvas, RectF iconRect) {
+
+ Paint bgPaint = plot.getGraphWidget().getGridBackgroundPaint();
+ if(drawIconBackgroundEnabled && bgPaint != null) {
+ canvas.drawRect(iconRect, bgPaint);
+ }
+ }
+
+ private void finishDrawingCell(Canvas canvas, RectF cellRect, RectF iconRect, String text) {
+
+ Paint bgPaint = plot.getGraphWidget().getGridBackgroundPaint();
+ if(drawIconBorderEnabled && bgPaint != null) {
+ iconBorderPaint.setColor(bgPaint.getColor());
+ canvas.drawRect(iconRect, iconBorderPaint);
+ }
+
+ float centeredTextOriginY = getRectCenterY(cellRect) + (FontUtils.getFontHeight(textPaint)/2);
+ canvas.drawText(text, iconRect.right + 2, centeredTextOriginY, textPaint);
+ }
+
+ protected void drawRegionLegendIcon(Canvas canvas, RectF rect, XYRegionFormatter formatter) {
+ canvas.drawRect(rect, formatter.getPaint());
+ }
+
+ private void drawRegionLegendCell(Canvas canvas, XYRegionFormatter formatter, RectF cellRect, String text) {
+ RectF iconRect = getIconRect(cellRect);
+ beginDrawingCell(canvas, iconRect);
+
+ drawRegionLegendIcon(
+ canvas,
+ iconRect,
+ formatter
+ );
+ finishDrawingCell(canvas, cellRect, iconRect, text);
+ }
+
+ private void drawSeriesLegendCell(Canvas canvas, XYSeriesRenderer renderer, XYSeriesFormatter formatter, RectF cellRect, String seriesTitle) {
+ RectF iconRect = getIconRect(cellRect);
+ beginDrawingCell(canvas, iconRect);
+
+ renderer.drawSeriesLegendIcon(
+ canvas,
+ iconRect,
+ formatter);
+ finishDrawingCell(canvas, cellRect, iconRect, seriesTitle);
+ }
+
+ @Override
+ protected synchronized void doOnDraw(Canvas canvas, RectF widgetRect) {
+ // TODO: a good amount of iterating could be avoided if
+ // we create a temporary list of all the legend items up here.
+ if(plot.isEmpty()) {
+ return;
+ }
+
+ //Hashtable<XYRegionFormatter, XYSeriesRenderer> regionRendererLookup = new Hashtable<XYRegionFormatter, XYSeriesRenderer>();
+
+ // Keep an alphabetically sorted list of regions:
+ TreeSet<Map.Entry<XYRegionFormatter, String>> sortedRegions = new TreeSet<Map.Entry<XYRegionFormatter, String>>(new RegionEntryComparator());
+
+ // Calculate the number of cells needed to draw the Legend:
+ int seriesCount = 0;
+ for(XYSeriesRenderer renderer : plot.getRendererList()) {
+
+ SeriesAndFormatterList sfl = plot.getSeriesAndFormatterListForRenderer(renderer.getClass());
+ if(sfl != null) {
+ seriesCount += sfl.size();
+ }
+
+ // Figure out how many regions need to be added to the legend:
+ Hashtable<XYRegionFormatter, String> urf = renderer.getUniqueRegionFormatters();
+ /*for(XYRegionFormatter xyf : urf.keySet()) {
+ regionRendererLookup.put(xyf, renderer);
+ }*/
+ sortedRegions.addAll(urf.entrySet());
+ //sortedRegions.addAll(renderer.getUniqueRegionFormatters().entrySet());
+ }
+ seriesCount += sortedRegions.size();
+
+ // Create an iterator specially created to draw the number of cells we calculated:
+ Iterator<RectF> it = tableModel.getIterator(widgetRect, seriesCount);
+
+ RectF cellRect;
+
+ // draw each series legend item:
+ for(XYSeriesRenderer renderer : plot.getRendererList()) {
+ SeriesAndFormatterList<XYSeries,XYSeriesFormatter> sfl = plot.getSeriesAndFormatterListForRenderer(renderer.getClass());
+
+ if (sfl != null) {
+ // maxIndex is only used if it has been determined.
+ // if it is 0 then it could not be determined.
+ for (int i = 0; i < sfl.size() && it.hasNext(); i++) {
+ cellRect = it.next();
+ XYSeriesFormatter formatter = sfl.getFormatter(i);
+ drawSeriesLegendCell(canvas, renderer, formatter, cellRect, sfl.getSeries(i).getTitle());
+ }
+ }
+ }
+
+ // draw each region legend item:
+ for(Map.Entry<XYRegionFormatter, String> entry : sortedRegions) {
+ if(!it.hasNext()) {
+ break;
+ }
+ cellRect = it.next();
+ XYRegionFormatter formatter = entry.getKey();
+ drawRegionLegendCell(canvas, formatter, cellRect, entry.getValue());
+ }
+ }
+
+
+ public Paint getTextPaint() {
+ return textPaint;
+ }
+
+ public void setTextPaint(Paint textPaint) {
+ this.textPaint = textPaint;
+ }
+
+ public boolean isDrawIconBackgroundEnabled() {
+ return drawIconBackgroundEnabled;
+ }
+
+ public void setDrawIconBackgroundEnabled(boolean drawIconBackgroundEnabled) {
+ this.drawIconBackgroundEnabled = drawIconBackgroundEnabled;
+ }
+
+ public boolean isDrawIconBorderEnabled() {
+ return drawIconBorderEnabled;
+ }
+
+ public void setDrawIconBorderEnabled(boolean drawIconBorderEnabled) {
+ this.drawIconBorderEnabled = drawIconBorderEnabled;
+ }
+
+ public TableModel getTableModel() {
+ return tableModel;
+ }
+
+ public SizeMetrics getIconSizeMetrics() {
+ return iconSizeMetrics;
+ }
+
+ /**
+ * Set the size of each legend's icon. Note that when using relative sizing,
+ * the size is calculated against the countaining cell's size, not the plot's size.
+ * @param iconSizeMetrics
+ */
+ public void setIconSizeMetrics(SizeMetrics iconSizeMetrics) {
+ this.iconSizeMetrics = iconSizeMetrics;
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYPlot.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYPlot.java
new file mode 100644
index 0000000..d3c286c
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYPlot.java
@@ -0,0 +1,1344 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.xy;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.PointF;
+import android.util.AttributeSet;
+import com.androidplot.Plot;
+import com.androidplot.ui.*;
+import com.androidplot.ui.TextOrientationType;
+import com.androidplot.ui.widget.TextLabelWidget;
+import com.androidplot.util.PixelUtils;
+
+import java.text.Format;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A View to graphically display x/y coordinates.
+ */
+public class XYPlot extends Plot<XYSeries, XYSeriesFormatter, XYSeriesRenderer> {
+
+ private BoundaryMode domainOriginBoundaryMode;
+ private BoundaryMode rangeOriginBoundaryMode;
+
+ // widgets
+ private XYLegendWidget legendWidget;
+ private XYGraphWidget graphWidget;
+ private TextLabelWidget domainLabelWidget;
+ private TextLabelWidget rangeLabelWidget;
+
+ private XYStepMode domainStepMode = XYStepMode.SUBDIVIDE;
+ private double domainStepValue = 10;
+
+ private XYStepMode rangeStepMode = XYStepMode.SUBDIVIDE;
+ private double rangeStepValue = 10;
+
+ // user settable min/max values
+ private Number userMinX;
+ private Number userMaxX;
+ private Number userMinY;
+ private Number userMaxY;
+
+ // these are the final min/max used for dispplaying data
+ private Number calculatedMinX;
+ private Number calculatedMaxX;
+ private Number calculatedMinY;
+ private Number calculatedMaxY;
+
+ // previous calculated min/max vals.
+ // primarily used for GROW/SHRINK operations.
+ private Number prevMinX;
+ private Number prevMaxX;
+ private Number prevMinY;
+ private Number prevMaxY;
+
+ // uses set boundary min and max values
+ // should be null if not used.
+ private Number rangeTopMin = null;
+ private Number rangeTopMax = null;
+ private Number rangeBottomMin = null;
+ private Number rangeBottomMax = null;
+ private Number domainLeftMin = null;
+ private Number domainLeftMax = null;
+ private Number domainRightMin = null;
+ private Number domainRightMax = null;
+
+ // used for calculating the domain/range extents that will be displayed on the plot.
+ // using boundaries and origins are mutually exclusive. because of this,
+ // setting one will disable the other. when only setting the FramingModel,
+ // the origin or boundary is set to the current value of the plot.
+ private XYFramingModel domainFramingModel = XYFramingModel.EDGE;
+ private XYFramingModel rangeFramingModel = XYFramingModel.EDGE;
+
+ private Number userDomainOrigin;
+ private Number userRangeOrigin;
+
+ private Number calculatedDomainOrigin;
+ private Number calculatedRangeOrigin;
+
+ @SuppressWarnings("FieldCanBeLocal")
+ private Number domainOriginExtent = null;
+ @SuppressWarnings("FieldCanBeLocal")
+ private Number rangeOriginExtent = null;
+
+ private BoundaryMode domainUpperBoundaryMode = BoundaryMode.AUTO;
+ private BoundaryMode domainLowerBoundaryMode = BoundaryMode.AUTO;
+ private BoundaryMode rangeUpperBoundaryMode = BoundaryMode.AUTO;
+ private BoundaryMode rangeLowerBoundaryMode = BoundaryMode.AUTO;
+
+ private boolean drawDomainOriginEnabled = true;
+ private boolean drawRangeOriginEnabled = true;
+
+ private ArrayList<YValueMarker> yValueMarkers;
+ private ArrayList<XValueMarker> xValueMarkers;
+
+ private RectRegion defaultBounds;
+
+
+ private static final int DEFAULT_LEGEND_WIDGET_H_DP = 10;
+ private static final int DEFAULT_LEGEND_WIDGET_ICON_SIZE_DP = 7;
+ private static final int DEFAULT_GRAPH_WIDGET_H_DP = 18;
+ private static final int DEFAULT_GRAPH_WIDGET_W_DP = 10;
+ private static final int DEFAULT_DOMAIN_LABEL_WIDGET_H_DP = 10;
+ private static final int DEFAULT_DOMAIN_LABEL_WIDGET_W_DP = 80;
+ private static final int DEFAULT_RANGE_LABEL_WIDGET_H_DP = 50;
+ private static final int DEFAULT_RANGE_LABEL_WIDGET_W_DP = 10;
+
+ private static final int DEFAULT_LEGEND_WIDGET_Y_OFFSET_DP = 0;
+ private static final int DEFAULT_LEGEND_WIDGET_X_OFFSET_DP = 40;
+ private static final int DEFAULT_GRAPH_WIDGET_Y_OFFSET_DP = 0;
+ private static final int DEFAULT_GRAPH_WIDGET_X_OFFSET_DP = 0;
+ private static final int DEFAULT_DOMAIN_LABEL_WIDGET_Y_OFFSET_DP = 0;
+ private static final int DEFAULT_DOMAIN_LABEL_WIDGET_X_OFFSET_DP = 20;
+ private static final int DEFAULT_RANGE_LABEL_WIDGET_Y_OFFSET_DP = 0;
+ private static final int DEFAULT_RANGE_LABEL_WIDGET_X_OFFSET_DP = 0;
+
+ private static final int DEFAULT_GRAPH_WIDGET_TOP_MARGIN_DP = 3;
+ private static final int DEFAULT_GRAPH_WIDGET_RIGHT_MARGIN_DP = 3;
+ private static final int DEFAULT_PLOT_LEFT_MARGIN_DP = 2;
+ private static final int DEFAULT_PLOT_RIGHT_MARGIN_DP = 2;
+ private static final int DEFAULT_PLOT_BOTTOM_MARGIN_DP = 2;
+
+ public XYPlot(Context context, String title) {
+ super(context, title);
+ }
+
+ public XYPlot(Context context, String title, RenderMode mode) {
+ super(context, title, mode);
+ }
+
+ public XYPlot(Context context, AttributeSet attributes) {
+ super(context, attributes);
+ }
+
+ public XYPlot(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ }
+
+ @Override
+ protected void onPreInit() {
+ legendWidget = new XYLegendWidget(
+ getLayoutManager(),
+ this,
+ new SizeMetrics(
+ PixelUtils.dpToPix(DEFAULT_LEGEND_WIDGET_H_DP),
+ SizeLayoutType.ABSOLUTE, 0.5f, SizeLayoutType.RELATIVE),
+ new DynamicTableModel(0, 1),
+ new SizeMetrics(
+ PixelUtils.dpToPix(DEFAULT_LEGEND_WIDGET_ICON_SIZE_DP),
+ SizeLayoutType.ABSOLUTE,
+ PixelUtils.dpToPix(DEFAULT_LEGEND_WIDGET_ICON_SIZE_DP),
+ SizeLayoutType.ABSOLUTE));
+
+ graphWidget = new XYGraphWidget(
+ getLayoutManager(),
+ this,
+ new SizeMetrics(
+ PixelUtils.dpToPix(DEFAULT_GRAPH_WIDGET_H_DP),
+ SizeLayoutType.FILL,
+ PixelUtils.dpToPix(DEFAULT_GRAPH_WIDGET_W_DP),
+ SizeLayoutType.FILL));
+
+ Paint backgroundPaint = new Paint();
+ backgroundPaint.setColor(Color.DKGRAY);
+ backgroundPaint.setStyle(Paint.Style.FILL);
+ graphWidget.setBackgroundPaint(backgroundPaint);
+
+
+ domainLabelWidget = new TextLabelWidget(
+ getLayoutManager(),
+ new SizeMetrics(
+ PixelUtils.dpToPix(DEFAULT_DOMAIN_LABEL_WIDGET_H_DP),
+ SizeLayoutType.ABSOLUTE,
+ PixelUtils.dpToPix(DEFAULT_DOMAIN_LABEL_WIDGET_W_DP),
+ SizeLayoutType.ABSOLUTE),
+ TextOrientationType.HORIZONTAL);
+ rangeLabelWidget = new TextLabelWidget(
+ getLayoutManager(),
+ new SizeMetrics(
+ PixelUtils.dpToPix(DEFAULT_RANGE_LABEL_WIDGET_H_DP),
+ SizeLayoutType.ABSOLUTE,
+ PixelUtils.dpToPix(DEFAULT_RANGE_LABEL_WIDGET_W_DP),
+ SizeLayoutType.ABSOLUTE),
+ TextOrientationType.VERTICAL_ASCENDING);
+
+ legendWidget.position(
+ PixelUtils.dpToPix(DEFAULT_LEGEND_WIDGET_X_OFFSET_DP),
+ XLayoutStyle.ABSOLUTE_FROM_RIGHT,
+ PixelUtils.dpToPix(DEFAULT_LEGEND_WIDGET_Y_OFFSET_DP),
+ YLayoutStyle.ABSOLUTE_FROM_BOTTOM,
+ AnchorPosition.RIGHT_BOTTOM);
+
+ graphWidget.position(
+ PixelUtils.dpToPix(DEFAULT_GRAPH_WIDGET_X_OFFSET_DP),
+ XLayoutStyle.ABSOLUTE_FROM_RIGHT,
+ PixelUtils.dpToPix(DEFAULT_GRAPH_WIDGET_Y_OFFSET_DP),
+ YLayoutStyle.ABSOLUTE_FROM_CENTER,
+ AnchorPosition.RIGHT_MIDDLE);
+
+ domainLabelWidget.position(
+ PixelUtils.dpToPix(DEFAULT_DOMAIN_LABEL_WIDGET_X_OFFSET_DP),
+ XLayoutStyle.ABSOLUTE_FROM_LEFT,
+ PixelUtils.dpToPix(DEFAULT_DOMAIN_LABEL_WIDGET_Y_OFFSET_DP),
+ YLayoutStyle.ABSOLUTE_FROM_BOTTOM,
+ AnchorPosition.LEFT_BOTTOM);
+
+ rangeLabelWidget.position(
+ PixelUtils.dpToPix(DEFAULT_RANGE_LABEL_WIDGET_X_OFFSET_DP),
+ XLayoutStyle.ABSOLUTE_FROM_LEFT,
+ PixelUtils.dpToPix(DEFAULT_RANGE_LABEL_WIDGET_Y_OFFSET_DP),
+ YLayoutStyle.ABSOLUTE_FROM_CENTER,
+ AnchorPosition.LEFT_MIDDLE);
+
+ getLayoutManager().moveToTop(getTitleWidget());
+ getLayoutManager().moveToTop(getLegendWidget());
+ graphWidget.setMarginTop(PixelUtils.dpToPix(DEFAULT_GRAPH_WIDGET_TOP_MARGIN_DP));
+ graphWidget.setMarginRight(PixelUtils.dpToPix(DEFAULT_GRAPH_WIDGET_RIGHT_MARGIN_DP));
+
+ getDomainLabelWidget().pack();
+ getRangeLabelWidget().pack();
+ setPlotMarginLeft(PixelUtils.dpToPix(DEFAULT_PLOT_LEFT_MARGIN_DP));
+ setPlotMarginRight(PixelUtils.dpToPix(DEFAULT_PLOT_RIGHT_MARGIN_DP));
+ setPlotMarginBottom(PixelUtils.dpToPix(DEFAULT_PLOT_BOTTOM_MARGIN_DP));
+
+ xValueMarkers = new ArrayList<XValueMarker>();
+ yValueMarkers = new ArrayList<YValueMarker>();
+
+ setDefaultBounds(new RectRegion(-1, 1, -1, 1));
+ }
+
+
+ public void setGridPadding(float left, float top, float right, float bottom) {
+ getGraphWidget().setGridPaddingTop(top);
+ getGraphWidget().setGridPaddingBottom(bottom);
+ getGraphWidget().setGridPaddingLeft(left);
+ getGraphWidget().setGridPaddingRight(right);
+ }
+
+ @Override
+ protected void notifyListenersBeforeDraw(Canvas canvas) {
+ super.notifyListenersBeforeDraw(canvas);
+
+ // this call must be AFTER the notify so that if the listener
+ // is a synchronized series, it has the opportunity to
+ // place a read lock on it's data.
+ calculateMinMaxVals();
+ }
+
+ /**
+ * Checks whether the point is within the plot's graph area.
+ *
+ * @param x
+ * @param y
+ * @return
+ */
+ public boolean containsPoint(float x, float y) {
+ if (getGraphWidget().getGridRect() != null) {
+ return getGraphWidget().getGridRect().contains(x, y);
+ }
+ return false;
+ }
+
+
+ /**
+ * Convenience method - wraps containsPoint(PointF).
+ *
+ * @param point
+ * @return
+ */
+ public boolean containsPoint(PointF point) {
+ return containsPoint(point.x, point.y);
+ }
+
+ public void setCursorPosition(PointF point) {
+ getGraphWidget().setCursorPosition(point);
+ }
+
+ public void setCursorPosition(float x, float y) {
+ getGraphWidget().setCursorPosition(x, y);
+ }
+
+ public Number getYVal(PointF point) {
+ return getGraphWidget().getYVal(point);
+ }
+
+ public Number getXVal(PointF point) {
+ return getGraphWidget().getXVal(point);
+ }
+
+ private boolean isXValWithinView(double xVal) {
+ return (userMinY == null || xVal >= userMinY.doubleValue()) &&
+ userMaxY == null || xVal <= userMaxY.doubleValue();
+ }
+
+ private boolean isPointVisible(Number x, Number y) {
+ // values without both an x and y val arent visible
+ if (x == null || y == null) {
+ return false;
+ }
+ return isValWithinRange(y.doubleValue(), userMinY, userMaxY) &&
+ isValWithinRange(x.doubleValue(), userMinX, userMaxX);
+ }
+
+ private boolean isValWithinRange(double val, Number min, Number max) {
+ boolean isAboveMinThreshold = min == null || val >= min.doubleValue();
+ boolean isBelowMaxThreshold = max == null || val <= max.doubleValue();
+ return isAboveMinThreshold &&
+ isBelowMaxThreshold;
+ }
+
+ public void calculateMinMaxVals() {
+ prevMinX = calculatedMinX;
+ prevMaxX = calculatedMaxX;
+ prevMinY = calculatedMinY;
+ prevMaxY = calculatedMaxY;
+
+ calculatedMinX = userMinX;
+ calculatedMaxX = userMaxX;
+ calculatedMinY = userMinY;
+ calculatedMaxY = userMaxY;
+
+ // next we go through each series to update our min/max values:
+ for (final XYSeries series : getSeriesSet()) {
+ // step through each point in each series:
+ for (int i = 0; i < series.size(); i++) {
+ Number thisX = series.getX(i);
+ Number thisY = series.getY(i);
+ if (isPointVisible(thisX, thisY)) {
+ // only calculate if a static value has not been set:
+ if (userMinX == null) {
+ if (thisX != null && (calculatedMinX == null ||
+ thisX.doubleValue() < calculatedMinX.doubleValue())) {
+ calculatedMinX = thisX;
+ }
+ }
+
+ if (userMaxX == null) {
+ if (thisX != null && (calculatedMaxX == null ||
+ thisX.doubleValue() > calculatedMaxX.doubleValue())) {
+ calculatedMaxX = thisX;
+ }
+ }
+
+ if (userMinY == null) {
+ if (thisY != null && (calculatedMinY == null ||
+ thisY.doubleValue() < calculatedMinY.doubleValue())) {
+ calculatedMinY = thisY;
+ }
+ }
+
+ if (userMaxY == null) {
+ if (thisY != null && (calculatedMaxY == null || thisY.doubleValue() > calculatedMaxY.doubleValue())) {
+ calculatedMaxY = thisY;
+ }
+ }
+ }
+ }
+ }
+
+ // at this point we now know what points are going to be visible on our
+ // plot, but we still need to make corrections based on modes being used:
+ // (grow, shrink etc.)
+ switch (domainFramingModel) {
+ case ORIGIN:
+ updateDomainMinMaxForOriginModel();
+ break;
+ case EDGE:
+ updateDomainMinMaxForEdgeModel();
+ calculatedMinX = ApplyUserMinMax(calculatedMinX, domainLeftMin,
+ domainLeftMax);
+ calculatedMaxX = ApplyUserMinMax(calculatedMaxX,
+ domainRightMin, domainRightMax);
+ break;
+ default:
+ throw new UnsupportedOperationException(
+ "Domain Framing Model not yet supported: " + domainFramingModel);
+ }
+
+ switch (rangeFramingModel) {
+ case ORIGIN:
+ updateRangeMinMaxForOriginModel();
+ break;
+ case EDGE:
+ if (getSeriesSet().size() > 0) {
+ updateRangeMinMaxForEdgeModel();
+ calculatedMinY = ApplyUserMinMax(calculatedMinY,
+ rangeBottomMin, rangeBottomMax);
+ calculatedMaxY = ApplyUserMinMax(calculatedMaxY, rangeTopMin,
+ rangeTopMax);
+ }
+ break;
+ default:
+ throw new UnsupportedOperationException(
+ "Range Framing Model not yet supported: " + domainFramingModel);
+ }
+
+ calculatedDomainOrigin = userDomainOrigin != null ? userDomainOrigin : getCalculatedMinX();
+ calculatedRangeOrigin = this.userRangeOrigin != null ? userRangeOrigin : getCalculatedMinY();
+ }
+
+ /**
+ * Should ONLY be called from updateMinMax.
+ * Results are undefined otherwise.
+ */
+ private void updateDomainMinMaxForEdgeModel() {
+ switch (domainUpperBoundaryMode) {
+ case FIXED:
+ break;
+ case AUTO:
+ break;
+ case GROW:
+ if (!(prevMaxX == null || (calculatedMaxX.doubleValue() > prevMaxX.doubleValue()))) {
+ calculatedMaxX = prevMaxX;
+ }
+ break;
+ case SHRINNK:
+ if (!(prevMaxX == null || calculatedMaxX.doubleValue() < prevMaxX.doubleValue())) {
+ calculatedMaxX = prevMaxX;
+ }
+ break;
+ default:
+ throw new UnsupportedOperationException(
+ "DomainUpperBoundaryMode not yet implemented: " + domainUpperBoundaryMode);
+ }
+
+ switch (domainLowerBoundaryMode) {
+ case FIXED:
+ break;
+ case AUTO:
+ break;
+ case GROW:
+ if (!(prevMinX == null || calculatedMinX.doubleValue() < prevMinX.doubleValue())) {
+ calculatedMinX = prevMinX;
+ }
+ break;
+ case SHRINNK:
+ if (!(prevMinX == null || calculatedMinX.doubleValue() > prevMinX.doubleValue())) {
+ calculatedMinX = prevMinX;
+ }
+ break;
+ default:
+ throw new UnsupportedOperationException(
+ "DomainLowerBoundaryMode not supported: " + domainLowerBoundaryMode);
+ }
+ }
+
+ public void updateRangeMinMaxForEdgeModel() {
+ switch (rangeUpperBoundaryMode) {
+ case FIXED:
+ break;
+ case AUTO:
+ break;
+ case GROW:
+ if (!(prevMaxY == null || calculatedMaxY.doubleValue() > prevMaxY.doubleValue())) {
+ calculatedMaxY = prevMaxY;
+ }
+ break;
+ case SHRINNK:
+ if (!(prevMaxY == null || calculatedMaxY.doubleValue() < prevMaxY.doubleValue())) {
+ calculatedMaxY = prevMaxY;
+ }
+ break;
+ default:
+ throw new UnsupportedOperationException(
+ "RangeUpperBoundaryMode not supported: " + rangeUpperBoundaryMode);
+ }
+
+ switch (rangeLowerBoundaryMode) {
+ case FIXED:
+ break;
+ case AUTO:
+ break;
+ case GROW:
+ if (!(prevMinY == null || calculatedMinY.doubleValue() < prevMinY.doubleValue())) {
+ calculatedMinY = prevMinY;
+ }
+ break;
+ case SHRINNK:
+ if (!(prevMinY == null || calculatedMinY.doubleValue() > prevMinY.doubleValue())) {
+ calculatedMinY = prevMinY;
+ }
+ break;
+ default:
+ throw new UnsupportedOperationException(
+ "RangeLowerBoundaryMode not supported: " + rangeLowerBoundaryMode);
+ }
+ }
+
+ /**
+ * Apply user supplied min and max to the calculated boundary value.
+ *
+ * @param value
+ * @param min
+ * @param max
+ */
+ private Number ApplyUserMinMax(Number value, Number min, Number max) {
+ value = (((min == null) || (value.doubleValue() > min.doubleValue()))
+ ? value
+ : min);
+ value = (((max == null) || (value.doubleValue() < max.doubleValue()))
+ ? value
+ : max);
+ return value;
+ }
+
+ /**
+ * Centers the domain axis on origin.
+ *
+ * @param origin
+ */
+ public void centerOnDomainOrigin(Number origin) {
+ centerOnDomainOrigin(origin, null, BoundaryMode.AUTO);
+ }
+
+ /**
+ * Centers the domain on origin, calculating the upper and lower boundaries of the axis
+ * using mode and extent.
+ *
+ * @param origin
+ * @param extent
+ * @param mode
+ */
+ public void centerOnDomainOrigin(Number origin, Number extent, BoundaryMode mode) {
+ if (origin == null) {
+ throw new NullPointerException("Origin param cannot be null.");
+ }
+ domainFramingModel = XYFramingModel.ORIGIN;
+ setUserDomainOrigin(origin);
+ domainOriginExtent = extent;
+ domainOriginBoundaryMode = mode;
+
+ if (domainOriginBoundaryMode == BoundaryMode.FIXED) {
+ double domO = userDomainOrigin.doubleValue();
+ double domE = domainOriginExtent.doubleValue();
+ userMaxX = domO + domE;
+ userMinX = domO - domE;
+ } else {
+ userMaxX = null;
+ userMinX = null;
+ }
+ }
+
+ /**
+ * Centers the range axis on origin.
+ *
+ * @param origin
+ */
+ public void centerOnRangeOrigin(Number origin) {
+ centerOnRangeOrigin(origin, null, BoundaryMode.AUTO);
+ }
+
+ /**
+ * Centers the domain on origin, calculating the upper and lower boundaries of the axis
+ * using mode and extent.
+ *
+ * @param origin
+ * @param extent
+ * @param mode
+ */
+ @SuppressWarnings("SameParameterValue")
+ public void centerOnRangeOrigin(Number origin, Number extent, BoundaryMode mode) {
+ if (origin == null) {
+ throw new NullPointerException("Origin param cannot be null.");
+ }
+ rangeFramingModel = XYFramingModel.ORIGIN;
+ setUserRangeOrigin(origin);
+ rangeOriginExtent = extent;
+ rangeOriginBoundaryMode = mode;
+
+ if (rangeOriginBoundaryMode == BoundaryMode.FIXED) {
+ double raO = userRangeOrigin.doubleValue();
+ double raE = rangeOriginExtent.doubleValue();
+ userMaxY = raO + raE;
+ userMinY = raO - raE;
+ } else {
+ userMaxY = null;
+ userMinY = null;
+ }
+ }
+
+ /**
+ * Returns the distance between x and y.
+ * Result is never a negative number.
+ *
+ * @param x
+ * @param y
+ * @return
+ */
+ private double distance(double x, double y) {
+ if (x > y) {
+ return x - y;
+ } else {
+ return y - x;
+ }
+ }
+
+ public void updateDomainMinMaxForOriginModel() {
+ double origin = userDomainOrigin.doubleValue();
+ double maxXDelta = distance(calculatedMaxX.doubleValue(), origin);
+ double minXDelta = distance(calculatedMinX.doubleValue(), origin);
+ double delta = maxXDelta > minXDelta ? maxXDelta : minXDelta;
+ double dlb = origin - delta;
+ double dub = origin + delta;
+ switch (domainOriginBoundaryMode) {
+ case AUTO:
+ calculatedMinX = dlb;
+ calculatedMaxX = dub;
+
+ break;
+ // if fixed, then the value already exists within "user" vals.
+ case FIXED:
+ break;
+ case GROW: {
+
+ if (prevMinX == null || dlb < prevMinX.doubleValue()) {
+ calculatedMinX = dlb;
+ } else {
+ calculatedMinX = prevMinX;
+ }
+
+ if (prevMaxX == null || dub > prevMaxX.doubleValue()) {
+ calculatedMaxX = dub;
+ } else {
+ calculatedMaxX = prevMaxX;
+ }
+ }
+ break;
+ case SHRINNK:
+ if (prevMinX == null || dlb > prevMinX.doubleValue()) {
+ calculatedMinX = dlb;
+ } else {
+ calculatedMinX = prevMinX;
+ }
+
+ if (prevMaxX == null || dub < prevMaxX.doubleValue()) {
+ calculatedMaxX = dub;
+ } else {
+ calculatedMaxX = prevMaxX;
+ }
+ break;
+ default:
+ throw new UnsupportedOperationException("Domain Origin Boundary Mode not yet supported: " + domainOriginBoundaryMode);
+ }
+ }
+
+ public void updateRangeMinMaxForOriginModel() {
+ switch (rangeOriginBoundaryMode) {
+ case AUTO:
+ double origin = userRangeOrigin.doubleValue();
+ double maxYDelta = distance(calculatedMaxY.doubleValue(), origin);
+ double minYDelta = distance(calculatedMinY.doubleValue(), origin);
+ if (maxYDelta > minYDelta) {
+ calculatedMinY = origin - maxYDelta;
+ calculatedMaxY = origin + maxYDelta;
+ } else {
+ calculatedMinY = origin - minYDelta;
+ calculatedMaxY = origin + minYDelta;
+ }
+ break;
+ case FIXED:
+ case GROW:
+ case SHRINNK:
+ default:
+ throw new UnsupportedOperationException(
+ "Range Origin Boundary Mode not yet supported: " + rangeOriginBoundaryMode);
+ }
+ }
+
+ /**
+ * Convenience method - wraps XYGraphWidget.getTicksPerRangeLabel().
+ * Equivalent to getGraphWidget().getTicksPerRangeLabel().
+ *
+ * @return
+ */
+ public int getTicksPerRangeLabel() {
+ return graphWidget.getTicksPerRangeLabel();
+ }
+
+ /**
+ * Convenience method - wraps XYGraphWidget.setTicksPerRangeLabel().
+ * Equivalent to getGraphWidget().setTicksPerRangeLabel().
+ *
+ * @param ticksPerRangeLabel
+ */
+ public void setTicksPerRangeLabel(int ticksPerRangeLabel) {
+ graphWidget.setTicksPerRangeLabel(ticksPerRangeLabel);
+ }
+
+ /**
+ * Convenience method - wraps XYGraphWidget.getTicksPerDomainLabel().
+ * Equivalent to getGraphWidget().getTicksPerDomainLabel().
+ *
+ * @return
+ */
+ public int getTicksPerDomainLabel() {
+ return graphWidget.getTicksPerDomainLabel();
+ }
+
+ /**
+ * Convenience method - wraps XYGraphWidget.setTicksPerDomainLabel().
+ * Equivalent to getGraphWidget().setTicksPerDomainLabel().
+ *
+ * @param ticksPerDomainLabel
+ */
+ public void setTicksPerDomainLabel(int ticksPerDomainLabel) {
+ graphWidget.setTicksPerDomainLabel(ticksPerDomainLabel);
+ }
+
+ public XYStepMode getDomainStepMode() {
+ return domainStepMode;
+ }
+
+ public void setDomainStepMode(XYStepMode domainStepMode) {
+ this.domainStepMode = domainStepMode;
+ }
+
+ public double getDomainStepValue() {
+ return domainStepValue;
+ }
+
+ public void setDomainStepValue(double domainStepValue) {
+ this.domainStepValue = domainStepValue;
+ }
+
+ public void setDomainStep(XYStepMode mode, double value) {
+ setDomainStepMode(mode);
+ setDomainStepValue(value);
+ }
+
+ public XYStepMode getRangeStepMode() {
+ return rangeStepMode;
+ }
+
+ public void setRangeStepMode(XYStepMode rangeStepMode) {
+ this.rangeStepMode = rangeStepMode;
+ }
+
+ public double getRangeStepValue() {
+ return rangeStepValue;
+ }
+
+ public void setRangeStepValue(double rangeStepValue) {
+ this.rangeStepValue = rangeStepValue;
+ }
+
+ public void setRangeStep(XYStepMode mode, double value) {
+ setRangeStepMode(mode);
+ setRangeStepValue(value);
+ }
+
+ public String getDomainLabel() {
+ return getDomainLabelWidget().getText();
+ }
+
+ public void setDomainLabel(String domainLabel) {
+ getDomainLabelWidget().setText(domainLabel);
+ }
+
+ public String getRangeLabel() {
+ return getRangeLabelWidget().getText();
+ }
+
+ public void setRangeLabel(String rangeLabel) {
+ getRangeLabelWidget().setText(rangeLabel);
+ }
+
+ public XYLegendWidget getLegendWidget() {
+ return legendWidget;
+ }
+
+ public void setLegendWidget(XYLegendWidget legendWidget) {
+ this.legendWidget = legendWidget;
+ }
+
+ public XYGraphWidget getGraphWidget() {
+ return graphWidget;
+ }
+
+ public void setGraphWidget(XYGraphWidget graphWidget) {
+ this.graphWidget = graphWidget;
+ }
+
+ public TextLabelWidget getDomainLabelWidget() {
+ return domainLabelWidget;
+ }
+
+ public void setDomainLabelWidget(TextLabelWidget domainLabelWidget) {
+ this.domainLabelWidget = domainLabelWidget;
+ }
+
+ public TextLabelWidget getRangeLabelWidget() {
+ return rangeLabelWidget;
+ }
+
+ public void setRangeLabelWidget(TextLabelWidget rangeLabelWidget) {
+ this.rangeLabelWidget = rangeLabelWidget;
+ }
+
+ /**
+ * Convenience method - wraps XYGraphWidget.getRangeValueFormat().
+ *
+ * @return
+ */
+ public Format getRangeValueFormat() {
+ return graphWidget.getRangeValueFormat();
+ }
+
+ /**
+ * Convenience method - wraps XYGraphWidget.setRangeValueFormat().
+ *
+ * @param rangeValueFormat
+ */
+ public void setRangeValueFormat(Format rangeValueFormat) {
+ graphWidget.setRangeValueFormat(rangeValueFormat);
+ }
+
+ /**
+ * Convenience method - wraps XYGraphWidget.getDomainValueFormat().
+ *
+ * @return
+ */
+ public Format getDomainValueFormat() {
+ return graphWidget.getDomainValueFormat();
+ }
+
+ /**
+ * Convenience method - wraps XYGraphWidget.setDomainValueFormat().
+ *
+ * @param domainValueFormat
+ */
+ public void setDomainValueFormat(Format domainValueFormat) {
+ graphWidget.setDomainValueFormat(domainValueFormat);
+ }
+
+ /**
+ * Setup the boundary mode, boundary values only applicable in FIXED mode.
+ *
+ * @param lowerBoundary
+ * @param upperBoundary
+ * @param mode
+ */
+ public synchronized void setDomainBoundaries(Number lowerBoundary, Number upperBoundary, BoundaryMode mode) {
+ setDomainBoundaries(lowerBoundary, mode, upperBoundary, mode);
+ }
+
+ /**
+ * Setup the boundary mode, boundary values only applicable in FIXED mode.
+ *
+ * @param lowerBoundary
+ * @param lowerBoundaryMode
+ * @param upperBoundary
+ * @param upperBoundaryMode
+ */
+ public synchronized void setDomainBoundaries(Number lowerBoundary, BoundaryMode lowerBoundaryMode,
+ Number upperBoundary, BoundaryMode upperBoundaryMode) {
+ setDomainLowerBoundary(lowerBoundary, lowerBoundaryMode);
+ setDomainUpperBoundary(upperBoundary, upperBoundaryMode);
+ }
+
+ /**
+ * Setup the boundary mode, boundary values only applicable in FIXED mode.
+ *
+ * @param lowerBoundary
+ * @param upperBoundary
+ * @param mode
+ */
+ public synchronized void setRangeBoundaries(Number lowerBoundary, Number upperBoundary, BoundaryMode mode) {
+ setRangeBoundaries(lowerBoundary, mode, upperBoundary, mode);
+ }
+
+ /**
+ * Setup the boundary mode, boundary values only applicable in FIXED mode.
+ *
+ * @param lowerBoundary
+ * @param lowerBoundaryMode
+ * @param upperBoundary
+ * @param upperBoundaryMode
+ */
+ public synchronized void setRangeBoundaries(Number lowerBoundary, BoundaryMode lowerBoundaryMode,
+ Number upperBoundary, BoundaryMode upperBoundaryMode) {
+ setRangeLowerBoundary(lowerBoundary, lowerBoundaryMode);
+ setRangeUpperBoundary(upperBoundary, upperBoundaryMode);
+ }
+
+ protected synchronized void setDomainUpperBoundaryMode(BoundaryMode mode) {
+ this.domainUpperBoundaryMode = mode;
+ }
+
+ protected synchronized void setUserMaxX(Number boundary) {
+ // Ifor 12/10/2011
+ // you want null for auto grow and shrink
+ //if(boundary == null) {
+ // throw new NullPointerException("Boundary value cannot be null.");
+ //}
+ this.userMaxX = boundary;
+ }
+
+ /**
+ * Setup the boundary mode, boundary values only applicable in FIXED mode.
+ *
+ * @param boundary
+ * @param mode
+ */
+ public synchronized void setDomainUpperBoundary(Number boundary, BoundaryMode mode) {
+ setUserMaxX((mode == BoundaryMode.FIXED) ? boundary : null);
+ setDomainUpperBoundaryMode(mode);
+ setDomainFramingModel(XYFramingModel.EDGE);
+ }
+
+ protected synchronized void setDomainLowerBoundaryMode(BoundaryMode mode) {
+ this.domainLowerBoundaryMode = mode;
+ }
+
+ protected synchronized void setUserMinX(Number boundary) {
+ // Ifor 12/10/2011
+ // you want null for auto grow and shrink
+ //if(boundary == null) {
+ // throw new NullPointerException("Boundary value cannot be null.");
+ //}
+ this.userMinX = boundary;
+ }
+
+ /**
+ * Setup the boundary mode, boundary values only applicable in FIXED mode.
+ *
+ * @param boundary
+ * @param mode
+ */
+ public synchronized void setDomainLowerBoundary(Number boundary, BoundaryMode mode) {
+ setUserMinX((mode == BoundaryMode.FIXED) ? boundary : null);
+ setDomainLowerBoundaryMode(mode);
+ setDomainFramingModel(XYFramingModel.EDGE);
+ //updateMinMaxVals();
+ }
+
+ protected synchronized void setRangeUpperBoundaryMode(BoundaryMode mode) {
+ this.rangeUpperBoundaryMode = mode;
+ }
+
+ protected synchronized void setUserMaxY(Number boundary) {
+ // Ifor 12/10/2011
+ // you want null for auto grow and shrink
+ //if(boundary == null) {
+ // throw new NullPointerException("Boundary value cannot be null.");
+ //}
+ this.userMaxY = boundary;
+ }
+
+ /**
+ * Setup the boundary mode, boundary values only applicable in FIXED mode.
+ *
+ * @param boundary
+ * @param mode
+ */
+ public synchronized void setRangeUpperBoundary(Number boundary, BoundaryMode mode) {
+ setUserMaxY((mode == BoundaryMode.FIXED) ? boundary : null);
+ setRangeUpperBoundaryMode(mode);
+ setRangeFramingModel(XYFramingModel.EDGE);
+ }
+
+ protected synchronized void setRangeLowerBoundaryMode(BoundaryMode mode) {
+ this.rangeLowerBoundaryMode = mode;
+ }
+
+ protected synchronized void setUserMinY(Number boundary) {
+ // Ifor 12/10/2011
+ // you want null for auto grow and shrink
+ //if(boundary == null) {
+ // throw new NullPointerException("Boundary value cannot be null.");
+ //}
+ this.userMinY = boundary;
+ }
+
+ /**
+ * Setup the boundary mode, boundary values only applicable in FIXED mode.
+ *
+ * @param boundary
+ * @param mode
+ */
+ public synchronized void setRangeLowerBoundary(Number boundary, BoundaryMode mode) {
+ setUserMinY((mode == BoundaryMode.FIXED) ? boundary : null);
+ setRangeLowerBoundaryMode(mode);
+ setRangeFramingModel(XYFramingModel.EDGE);
+ }
+
+ private Number getUserMinX() {
+ return userMinX;
+ }
+
+ private Number getUserMaxX() {
+ return userMaxX;
+ }
+
+ private Number getUserMinY() {
+ return userMinY;
+ }
+
+ private Number getUserMaxY() {
+ return userMaxY;
+ }
+
+ public Number getDomainOrigin() {
+ return calculatedDomainOrigin;
+ }
+
+ public Number getRangeOrigin() {
+ return calculatedRangeOrigin;
+ }
+
+ protected BoundaryMode getDomainUpperBoundaryMode() {
+ return domainUpperBoundaryMode;
+ }
+
+ protected BoundaryMode getDomainLowerBoundaryMode() {
+ return domainLowerBoundaryMode;
+ }
+
+ protected BoundaryMode getRangeUpperBoundaryMode() {
+ return rangeUpperBoundaryMode;
+ }
+
+ protected BoundaryMode getRangeLowerBoundaryMode() {
+ return rangeLowerBoundaryMode;
+ }
+
+ public synchronized void setUserDomainOrigin(Number origin) {
+ if (origin == null) {
+ throw new NullPointerException("Origin value cannot be null.");
+ }
+ this.userDomainOrigin = origin;
+ }
+
+ public synchronized void setUserRangeOrigin(Number origin) {
+ if (origin == null) {
+ throw new NullPointerException("Origin value cannot be null.");
+ }
+ this.userRangeOrigin = origin;
+ }
+
+ public XYFramingModel getDomainFramingModel() {
+ return domainFramingModel;
+ }
+
+ @SuppressWarnings("SameParameterValue")
+ protected void setDomainFramingModel(XYFramingModel domainFramingModel) {
+ this.domainFramingModel = domainFramingModel;
+ }
+
+ public XYFramingModel getRangeFramingModel() {
+
+ return rangeFramingModel;
+ }
+
+ @SuppressWarnings("SameParameterValue")
+ protected void setRangeFramingModel(XYFramingModel rangeFramingModel) {
+ this.rangeFramingModel = rangeFramingModel;
+ }
+
+ /**
+ * CalculatedMinX value after the the framing model has been applied.
+ *
+ * @return
+ */
+ public Number getCalculatedMinX() {
+ return calculatedMinX != null ? calculatedMinX : getDefaultBounds().getMinX();
+ }
+
+ /**
+ * CalculatedMaxX value after the the framing model has been applied.
+ *
+ * @return
+ */
+ public Number getCalculatedMaxX() {
+ return calculatedMaxX != null ? calculatedMaxX : getDefaultBounds().getMaxX();
+ }
+
+ /**
+ * CalculatedMinY value after the the framing model has been applied.
+ *
+ * @return
+ */
+ public Number getCalculatedMinY() {
+ return calculatedMinY != null ? calculatedMinY : getDefaultBounds().getMinY();
+ }
+
+ /**
+ * CalculatedMaxY value after the the framing model has been applied.
+ *
+ * @return
+ */
+ public Number getCalculatedMaxY() {
+ return calculatedMaxY != null ? calculatedMaxY : getDefaultBounds().getMaxY();
+ }
+
+ public boolean isDrawDomainOriginEnabled() {
+ return drawDomainOriginEnabled;
+ }
+
+ public void setDrawDomainOriginEnabled(boolean drawDomainOriginEnabled) {
+ this.drawDomainOriginEnabled = drawDomainOriginEnabled;
+ }
+
+ public boolean isDrawRangeOriginEnabled() {
+ return drawRangeOriginEnabled;
+ }
+
+ public void setDrawRangeOriginEnabled(boolean drawRangeOriginEnabled) {
+ this.drawRangeOriginEnabled = drawRangeOriginEnabled;
+ }
+
+ /**
+ * Appends the specified marker to the end of plot's yValueMarkers list.
+ *
+ * @param marker The YValueMarker to be added.
+ * @return true if the object was successfully added, false otherwise.
+ */
+ public boolean addMarker(YValueMarker marker) {
+ if (yValueMarkers.contains(marker)) {
+ return false;
+ } else {
+ return yValueMarkers.add(marker);
+ }
+ }
+
+ /**
+ * Removes the specified marker from the plot.
+ *
+ * @param marker
+ * @return The YValueMarker removed if successfull, null otherwise.
+ */
+ public YValueMarker removeMarker(YValueMarker marker) {
+ int markerIndex = yValueMarkers.indexOf(marker);
+ if (markerIndex == -1) {
+ return null;
+ } else {
+ return yValueMarkers.remove(markerIndex);
+ }
+ }
+
+ /**
+ * Convenience method - combines removeYMarkers() and removeXMarkers().
+ *
+ * @return
+ */
+ public int removeMarkers() {
+ int removed = removeXMarkers();
+ removed += removeYMarkers();
+ return removed;
+ }
+
+ /**
+ * Removes all YValueMarker instances from the plot.
+ *
+ * @return
+ */
+ public int removeYMarkers() {
+ int numMarkersRemoved = yValueMarkers.size();
+ yValueMarkers.clear();
+ return numMarkersRemoved;
+ }
+
+ /**
+ * Appends the specified marker to the end of plot's xValueMarkers list.
+ *
+ * @param marker The XValueMarker to be added.
+ * @return true if the object was successfully added, false otherwise.
+ */
+ public boolean addMarker(XValueMarker marker) {
+ return !xValueMarkers.contains(marker) && xValueMarkers.add(marker);
+ }
+
+ /**
+ * Removes the specified marker from the plot.
+ *
+ * @param marker
+ * @return The XValueMarker removed if successfull, null otherwise.
+ */
+ public XValueMarker removeMarker(XValueMarker marker) {
+ int markerIndex = xValueMarkers.indexOf(marker);
+ if (markerIndex == -1) {
+ return null;
+ } else {
+ return xValueMarkers.remove(markerIndex);
+ }
+ }
+
+ /**
+ * Removes all XValueMarker instances from the plot.
+ *
+ * @return
+ */
+ public int removeXMarkers() {
+ int numMarkersRemoved = xValueMarkers.size();
+ xValueMarkers.clear();
+ return numMarkersRemoved;
+ }
+
+ protected List<YValueMarker> getYValueMarkers() {
+ return yValueMarkers;
+ }
+
+ protected List<XValueMarker> getXValueMarkers() {
+ return xValueMarkers;
+ }
+
+ public RectRegion getDefaultBounds() {
+ return defaultBounds;
+ }
+
+ public void setDefaultBounds(RectRegion defaultBounds) {
+ this.defaultBounds = defaultBounds;
+ }
+
+ /**
+ * @return the rangeTopMin
+ */
+ public Number getRangeTopMin() {
+ return rangeTopMin;
+ }
+
+ /**
+ * @param rangeTopMin the rangeTopMin to set
+ */
+ public synchronized void setRangeTopMin(Number rangeTopMin) {
+ this.rangeTopMin = rangeTopMin;
+ }
+
+ /**
+ * @return the rangeTopMax
+ */
+ public Number getRangeTopMax() {
+ return rangeTopMax;
+ }
+
+ /**
+ * @param rangeTopMax the rangeTopMax to set
+ */
+ public synchronized void setRangeTopMax(Number rangeTopMax) {
+ this.rangeTopMax = rangeTopMax;
+ }
+
+ /**
+ * @return the rangeBottomMin
+ */
+ public Number getRangeBottomMin() {
+ return rangeBottomMin;
+ }
+
+ /**
+ * @param rangeBottomMin the rangeBottomMin to set
+ */
+ public synchronized void setRangeBottomMin(Number rangeBottomMin) {
+ this.rangeBottomMin = rangeBottomMin;
+ }
+
+ /**
+ * @return the rangeBottomMax
+ */
+ public Number getRangeBottomMax() {
+ return rangeBottomMax;
+ }
+
+ /**
+ * @param rangeBottomMax the rangeBottomMax to set
+ */
+ public synchronized void setRangeBottomMax(Number rangeBottomMax) {
+ this.rangeBottomMax = rangeBottomMax;
+ }
+
+ /**
+ * @return the domainLeftMin
+ */
+ public Number getDomainLeftMin() {
+ return domainLeftMin;
+ }
+
+ /**
+ * @param domainLeftMin the domainLeftMin to set
+ */
+ public synchronized void setDomainLeftMin(Number domainLeftMin) {
+ this.domainLeftMin = domainLeftMin;
+ }
+
+ /**
+ * @return the domainLeftMax
+ */
+ public Number getDomainLeftMax() {
+ return domainLeftMax;
+ }
+
+ /**
+ * @param domainLeftMax the domainLeftMax to set
+ */
+ public synchronized void setDomainLeftMax(Number domainLeftMax) {
+ this.domainLeftMax = domainLeftMax;
+ }
+
+ /**
+ * @return the domainRightMin
+ */
+ public Number getDomainRightMin() {
+ return domainRightMin;
+ }
+
+ /**
+ * @param domainRightMin the domainRightMin to set
+ */
+ public synchronized void setDomainRightMin(Number domainRightMin) {
+ this.domainRightMin = domainRightMin;
+ }
+
+ /**
+ * @return the domainRightMax
+ */
+ public Number getDomainRightMax() {
+ return domainRightMax;
+ }
+
+ /**
+ * @param domainRightMax the domainRightMax to set
+ */
+ public synchronized void setDomainRightMax(Number domainRightMax) {
+ this.domainRightMax = domainRightMax;
+ }
+}
\ No newline at end of file
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYPlotZoomPan.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYPlotZoomPan.java
new file mode 100644
index 0000000..e1064dc
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYPlotZoomPan.java
@@ -0,0 +1,384 @@
+package com.androidplot.xy;
+
+import android.content.Context;
+import android.graphics.PointF;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnTouchListener;
+
+public class XYPlotZoomPan extends XYPlot implements OnTouchListener {
+ private static final float MIN_DIST_2_FING = 5f;
+
+ // Definition of the touch states
+ private enum State
+ {
+ NONE,
+ ONE_FINGER_DRAG,
+ TWO_FINGERS_DRAG
+ }
+
+ private State mode = State.NONE;
+ private float minXLimit = Float.MAX_VALUE;
+ private float maxXLimit = Float.MAX_VALUE;
+ private float minYLimit = Float.MAX_VALUE;
+ private float maxYLimit = Float.MAX_VALUE;
+ private float lastMinX = Float.MAX_VALUE;
+ private float lastMaxX = Float.MAX_VALUE;
+ private float lastMinY = Float.MAX_VALUE;
+ private float lastMaxY = Float.MAX_VALUE;
+ private PointF firstFingerPos;
+ private float mDistX;
+ private boolean mZoomEnabled; //default is enabled
+ private boolean mZoomVertically;
+ private boolean mZoomHorizontally;
+ private boolean mCalledBySelf;
+ private boolean mZoomEnabledInit;
+ private boolean mZoomVerticallyInit;
+ private boolean mZoomHorizontallyInit;
+
+ public XYPlotZoomPan(Context context, String title, RenderMode mode) {
+ super(context, title, mode);
+ setZoomEnabled(true); //Default is ZoomEnabled if instantiated programmatically
+ }
+
+ public XYPlotZoomPan(final Context context, final AttributeSet attrs) {
+ super(context, attrs);
+ if(mZoomEnabled || !mZoomEnabledInit) {
+ setZoomEnabled(true);
+ }
+ if(!mZoomHorizontallyInit) {
+ mZoomHorizontally = true;
+ }
+ if(!mZoomVerticallyInit) {
+ mZoomVertically = true;
+ }
+ }
+
+ public XYPlotZoomPan(final Context context, final AttributeSet attrs, final int defStyle) {
+ super(context, attrs, defStyle);
+ if(mZoomEnabled || !mZoomEnabledInit) {
+ setZoomEnabled(true);
+ }
+ if(!mZoomHorizontallyInit) {
+ mZoomHorizontally = true;
+ }
+ if(!mZoomVerticallyInit) {
+ mZoomVertically = true;
+ }
+ }
+
+ public XYPlotZoomPan(final Context context, final String title) {
+ super(context, title);
+ }
+
+ @Override
+ public void setOnTouchListener(OnTouchListener l) {
+ if(l != this) {
+ mZoomEnabled = false;
+ }
+ super.setOnTouchListener(l);
+ }
+
+ public boolean getZoomVertically() {
+ return mZoomVertically;
+ }
+
+ public void setZoomVertically(boolean zoomVertically) {
+ mZoomVertically = zoomVertically;
+ mZoomVerticallyInit = true;
+ }
+
+ public boolean getZoomHorizontally() {
+ return mZoomHorizontally;
+ }
+
+ public void setZoomHorizontally(boolean zoomHorizontally) {
+ mZoomHorizontally = zoomHorizontally;
+ mZoomHorizontallyInit = true;
+ }
+
+ public void setZoomEnabled(boolean enabled) {
+ if(enabled) {
+ setOnTouchListener(this);
+ } else {
+ setOnTouchListener(null);
+ }
+ mZoomEnabled = enabled;
+ mZoomEnabledInit = true;
+ }
+
+ public boolean getZoomEnabled() {
+ return mZoomEnabled;
+ }
+
+ private float getMinXLimit() {
+ if(minXLimit == Float.MAX_VALUE) {
+ minXLimit = getCalculatedMinX().floatValue();
+ lastMinX = minXLimit;
+ }
+ return minXLimit;
+ }
+
+ private float getMaxXLimit() {
+ if(maxXLimit == Float.MAX_VALUE) {
+ maxXLimit = getCalculatedMaxX().floatValue();
+ lastMaxX = maxXLimit;
+ }
+ return maxXLimit;
+ }
+
+ private float getMinYLimit() {
+ if(minYLimit == Float.MAX_VALUE) {
+ minYLimit = getCalculatedMinY().floatValue();
+ lastMinY = minYLimit;
+ }
+ return minYLimit;
+ }
+
+ private float getMaxYLimit() {
+ if(maxYLimit == Float.MAX_VALUE) {
+ maxYLimit = getCalculatedMaxY().floatValue();
+ lastMaxY = maxYLimit;
+ }
+ return maxYLimit;
+ }
+
+ private float getLastMinX() {
+ if(lastMinX == Float.MAX_VALUE) {
+ lastMinX = getCalculatedMinX().floatValue();
+ }
+ return lastMinX;
+ }
+
+ private float getLastMaxX() {
+ if(lastMaxX == Float.MAX_VALUE) {
+ lastMaxX = getCalculatedMaxX().floatValue();
+ }
+ return lastMaxX;
+ }
+
+ private float getLastMinY() {
+ if(lastMinY == Float.MAX_VALUE) {
+ lastMinY = getCalculatedMinY().floatValue();
+ }
+ return lastMinY;
+ }
+
+ private float getLastMaxY() {
+ if(lastMaxY == Float.MAX_VALUE) {
+ lastMaxY = getCalculatedMaxY().floatValue();
+ }
+ return lastMaxY;
+ }
+
+ @Override
+ public synchronized void setDomainBoundaries(final Number lowerBoundary, final BoundaryMode lowerBoundaryMode, final Number upperBoundary, final BoundaryMode upperBoundaryMode) {
+ super.setDomainBoundaries(lowerBoundary, lowerBoundaryMode, upperBoundary, upperBoundaryMode);
+ if(mCalledBySelf) {
+ mCalledBySelf = false;
+ } else {
+ minXLimit = lowerBoundaryMode == BoundaryMode.FIXED ? lowerBoundary.floatValue() : getCalculatedMinX().floatValue();
+ maxXLimit = upperBoundaryMode == BoundaryMode.FIXED ? upperBoundary.floatValue() : getCalculatedMaxX().floatValue();
+ lastMinX = minXLimit;
+ lastMaxX = maxXLimit;
+ }
+ }
+
+ @Override
+ public synchronized void setRangeBoundaries(final Number lowerBoundary, final BoundaryMode lowerBoundaryMode, final Number upperBoundary, final BoundaryMode upperBoundaryMode) {
+ super.setRangeBoundaries(lowerBoundary, lowerBoundaryMode, upperBoundary, upperBoundaryMode);
+ if(mCalledBySelf) {
+ mCalledBySelf = false;
+ } else {
+ minYLimit = lowerBoundaryMode == BoundaryMode.FIXED ? lowerBoundary.floatValue() : getCalculatedMinY().floatValue();
+ maxYLimit = upperBoundaryMode == BoundaryMode.FIXED ? upperBoundary.floatValue() : getCalculatedMaxY().floatValue();
+ lastMinY = minYLimit;
+ lastMaxY = maxYLimit;
+ }
+ }
+
+ @Override
+ public synchronized void setDomainBoundaries(final Number lowerBoundary, final Number upperBoundary, final BoundaryMode mode) {
+ super.setDomainBoundaries(lowerBoundary, upperBoundary, mode);
+ if(mCalledBySelf) {
+ mCalledBySelf = false;
+ } else {
+ minXLimit = mode == BoundaryMode.FIXED ? lowerBoundary.floatValue() : getCalculatedMinX().floatValue();
+ maxXLimit = mode == BoundaryMode.FIXED ? upperBoundary.floatValue() : getCalculatedMaxX().floatValue();
+ lastMinX = minXLimit;
+ lastMaxX = maxXLimit;
+ }
+ }
+
+ @Override
+ public synchronized void setRangeBoundaries(final Number lowerBoundary, final Number upperBoundary, final BoundaryMode mode) {
+ super.setRangeBoundaries(lowerBoundary, upperBoundary, mode);
+ if(mCalledBySelf) {
+ mCalledBySelf = false;
+ } else {
+ minYLimit = mode == BoundaryMode.FIXED ? lowerBoundary.floatValue() : getCalculatedMinY().floatValue();
+ maxYLimit = mode == BoundaryMode.FIXED ? upperBoundary.floatValue() : getCalculatedMaxY().floatValue();
+ lastMinY = minYLimit;
+ lastMaxY = maxYLimit;
+ }
+ }
+
+ @Override
+ public boolean onTouch(final View view, final MotionEvent event) {
+ switch (event.getAction() & MotionEvent.ACTION_MASK)
+ {
+ case MotionEvent.ACTION_DOWN: // start gesture
+ firstFingerPos = new PointF(event.getX(), event.getY());
+ mode = State.ONE_FINGER_DRAG;
+ break;
+ case MotionEvent.ACTION_POINTER_DOWN: // second finger
+ {
+ mDistX = getXDistance(event);
+ // the distance check is done to avoid false alarms
+ if(mDistX > MIN_DIST_2_FING || mDistX < -MIN_DIST_2_FING) {
+ mode = State.TWO_FINGERS_DRAG;
+ }
+ break;
+ }
+ case MotionEvent.ACTION_POINTER_UP: // end zoom
+ mode = State.NONE;
+ break;
+ case MotionEvent.ACTION_MOVE:
+ if(mode == State.ONE_FINGER_DRAG) {
+ pan(event);
+ } else if(mode == State.TWO_FINGERS_DRAG) {
+ zoom(event);
+ }
+ break;
+ }
+ return true;
+ }
+
+ private float getXDistance(final MotionEvent event) {
+ return event.getX(0) - event.getX(1);
+ }
+
+ private void pan(final MotionEvent motionEvent) {
+ final PointF oldFirstFinger = firstFingerPos; //save old position of finger
+ firstFingerPos = new PointF(motionEvent.getX(), motionEvent.getY()); //update finger position
+ PointF newX = new PointF();
+ if(mZoomHorizontally) {
+ calculatePan(oldFirstFinger, newX, true);
+ mCalledBySelf = true;
+ super.setDomainBoundaries(newX.x, newX.y, BoundaryMode.FIXED);
+ lastMinX = newX.x;
+ lastMaxX = newX.y;
+ }
+ if(mZoomVertically) {
+ calculatePan(oldFirstFinger, newX, false);
+ mCalledBySelf = true;
+ super.setRangeBoundaries(newX.x, newX.y, BoundaryMode.FIXED);
+ lastMinY = newX.x;
+ lastMaxY = newX.y;
+ }
+ redraw();
+ }
+
+ private void calculatePan(final PointF oldFirstFinger, PointF newX, final boolean horizontal) {
+ final float offset;
+ // multiply the absolute finger movement for a factor.
+ // the factor is dependent on the calculated min and max
+ if(horizontal) {
+ newX.x = getLastMinX();
+ newX.y = getLastMaxX();
+ offset = (oldFirstFinger.x - firstFingerPos.x) * ((newX.y - newX.x) / getWidth());
+ } else {
+ newX.x = getLastMinY();
+ newX.y = getLastMaxY();
+ offset = -(oldFirstFinger.y - firstFingerPos.y) * ((newX.y - newX.x) / getHeight());
+ }
+ // move the calculated offset
+ newX.x = newX.x + offset;
+ newX.y = newX.y + offset;
+ //get the distance between max and min
+ final float diff = newX.y - newX.x;
+ //check if we reached the limit of panning
+ if(horizontal) {
+ if(newX.x < getMinXLimit()) {
+ newX.x = getMinXLimit();
+ newX.y = newX.x + diff;
+ }
+ if(newX.y > getMaxXLimit()) {
+ newX.y = getMaxXLimit();
+ newX.x = newX.y - diff;
+ }
+ } else {
+ if(newX.x < getMinYLimit()) {
+ newX.x = getMinYLimit();
+ newX.y = newX.x + diff;
+ }
+ if(newX.y > getMaxYLimit()) {
+ newX.y = getMaxYLimit();
+ newX.x = newX.y - diff;
+ }
+ }
+ }
+
+ private void zoom(final MotionEvent motionEvent) {
+ final float oldDist = mDistX;
+ final float newDist = getXDistance(motionEvent);
+ // sign change! Fingers have crossed ;-)
+ if(oldDist > 0 && newDist < 0 || oldDist < 0 && newDist > 0) {
+ return;
+ }
+ mDistX = newDist;
+ float scale = (oldDist / mDistX);
+ // sanity check
+ if(Float.isInfinite(scale) || Float.isNaN(scale) || scale > -0.001 && scale < 0.001) {
+ return;
+ }
+ PointF newX = new PointF();
+ if(mZoomHorizontally) {
+ calculateZoom(scale, newX, true);
+ mCalledBySelf = true;
+ super.setDomainBoundaries(newX.x, newX.y, BoundaryMode.FIXED);
+ lastMinX = newX.x;
+ lastMaxX = newX.y;
+ }
+ if(mZoomVertically) {
+ calculateZoom(scale, newX, false);
+ mCalledBySelf = true;
+ super.setRangeBoundaries(newX.x, newX.y, BoundaryMode.FIXED);
+ lastMinY = newX.x;
+ lastMaxY = newX.y;
+ }
+ redraw();
+ }
+
+ private void calculateZoom(float scale, PointF newX, final boolean horizontal) {
+ final float calcMax;
+ final float span;
+ if(horizontal) {
+ calcMax = getLastMaxX();
+ span = calcMax - getLastMinX();
+ } else {
+ calcMax = getLastMaxY();
+ span = calcMax - getLastMinY();
+ }
+ final float midPoint = calcMax - (span / 2.0f);
+ final float offset = span * scale / 2.0f;
+ newX.x = midPoint - offset;
+ newX.y = midPoint + offset;
+ if(horizontal) {
+ if(newX.x < getMinXLimit()) {
+ newX.x = getMinXLimit();
+ }
+ if(newX.y > getMaxXLimit()) {
+ newX.y = getMaxXLimit();
+ }
+ } else {
+ if(newX.x < getMinYLimit()) {
+ newX.x = getMinYLimit();
+ }
+ if(newX.y > getMaxYLimit()) {
+ newX.y = getMaxYLimit();
+ }
+ }
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYRegionFormatter.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYRegionFormatter.java
new file mode 100644
index 0000000..9566d5a
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYRegionFormatter.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.xy;
+
+import android.content.Context;
+import android.graphics.Paint;
+import com.androidplot.util.Configurator;
+
+/**
+ * Base class of all XYRegionFormatters.
+ */
+public class XYRegionFormatter {
+
+ //private int color;
+ private Paint paint = new Paint();
+
+ {
+ paint.setStyle(Paint.Style.FILL);
+ paint.setAntiAlias(true);
+ }
+
+ /**
+ * Provided as a convenience to users; allows instantiation and xml configuration
+ * to take place in a single line
+ *
+ * @param ctx
+ * @param xmlCfgId Id of the xml config file within /res/xml
+ */
+ public XYRegionFormatter(Context ctx, int xmlCfgId) {
+ // prevent configuration of classes derived from this one:
+ if (getClass().equals(XYRegionFormatter.class)) {
+ Configurator.configure(ctx, this, xmlCfgId);
+ }
+ }
+
+ public XYRegionFormatter(int color) {
+ //paint = new Paint();
+ paint.setColor(color);
+ //paint.setStyle(Paint.Style.FILL);
+ //paint.setAntiAlias(true);
+ //this.color = color;
+ }
+
+ public int getColor() {
+ return paint.getColor();
+ }
+
+ public void setColor(int color) {
+ paint.setColor(color);
+ }
+
+ /**
+ * Advanced users can use this method to access the Paint instance to add transparency etc.
+ * @return
+ */
+ public Paint getPaint() {
+ return paint;
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYSeries.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYSeries.java
new file mode 100644
index 0000000..22b7c19
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYSeries.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.xy;
+
+import android.util.Pair;
+import com.androidplot.Series;
+
+/**
+ * Represents a two dimensional series of data represented as xy values.
+ */
+public interface XYSeries extends Series<Pair<Number, Number>> {
+
+ /**
+ * @return Number of elements in this Series.
+ */
+ public int size();
+
+ /**
+ * Returns the x-value for an index within a series.
+ *
+ * @param index the index index (in the range <code>0</code> to
+ * <code>size()-1</code>).
+ *
+ * @return The x-value.
+ */
+ public Number getX(int index);
+
+ /**
+ * Returns the y-value for an index within a series.
+ *
+ * @param index the index index (in the range <code>0</code> to
+ * <code>size()-1</code>).
+ *
+ * @return The y-value.
+ */
+ public Number getY(int index);
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYSeriesFormatter.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYSeriesFormatter.java
new file mode 100644
index 0000000..465f5bd
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYSeriesFormatter.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.xy;
+
+import android.content.Context;
+import com.androidplot.ui.SeriesRenderer;
+import com.androidplot.ui.Formatter;
+import com.androidplot.util.ZHash;
+import com.androidplot.util.ZIndexable;
+
+public abstract class XYSeriesFormatter<XYRegionFormatterType extends XYRegionFormatter> extends Formatter<XYPlot> {
+ ZHash<RectRegion, XYRegionFormatterType> regions;
+
+ {
+ regions = new ZHash<RectRegion, XYRegionFormatterType>();
+ }
+
+ public void addRegion(RectRegion region, XYRegionFormatterType regionFormatter) {
+ regions.addToBottom(region, regionFormatter);
+ }
+
+ public void removeRegion(RectRegion region) {
+ regions.remove(region);
+ }
+
+ /**
+ * Can be used to access z-index manipulation methods of ZIndexable.
+ * @return
+ */
+ public ZIndexable<RectRegion> getRegions() {
+ return regions;
+ }
+
+ /**
+ * @param region
+ * @return
+ */
+ public XYRegionFormatterType getRegionFormatter(RectRegion region) {
+ return regions.get(region);
+ }
+
+ /**
+ * Not completely sure why this is necessary, but if it's not here then
+ * subclasses are forced to take a Plot instead of an XYPlot as a parameter,
+ * which in turn breaks the pattern.
+ * @param plot
+ * @return
+ */
+ @Override
+ public abstract SeriesRenderer getRendererInstance(XYPlot plot);
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYSeriesRenderer.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYSeriesRenderer.java
new file mode 100644
index 0000000..14f1ca9
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYSeriesRenderer.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.xy;
+
+import android.graphics.Canvas;
+import android.graphics.RectF;
+import com.androidplot.exception.PlotRenderException;
+import com.androidplot.ui.SeriesAndFormatterList;
+import com.androidplot.ui.SeriesRenderer;
+import com.androidplot.util.ZIndexable;
+
+import java.util.Hashtable;
+
+public abstract class XYSeriesRenderer<XYFormatterType extends XYSeriesFormatter>
+ extends SeriesRenderer<XYPlot, XYSeries, XYFormatterType> {
+
+ public XYSeriesRenderer(XYPlot plot) {
+ super(plot);
+ }
+
+ public Hashtable<XYRegionFormatter, String> getUniqueRegionFormatters() {
+
+ Hashtable<XYRegionFormatter, String> found = new Hashtable<XYRegionFormatter, String>();
+ SeriesAndFormatterList<XYSeries, XYFormatterType> sfl = getSeriesAndFormatterList();
+
+ if (sfl != null) {
+ for (XYFormatterType xyf : sfl.getFormatterList()) {
+ ZIndexable<RectRegion> regionIndexer = xyf.getRegions();
+ for (RectRegion region : regionIndexer.elements()) {
+ XYRegionFormatter f = xyf.getRegionFormatter(region);
+ found.put(f, region.getLabel());
+ }
+ }
+ }
+
+ return found;
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYStep.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYStep.java
new file mode 100644
index 0000000..a4dacd5
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYStep.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.xy;
+
+/**
+ * An immutable object generated by XYStepCalculator representing
+ * a stepping model to be used by an XYPlot.
+ */
+public class XYStep {
+
+ private final float stepCount;
+ private final float stepPix;
+ private final double stepVal;
+
+ //public XYStep() {}
+
+ public XYStep(float stepCount, float stepPix, double stepVal) {
+ this.stepCount = stepCount;
+ this.stepPix = stepPix;
+ this.stepVal = stepVal;
+ }
+
+ public double getStepCount() {
+ return stepCount;
+ }
+
+ /*public void setStepCount(double stepCount) {
+ this.stepCount = stepCount;
+ }*/
+
+ public float getStepPix() {
+ return stepPix;
+ }
+
+ /*public void setStepPix(float stepPix) {
+ this.stepPix = stepPix;
+ }*/
+
+ public double getStepVal() {
+ return stepVal;
+ }
+
+ /*public void setStepVal(double stepVal) {
+ this.stepVal = stepVal;
+ }*/
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYStepCalculator.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYStepCalculator.java
new file mode 100644
index 0000000..fedc201
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYStepCalculator.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.xy;
+
+import android.graphics.RectF;
+import com.androidplot.util.ValPixConverter;
+
+/**
+ * Calculates "stepping" values for a plot. These values are most commonly used for
+ * drawing grid lines on a graph.
+ */
+public class XYStepCalculator {
+
+
+ /**
+ * Convenience method - wraps other form of getStep().
+ * @param plot
+ * @param axisType
+ * @param rect
+ * @param minVal
+ * @param maxVal
+ * @return
+ */
+ public static XYStep getStep(XYPlot plot, XYAxisType axisType, RectF rect, Number minVal, Number maxVal) {
+ XYStep step = null;
+ switch(axisType) {
+ case DOMAIN:
+ step = getStep(plot.getDomainStepMode(), rect.width(), plot.getDomainStepValue(), minVal, maxVal);
+ break;
+ case RANGE:
+ step = getStep(plot.getRangeStepMode(), rect.height(), plot.getRangeStepValue(), minVal, maxVal);
+ break;
+ }
+ return step;
+ }
+
+ public static XYStep getStep(XYStepMode typeXY, float plotPixelSize, double stepValue, Number minVal, Number maxVal) {
+ //XYStep step = new XYStep();
+ double stepVal = 0;
+ float stepPix = 0;
+ float stepCount = 0;
+ switch(typeXY) {
+ case INCREMENT_BY_VAL:
+ stepVal = stepValue;
+ stepPix = (float)(stepValue/ ValPixConverter.valPerPix(minVal.doubleValue(), maxVal.doubleValue(), plotPixelSize));
+ stepCount = plotPixelSize /stepPix;
+ break;
+ case INCREMENT_BY_PIXELS:
+ stepPix = new Double(stepValue).floatValue();
+ stepCount = plotPixelSize /stepPix;
+ stepVal = ValPixConverter.valPerPix(minVal.doubleValue(), maxVal.doubleValue(), plotPixelSize)*stepPix;
+ break;
+ case SUBDIVIDE:
+ stepCount = new Double(stepValue).floatValue();
+ stepPix = (plotPixelSize /(stepCount-1));
+ stepVal = ValPixConverter.valPerPix(minVal.doubleValue(), maxVal.doubleValue(), plotPixelSize)*stepPix;
+ break;
+ }
+ return new XYStep(stepCount, stepPix, stepVal);
+ }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYStepMode.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYStepMode.java
new file mode 100644
index 0000000..005ec5f
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYStepMode.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.xy;
+
+/**
+ * INCREMENTAL_VALUE - (default) draw a tick every n values.
+ * INCREMENTAL_PIXEL - draw a tick every n pixels.
+ * SUBDIVIDE - draw n number of evenly spaced ticks.
+ */
+public enum XYStepMode {
+ SUBDIVIDE, // default
+ INCREMENT_BY_VAL,
+ INCREMENT_BY_PIXELS
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/YValueMarker.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/YValueMarker.java
new file mode 100644
index 0000000..d1fbde5
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/YValueMarker.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.xy;
+
+import android.graphics.Paint;
+import com.androidplot.ui.XLayoutStyle;
+import com.androidplot.ui.XPositionMetric;
+
+public class YValueMarker extends ValueMarker<XPositionMetric> {
+
+
+ /**
+ *
+ * @param value
+ * @param text Set to null to use the plot's default formatter.
+ */
+ public YValueMarker(Number value, String text) {
+ super(value, text, new XPositionMetric(3, XLayoutStyle.ABSOLUTE_FROM_LEFT));
+ }
+
+ /**
+ *
+ * @param value
+ * @param text Set to null to use the plot's default formatter.
+ * @param textPosition
+ * @param linePaint
+ * @param textPaint
+ */
+ public YValueMarker(Number value, String text, XPositionMetric textPosition, Paint linePaint, Paint textPaint) {
+ super(value, text, textPosition, linePaint, textPaint);
+ }
+
+ /**
+ *
+ * @param value
+ * @param text Set to null to use the plot's default formatter.
+ * @param textPosition
+ * @param linePaint
+ * @param textPaint
+ */
+ public YValueMarker(Number value, String text, XPositionMetric textPosition, int linePaint, int textPaint) {
+ super(value, text, textPosition, linePaint, textPaint);
+ }
+}
diff --git a/AndroidPlot-Core/src/main/javadoc/com/androidplot/series/package.html b/AndroidPlot-Core/src/main/javadoc/com/androidplot/series/package.html
new file mode 100644
index 0000000..1dc055f
--- /dev/null
+++ b/AndroidPlot-Core/src/main/javadoc/com/androidplot/series/package.html
@@ -0,0 +1,26 @@
+<!--
+ ~ Copyright 2012 AndroidPlot.com
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+ <title></title>
+</head>
+<body>
+Series interface definitions.
+</body>
+</html>
\ No newline at end of file
diff --git a/AndroidPlot-Core/src/main/javadoc/com/androidplot/ui/package.html b/AndroidPlot-Core/src/main/javadoc/com/androidplot/ui/package.html
new file mode 100644
index 0000000..dc47c84
--- /dev/null
+++ b/AndroidPlot-Core/src/main/javadoc/com/androidplot/ui/package.html
@@ -0,0 +1,26 @@
+<!--
+ ~ Copyright 2012 AndroidPlot.com
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+ <title></title>
+</head>
+<body>
+Visual components of AndroidPlot
+</body>
+</html>
\ No newline at end of file
diff --git a/AndroidPlot-Core/src/main/javadoc/com/androidplot/util/package.html b/AndroidPlot-Core/src/main/javadoc/com/androidplot/util/package.html
new file mode 100644
index 0000000..95a1a39
--- /dev/null
+++ b/AndroidPlot-Core/src/main/javadoc/com/androidplot/util/package.html
@@ -0,0 +1,26 @@
+<!--
+ ~ Copyright 2012 AndroidPlot.com
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+ <title></title>
+</head>
+<body>
+General use classes and interfaces.
+</body>
+</html>
\ No newline at end of file
diff --git a/AndroidPlot-Core/src/main/javadoc/com/androidplot/xy/package.html b/AndroidPlot-Core/src/main/javadoc/com/androidplot/xy/package.html
new file mode 100644
index 0000000..c894bab
--- /dev/null
+++ b/AndroidPlot-Core/src/main/javadoc/com/androidplot/xy/package.html
@@ -0,0 +1,26 @@
+<!--
+ ~ Copyright 2012 AndroidPlot.com
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+ <title></title>
+</head>
+<body>
+Classes for creating XYPlots.
+</body>
+</html>
\ No newline at end of file
diff --git a/AndroidPlot-Core/src/main/javadoc/doc-files/barplot1.jpg b/AndroidPlot-Core/src/main/javadoc/doc-files/barplot1.jpg
new file mode 100644
index 0000000..c2f4fa9
--- /dev/null
+++ b/AndroidPlot-Core/src/main/javadoc/doc-files/barplot1.jpg
Binary files differ
diff --git a/AndroidPlot-Core/src/main/javadoc/doc-files/lineplot1.jpg b/AndroidPlot-Core/src/main/javadoc/doc-files/lineplot1.jpg
new file mode 100644
index 0000000..f1917b2
--- /dev/null
+++ b/AndroidPlot-Core/src/main/javadoc/doc-files/lineplot1.jpg
Binary files differ
diff --git a/AndroidPlot-Core/src/main/javadoc/overview.html b/AndroidPlot-Core/src/main/javadoc/overview.html
new file mode 100644
index 0000000..d882141
--- /dev/null
+++ b/AndroidPlot-Core/src/main/javadoc/overview.html
@@ -0,0 +1,20 @@
+<!--
+ ~ Copyright 2012 AndroidPlot.com
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<body>
+A library for drawing plots on the Android platform.
+Visit <a href="http://androidplot.com/wiki/Docs">http://androidplot.com/wiki/Docs</a> for documentation and tutorials.
+</body>
\ No newline at end of file
diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/LineRegionTest.java b/AndroidPlot-Core/src/test/java/com/androidplot/LineRegionTest.java
new file mode 100644
index 0000000..a524313
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/LineRegionTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class LineRegionTest {
+ @Before
+ public void setUp() throws Exception {
+
+ }
+
+ @After
+ public void tearDown() throws Exception {
+
+ }
+
+ @Test
+ public void testConstructor() throws Exception {
+ LineRegion lr = new LineRegion(0d, 0d);
+ assertEquals(0d, lr.getMinVal());
+ assertEquals(0d, lr.getMaxVal());
+
+ lr = new LineRegion(1.5d, -2d);
+ assertEquals(-2d, lr.getMinVal());
+ assertEquals(1.5d, lr.getMaxVal());
+
+ lr = new LineRegion(10d, 20d);
+ assertEquals(10d, lr.getMinVal());
+ assertEquals(20d, lr.getMaxVal());
+ }
+
+
+ @Test
+ public void testContains() throws Exception {
+
+ }
+
+ @Test
+ public void testIntersects() throws Exception {
+ LineRegion line1 = new LineRegion(1, 10);
+ LineRegion line2 = new LineRegion(11, 20);
+ assertFalse(line1.intersects(line2));
+
+ line1.setMaxVal(15);
+ assertTrue(line1.intersects(line2));
+
+ //l1end = 30;
+ line1.setMaxVal(30);
+ assertTrue(line1.intersects(line2));
+
+ //l1start = 21;
+ line1.setMinVal(21);
+ assertFalse(line1.intersects(line2));
+ }
+
+ @Test
+ public void testLength() throws Exception {
+ LineRegion lr = new LineRegion(0, 10);
+ assertEquals(10d, lr.length().doubleValue(), 0);
+
+ lr = new LineRegion(-5, 5);
+ assertEquals(10d, lr.length().doubleValue(), 0);
+ }
+}
diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/PlotTest.java b/AndroidPlot-Core/src/test/java/com/androidplot/PlotTest.java
new file mode 100644
index 0000000..3dc8aeb
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/PlotTest.java
@@ -0,0 +1,483 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot;
+
+import android.content.Context;
+import android.graphics.*;
+import android.os.Handler;
+import android.util.Log;
+import android.view.View;
+import com.androidplot.mock.MockContext;
+import com.androidplot.mock.MockPaint;
+import com.androidplot.ui.SeriesAndFormatterList;
+import com.androidplot.exception.PlotRenderException;
+import com.androidplot.ui.SeriesRenderer;
+import com.androidplot.ui.Formatter;
+//import mockit.*;
+import com.androidplot.ui.widget.TextLabelWidget;
+import com.androidplot.util.Configurator;
+import com.androidplot.util.FontUtils;
+import com.androidplot.util.PixelUtils;
+import mockit.*;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotSame;
+import static junit.framework.Assert.assertNull;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+@UsingMocksAndStubs({Log.class, View.class,Handler.class,Paint.class,Color.class,
+ RectF.class, Rect.class, FontUtils.class, Canvas.class,
+ PixelUtils.class,Context.class})
+
+public class PlotTest {
+
+ static class MockPlotListener implements PlotListener {
+
+ @Override
+ public void onBeforeDraw(Plot source, Canvas canvas) {}
+
+ @Override
+ public void onAfterDraw(Plot source, Canvas canvas) {}
+ }
+
+ static class MockSeries implements Series {
+ @Override
+ public String getTitle() {
+ return null;
+ }
+
+ }
+
+ static class MockSeries2 implements Series {
+ @Override
+ public String getTitle() {
+ return null;
+ }
+ }
+
+ static class MockSeries3 implements Series {
+ @Override
+ public String getTitle() {
+ return null;
+ }
+ }
+
+ static class MockRenderer1 extends SeriesRenderer {
+
+ public MockRenderer1(Plot plot) {
+ super(plot);
+ }
+
+ @Override
+ public void onRender(Canvas canvas, RectF plotArea) throws PlotRenderException {
+
+ }
+
+ @Override
+ public void doDrawLegendIcon(Canvas canvas, RectF rect, Formatter formatter) {
+
+ }
+ }
+ static class MockRenderer2 extends SeriesRenderer {
+
+ public MockRenderer2(Plot plot) {
+ super(plot);
+ }
+
+ @Override
+ public void onRender(Canvas canvas, RectF plotArea) throws PlotRenderException {
+
+ }
+
+ @Override
+ public void doDrawLegendIcon(Canvas canvas, RectF rect, Formatter formatter) {
+
+ }
+ }
+
+ static class MockFormatter1 extends Formatter<MockPlot> {
+
+ @Override
+ public Class<? extends SeriesRenderer> getRendererClass() {
+ return MockRenderer1.class;
+ }
+
+ @Override
+ public SeriesRenderer getRendererInstance(MockPlot plot) {
+ return new MockRenderer1(plot);
+ }
+ }
+
+ static class MockFormatter2 extends Formatter<MockPlot> {
+
+ @Override
+ public Class<? extends SeriesRenderer> getRendererClass() {
+ return MockRenderer2.class;
+ }
+
+ @Override
+ public SeriesRenderer getRendererInstance(MockPlot plot) {
+ return new MockRenderer2(plot);
+ }
+ }
+
+ //@MockClass(realClass = Plot.class)
+ public static class MockPlot extends Plot<MockSeries, Formatter, SeriesRenderer> {
+ public MockPlot(Context context, String title) {
+ super(context, title);
+ }
+
+ @Override
+ protected void onPreInit() {
+
+ }
+
+ /*@Override
+ protected SeriesRenderer doGetRendererInstance(Class clazz) {
+ if(clazz == MockRenderer1.class) {
+ return new MockRenderer1(this);
+ } else if(clazz == MockRenderer2.class) {
+ return new MockRenderer2(this);
+ } else {
+ return null;
+ }
+ }*/
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ Mockit.setUpMocks(MockPaint.class,MockContext.class);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+
+ }
+
+ @Test
+ public void testAddSeries() throws Exception {
+ Context context = Mockit.setUpMock(new MockContext());
+ //Plot plot = Mockit.setUpMock(Plot.class, new MockPlot(context, "MockPlot"));
+ //Plot plot = Mockit.setUpMock(new MockPlot());
+ Plot plot = new MockPlot(context, "MockPlot");
+
+ MockSeries m1 = new MockSeries();
+ Class cl = MockRenderer1.class;
+
+
+
+ plot.addSeries(m1, new MockFormatter1());
+
+ LinkedHashMap<Class<SeriesRenderer>, SeriesAndFormatterList<MockSeries,MockFormatter1>> registry = Deencapsulation.getField(plot, "seriesRegistry");
+ assertEquals(1, registry.size());
+ assertEquals(1, registry.get(cl).size());
+
+ plot.addSeries(m1, new MockFormatter1());
+
+ // duplicate Renderer added, registry size should not grow:
+ assertEquals(1, registry.size());
+ assertEquals(1, registry.get(cl).size());
+
+ MockSeries m2 = new MockSeries();
+
+ plot.addSeries(m2, new MockFormatter1());
+
+ // still should only be one renderer type:
+ assertEquals(1, registry.size());
+
+ // we added a new instance of cl to the renderer so there should be 2 in the subregistry:
+ assertEquals(2, registry.get(cl).size());
+
+
+ // lets add another renderer:
+ plot.addSeries(m1, new MockFormatter2());
+
+ assertEquals(2, registry.size());
+ }
+
+ @Test
+ public void testRemoveSeries() throws Exception {
+
+ Context context = Mockit.setUpMock(new MockContext());
+ Plot plot = new MockPlot(context, "MockPlot");
+ LinkedHashMap<Class<SeriesRenderer>, SeriesAndFormatterList<MockSeries,MockFormatter1>> registry = Deencapsulation.getField(plot, "seriesRegistry");
+
+ MockSeries m1 = new MockSeries();
+ MockSeries m2 = new MockSeries();
+ MockSeries m3 = new MockSeries();
+
+ plot.addSeries(m1, new MockFormatter1());
+ plot.addSeries(m2, new MockFormatter1());
+ plot.addSeries(m3, new MockFormatter1());
+
+ plot.addSeries(m1, new MockFormatter2());
+ plot.addSeries(m2, new MockFormatter2());
+ plot.addSeries(m3, new MockFormatter2());
+
+
+ // a quick sanity check:
+ assertEquals(2, registry.size());
+ assertEquals(3, registry.get(MockRenderer1.class).size());
+ assertEquals(3, registry.get(MockRenderer2.class).size());
+
+ plot.removeSeries(m1, MockRenderer1.class);
+ assertEquals(2, registry.get(MockRenderer1.class).size());
+
+ plot.removeSeries(m2, MockRenderer1.class);
+ assertEquals(1, registry.get(MockRenderer1.class).size());
+
+ plot.removeSeries(m2, MockRenderer1.class);
+ assertEquals(1, registry.get(MockRenderer1.class).size());
+
+ plot.removeSeries(m3, MockRenderer1.class);
+
+ // all the elements should be gone from MockRenderer1, thus the renderer should
+ // also be gone:
+ assertNull(registry.get(MockRenderer1.class));
+
+
+ // add em all back
+ plot.addSeries(m1, new MockFormatter1());
+ plot.addSeries(m2, new MockFormatter1());
+ plot.addSeries(m3, new MockFormatter1());
+
+ plot.addSeries(m1, new MockFormatter1());
+ plot.addSeries(m2, new MockFormatter1());
+ plot.addSeries(m3, new MockFormatter1());
+
+
+ // a quick sanity check:
+ assertEquals(2, registry.size());
+ assertEquals(3, registry.get(MockRenderer1.class).size());
+ assertEquals(3, registry.get(MockRenderer2.class).size());
+
+ // now lets try removing a series from all renderers:
+ plot.removeSeries(m1);
+ assertEquals(2, registry.get(MockRenderer1.class).size());
+ assertEquals(2, registry.get(MockRenderer2.class).size());
+
+ // and now lets remove the remaining series:
+ plot.removeSeries(m2);
+ plot.removeSeries(m3);
+
+ // nothing should be left:
+ assertNull(registry.get(MockRenderer1.class));
+ assertNull(registry.get(MockRenderer2.class));
+ }
+
+
+ @Test
+ public void testGetFormatter() throws Exception {
+ Context context = Mockit.setUpMock(new MockContext());
+ Plot plot = new MockPlot(context, "MockPlot");
+ LinkedHashMap<Class<SeriesRenderer>, SeriesAndFormatterList<MockSeries,MockFormatter1>> registry = Deencapsulation.getField(plot, "seriesRegistry");
+
+ MockSeries m1 = new MockSeries();
+ MockSeries m2 = new MockSeries();
+ MockSeries m3 = new MockSeries();
+
+ MockFormatter1 f1 = new MockFormatter1();
+ MockFormatter1 f2 = new MockFormatter1();
+ MockFormatter2 f3 = new MockFormatter2();
+
+ plot.addSeries(m1, f1);
+ plot.addSeries(m2, f2);
+ plot.addSeries(m3, new MockFormatter1());
+
+ plot.addSeries(m1, new MockFormatter1());
+ plot.addSeries(m2, f3);
+ plot.addSeries(m3, new MockFormatter1());
+
+ assertEquals(registry.get(MockRenderer1.class).getFormatter(m1), f1);
+ assertEquals(registry.get(MockRenderer1.class).getFormatter(m2), f2);
+ assertEquals(registry.get(MockRenderer2.class).getFormatter(m2), f3);
+
+ assertNotSame(registry.get(MockRenderer2.class).getFormatter(m2), f1);
+
+ }
+
+ @Test
+ public void testGetSeriesListForRenderer() throws Exception {
+
+ Context context = Mockit.setUpMock(new MockContext());
+ Plot plot = new MockPlot(context, "MockPlot");
+ //LinkedHashMap<Class<SeriesRenderer>, SeriesAndFormatterList<MockSeries,MockFormatter1>> registry = Deencapsulation.getField(plot, "seriesRegistry");
+
+ MockSeries m1 = new MockSeries();
+ MockSeries m2 = new MockSeries();
+ MockSeries m3 = new MockSeries();
+
+ plot.addSeries(m1, new MockFormatter1());
+ plot.addSeries(m2, new MockFormatter1());
+ plot.addSeries(m3, new MockFormatter1());
+
+ plot.addSeries(m1, new MockFormatter1());
+ plot.addSeries(m2, new MockFormatter1());
+ plot.addSeries(m3, new MockFormatter1());
+
+ List<MockSeries> m1List = plot.getSeriesListForRenderer(MockRenderer1.class);
+ assertEquals(3, m1List.size());
+ assertEquals(m1, m1List.get(0));
+ assertNotSame(m2, m1List.get(0));
+ assertEquals(m2, m1List.get(1));
+ assertEquals(m3, m1List.get(2));
+ }
+
+ @Test
+ public void testGetRendererList() throws Exception {
+
+ Context context = Mockit.setUpMock(new MockContext());
+ Plot plot = new MockPlot(context, "MockPlot");
+ //LinkedHashMap<Class<SeriesRenderer>, SeriesAndFormatterList<MockSeries,MockFormatter1>> registry = Deencapsulation.getField(plot, "seriesRegistry");
+
+ MockSeries m1 = new MockSeries();
+ MockSeries m2 = new MockSeries();
+ MockSeries m3 = new MockSeries();
+
+ plot.addSeries(m1, new MockFormatter1());
+ plot.addSeries(m2, new MockFormatter1());
+ plot.addSeries(m3, new MockFormatter1());
+
+ plot.addSeries(m1, new MockFormatter2());
+ plot.addSeries(m2, new MockFormatter2());
+ plot.addSeries(m3, new MockFormatter2());
+
+ List<SeriesRenderer> rList = plot.getRendererList();
+ assertEquals(2, rList.size());
+
+ assertEquals(MockRenderer1.class, rList.get(0).getClass());
+ assertEquals(MockRenderer2.class, rList.get(1).getClass());
+ }
+
+ @Test
+ public void testAddListener() throws Exception {
+ Context context = Mockit.setUpMock(new MockContext());
+ Plot plot = new MockPlot(context, "MockPlot");
+ ArrayList<PlotListener> listeners = Deencapsulation.getField(plot, "listeners");
+ //LinkedHashMap<Class<SeriesRenderer>, SeriesAndFormatterList<MockSeries,MockFormatter1>> registry = Deencapsulation.getField(plot, "seriesRegistry");
+
+ assertEquals(0, listeners.size());
+
+ MockPlotListener pl1 = new MockPlotListener();
+ MockPlotListener pl2 = new MockPlotListener();
+
+ plot.addListener(pl1);
+
+ assertEquals(1, listeners.size());
+
+ // should return false on a double entry attempt
+ assertFalse(plot.addListener(pl1));
+
+ // make sure the listener wasnt added anyway:
+ assertEquals(1, listeners.size());
+
+ plot.addListener(pl2);
+
+ assertEquals(2, listeners.size());
+
+ }
+
+ @Test
+ public void testRemoveListener() throws Exception {
+ Context context = Mockit.setUpMock(new MockContext());
+ Plot plot = new MockPlot(context, "MockPlot");
+ ArrayList<PlotListener> listeners = Deencapsulation.getField(plot, "listeners");
+ //LinkedHashMap<Class<SeriesRenderer>, SeriesAndFormatterList<MockSeries,MockFormatter1>> registry = Deencapsulation.getField(plot, "seriesRegistry");
+
+ assertEquals(0, listeners.size());
+
+ MockPlotListener pl1 = new MockPlotListener();
+ MockPlotListener pl2 = new MockPlotListener();
+ MockPlotListener pl3 = new MockPlotListener();
+
+ plot.addListener(pl1);
+ plot.addListener(pl2);
+
+ assertEquals(2, listeners.size());
+
+ assertFalse(plot.removeListener(pl3));
+
+ assertTrue(plot.removeListener(pl1));
+
+ assertEquals(1, listeners.size());
+
+ assertFalse(plot.removeListener(pl1));
+
+ assertEquals(1, listeners.size());
+
+ assertTrue(plot.removeListener(pl2));
+
+ assertEquals(0, listeners.size());
+
+ }
+
+ /*@Test
+ public void testGuessGetterName() throws Exception {
+ Context context = Mockit.setUpMock(new MockContext());
+ Plot plot = new MockPlot(context, "MockPlot");
+
+ Method m = Plot.class.getDeclaredMethod("guessGetterMethod", Object.class, String.class);
+ assertNotNull(m);
+ }
+
+ @Test
+ public void testGuessSetterName() throws Exception {
+ Context context = Mockit.setUpMock(new MockContext());
+ Plot plot = new MockPlot(context, "MockPlot");
+
+ Method m = Plot.class.getDeclaredMethod("guessSetterMethod", Object.class, String.class, Class.class);
+ assertNotNull(m);
+ }*/
+
+
+
+ @Test
+ public void testConfigure() throws Exception {
+ //Context context = Mockit.setUpMock(new MockContext.MockContext2());
+ Context context = new MockContext.MockContext2();
+ Plot plot = new MockPlot(context, "MockPlot");
+
+ HashMap<String, String> params = new HashMap<String, String>();
+ String param1 = "this is a test.";
+ //String param2 = Plot.RenderMode.USE_BACKGROUND_THREAD.toString();
+ String param2 = "use_background_thread";
+ String param3 = "#FF0000";
+ params.put("title", param1);
+ params.put("renderMode", param2);
+ params.put("backgroundPaint.color", param3);
+
+
+ //Method m = Plot.class.getDeclaredMethod("configure", params.getClass());
+ //m.setAccessible(true);
+ //m.invoke(plot, params);
+ Configurator.configure(context, plot, params);
+
+ assertEquals(param1, plot.getTitle());
+ assertEquals(Plot.RenderMode.USE_BACKGROUND_THREAD, plot.getRenderMode());
+ assertEquals(Color.parseColor(param3), plot.getBackgroundPaint().getColor());
+ }
+}
diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/RegionTest.java b/AndroidPlot-Core/src/test/java/com/androidplot/RegionTest.java
new file mode 100644
index 0000000..dcf4148
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/RegionTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class RegionTest {
+ @Before
+ public void setUp() throws Exception {
+
+ }
+
+ @After
+ public void tearDown() throws Exception {
+
+ }
+
+ @Test
+ public void testContains() throws Exception {
+
+ }
+
+ @Test
+ public void testIntersects() throws Exception {
+ LineRegion line1 = new LineRegion(1, 10);
+ LineRegion line2 = new LineRegion(11, 20);
+ assertFalse(line1.intersects(line2));
+
+ line1.setMaxVal(15);
+ assertTrue(line1.intersects(line2));
+
+ //l1end = 30;
+ line1.setMaxVal(30);
+ assertTrue(line1.intersects(line2));
+
+ //l1start = 21;
+ line1.setMinVal(21);
+ assertFalse(line1.intersects(line2));
+ }
+}
diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockCanvas.java b/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockCanvas.java
new file mode 100644
index 0000000..44fca82
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockCanvas.java
@@ -0,0 +1,34 @@
+package com.androidplot.mock;
+
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PointF;
+import mockit.Mock;
+import mockit.MockClass;
+import mockit.MockUp;
+import mockit.Mocked;
+
+@MockClass(realClass = Canvas.class, stubs="", inverse=true)
+public class MockCanvas {
+
+ @Mock
+ public int getHeight() {
+ return 100;
+ }
+
+ @Mock
+ public int getWidth() {
+ return 100;
+ }
+
+ @Mock
+ public void restore() {}
+
+ @Mock
+ public int save(int flags) {
+ return 1;
+ }
+
+ @Mock
+ public void drawPoint(float x, float y, Paint paint) {}
+}
diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockContext.java b/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockContext.java
new file mode 100644
index 0000000..0251c77
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockContext.java
@@ -0,0 +1,428 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.mock;
+
+import android.content.*;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.database.DatabaseErrorHandler;
+import android.database.sqlite.SQLiteDatabase;
+import android.graphics.Bitmap;
+import android.graphics.Paint;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import mockit.Instantiation;
+import mockit.Mock;
+import mockit.MockClass;
+
+import java.io.*;
+
+@MockClass(realClass = Context.class)
+public class MockContext {
+
+ /**
+ * Useful for when methods are going to actually be called on a Context instance.
+ * See {@link com.androidplot.PlotTest#testConfigure()} for an example.
+ */
+ public static final class MockContext2 extends Context {
+
+ public MockContext2() {}
+
+ @Override
+ public AssetManager getAssets() {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Mock
+ public android.content.res.Resources getResources() { throw new IllegalArgumentException();}
+
+ @Override
+ public PackageManager getPackageManager() {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public ContentResolver getContentResolver() {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public Looper getMainLooper() {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public Context getApplicationContext() {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void setTheme(int i) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public Resources.Theme getTheme() {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public ClassLoader getClassLoader() {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public String getPackageName() {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public ApplicationInfo getApplicationInfo() {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public String getPackageResourcePath() {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public String getPackageCodePath() {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public SharedPreferences getSharedPreferences(String s, int i) {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public FileInputStream openFileInput(String s) throws FileNotFoundException {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public FileOutputStream openFileOutput(String s, int i) throws FileNotFoundException {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public boolean deleteFile(String s) {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public File getFileStreamPath(String s) {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public File getFilesDir() {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public File getExternalFilesDir(String s) {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public File getObbDir() {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public File getCacheDir() {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public File getExternalCacheDir() {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public String[] fileList() {
+ return new String[0]; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public File getDir(String s, int i) {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public SQLiteDatabase openOrCreateDatabase(String s, int i, SQLiteDatabase.CursorFactory cursorFactory) {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public SQLiteDatabase openOrCreateDatabase(String s, int i, SQLiteDatabase.CursorFactory cursorFactory, DatabaseErrorHandler databaseErrorHandler) {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public boolean deleteDatabase(String s) {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public File getDatabasePath(String s) {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public String[] databaseList() {
+ return new String[0]; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public Drawable getWallpaper() {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public Drawable peekWallpaper() {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public int getWallpaperDesiredMinimumWidth() {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public int getWallpaperDesiredMinimumHeight() {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void setWallpaper(Bitmap bitmap) throws IOException {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void setWallpaper(InputStream inputStream) throws IOException {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void clearWallpaper() throws IOException {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void startActivity(Intent intent) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ //@Override
+ public void startActivity(Intent intent, Bundle bundle) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void startActivities(Intent[] intents) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ //@Override
+ public void startActivities(Intent[] intents, Bundle bundle) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void startIntentSender(IntentSender intentSender, Intent intent, int i, int i1, int i2) throws IntentSender.SendIntentException {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ //@Override
+ public void startIntentSender(IntentSender intentSender, Intent intent, int i, int i1, int i2, Bundle bundle) throws IntentSender.SendIntentException {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void sendBroadcast(Intent intent) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void sendBroadcast(Intent intent, String s) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void sendOrderedBroadcast(Intent intent, String s) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void sendOrderedBroadcast(Intent intent, String s, BroadcastReceiver broadcastReceiver, Handler handler, int i, String s1, Bundle bundle) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void sendStickyBroadcast(Intent intent) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void sendStickyOrderedBroadcast(Intent intent, BroadcastReceiver broadcastReceiver, Handler handler, int i, String s, Bundle bundle) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void removeStickyBroadcast(Intent intent) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public Intent registerReceiver(BroadcastReceiver broadcastReceiver, IntentFilter intentFilter) {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public Intent registerReceiver(BroadcastReceiver broadcastReceiver, IntentFilter intentFilter, String s, Handler handler) {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void unregisterReceiver(BroadcastReceiver broadcastReceiver) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public ComponentName startService(Intent intent) {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public boolean stopService(Intent intent) {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public boolean bindService(Intent intent, ServiceConnection serviceConnection, int i) {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void unbindService(ServiceConnection serviceConnection) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public boolean startInstrumentation(ComponentName componentName, String s, Bundle bundle) {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public Object getSystemService(String s) {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public int checkPermission(String s, int i, int i1) {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public int checkCallingPermission(String s) {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public int checkCallingOrSelfPermission(String s) {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void enforcePermission(String s, int i, int i1, String s1) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void enforceCallingPermission(String s, String s1) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void enforceCallingOrSelfPermission(String s, String s1) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void grantUriPermission(String s, Uri uri, int i) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void revokeUriPermission(Uri uri, int i) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public int checkUriPermission(Uri uri, int i, int i1, int i2) {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public int checkCallingUriPermission(Uri uri, int i) {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public int checkCallingOrSelfUriPermission(Uri uri, int i) {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public int checkUriPermission(Uri uri, String s, String s1, int i, int i1, int i2) {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void enforceUriPermission(Uri uri, int i, int i1, int i2, String s) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void enforceCallingUriPermission(Uri uri, int i, String s) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void enforceCallingOrSelfUriPermission(Uri uri, int i, String s) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void enforceUriPermission(Uri uri, String s, String s1, int i, int i1, int i2, String s2) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public Context createPackageContext(String s, int i) throws PackageManager.NameNotFoundException {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+ }
+}
diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockLooper.java b/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockLooper.java
new file mode 100644
index 0000000..5a4649e
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockLooper.java
@@ -0,0 +1,25 @@
+package com.androidplot.mock;
+
+import android.os.Looper;
+import mockit.Mock;
+import mockit.MockClass;
+
+
+/**
+ * myLooper and getMainLooper will always be equal. The implication is that any calling
+ * entity will assume that it is running on the main thread. Simulation of background mode
+ * is not supported.
+ */
+@MockClass(realClass = Looper.class)
+public class MockLooper {
+
+ @Mock
+ public static Looper myLooper() {
+ return null;
+ }
+
+ @Mock
+ public static Looper getMainLooper() {
+ return null;
+ }
+}
diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockPaint.java b/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockPaint.java
new file mode 100644
index 0000000..f0209ee
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockPaint.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.mock;
+
+import android.graphics.Paint;
+import android.graphics.RectF;
+import mockit.Instantiation;
+import mockit.Mock;
+import mockit.MockClass;
+
+@MockClass(realClass = Paint.class)
+public final class MockPaint {
+ int color = 0;
+
+ @Mock
+ public void setColor(int color) {
+ this.color = color;
+ }
+
+ @Mock
+ public int getColor() {
+ return color;
+ }
+}
diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockPixelUtils.java b/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockPixelUtils.java
new file mode 100644
index 0000000..45cb25c
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockPixelUtils.java
@@ -0,0 +1,22 @@
+package com.androidplot.mock;
+
+import android.content.Context;
+import android.graphics.PointF;
+import android.graphics.RectF;
+import com.androidplot.util.PixelUtils;
+import mockit.Mock;
+import mockit.MockClass;
+
+@MockClass(realClass = PixelUtils.class, stubs="", inverse=true)
+public class MockPixelUtils {
+
+ @Mock
+ public static void init(Context ctx) {
+
+ }
+
+ @Mock
+ public static PointF sub(PointF lhs, PointF rhs) {
+ return new PointF(0, 0);
+ }
+}
diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockPointF.java b/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockPointF.java
new file mode 100644
index 0000000..0970c35
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockPointF.java
@@ -0,0 +1,25 @@
+package com.androidplot.mock;
+
+import android.graphics.PointF;
+import mockit.Mock;
+import mockit.MockClass;
+
+@MockClass(realClass = PointF.class)
+public class MockPointF {
+ float x;
+ float y;
+
+ @Mock
+ public void $init() {}
+
+ @Mock
+ public void $init(float x, float y) {
+ set(x, y);
+ }
+
+ @Mock
+ public void set(float x, float y) {
+ this.x = x;
+ this.y = y;
+ }
+}
diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockRectF.java b/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockRectF.java
new file mode 100644
index 0000000..022788c
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockRectF.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.mock;
+
+import android.graphics.RectF;
+import mockit.Instantiation;
+import mockit.Mock;
+import mockit.MockClass;
+
+@MockClass(realClass = RectF.class, stubs="", inverse=true)
+public final class MockRectF {
+ //public float left;
+ //public float top;
+ //public float right;
+ //public float bottom;
+
+ public RectF it;
+
+ @Mock
+ public void $init() {
+
+ }
+
+ @Mock
+ public void $init(RectF rhs) {
+ it.left = rhs.left;
+ it.top = rhs.top;
+ it.right = rhs.right;
+ it.bottom = rhs.bottom;
+ }
+
+ @Mock
+ public void $init(float left, float top, float right, float bottom) {
+ it.left = left;
+ it.top = top;
+ it.right = right;
+ it.bottom = bottom;
+ // do anything here
+
+ }
+
+ @Mock
+ public void offset(float dx, float dy) {
+ float w = width();
+ float h = height();
+
+ it.left = it.left + dx;
+ it.right = it.right + dx;
+
+ it.top = it.top + dy;
+ it.bottom = it.bottom + dy;
+ }
+
+ @Mock
+ public void offsetTo(float left, float top) {
+
+ it.right = left + width();
+ it.bottom = top + height();
+
+ it.left = left;
+ it.top = top;
+
+ // do anything here
+ }
+
+ @Mock
+ public float height() {
+ return it.bottom - it.top;
+ }
+
+ @Mock
+ public float width() {
+ return it.right - it.left;
+ }
+
+ @Mock
+ public String toString() {
+ return null;
+ }
+}
diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockSizeMetrics.java b/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockSizeMetrics.java
new file mode 100644
index 0000000..389b1d9
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockSizeMetrics.java
@@ -0,0 +1,21 @@
+package com.androidplot.mock;
+
+import com.androidplot.ui.SizeLayoutType;
+import com.androidplot.ui.SizeMetric;
+import com.androidplot.ui.SizeMetrics;
+import mockit.Mock;
+import mockit.MockClass;
+
+@MockClass(realClass = SizeMetrics.class)
+public class MockSizeMetrics {
+
+ @Mock
+ public SizeMetric getWidthMetric() {
+ return new SizeMetric(0, SizeLayoutType.ABSOLUTE);
+ }
+
+ @Mock
+ public SizeMetric getHeightMetric() {
+ return new SizeMetric(0, SizeLayoutType.ABSOLUTE);
+ }
+}
diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockWidget.java b/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockWidget.java
new file mode 100644
index 0000000..53dc6b8
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockWidget.java
@@ -0,0 +1,20 @@
+package com.androidplot.mock;
+
+import com.androidplot.ui.widget.Widget;
+import mockit.Mock;
+import mockit.MockClass;
+
+@MockClass(realClass = Widget.class, inverse = false)
+public class MockWidget {
+
+ @Mock
+ public float getWidthPix(float f) {
+ return 100;
+ }
+
+ @Mock
+ public float getHeightPix(float f) {
+ return 100;
+ }
+
+}
diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/ui/DynamicTableModelTest.java b/AndroidPlot-Core/src/test/java/com/androidplot/ui/DynamicTableModelTest.java
new file mode 100644
index 0000000..2585ec8
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/ui/DynamicTableModelTest.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.ui;
+
+import android.graphics.RectF;
+import com.androidplot.mock.MockRectF;
+import com.androidplot.ui.DynamicTableModel;
+import com.androidplot.ui.TableModel;
+import com.androidplot.ui.TableOrder;
+import mockit.*;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Iterator;
+
+import static junit.framework.Assert.assertEquals;
+
+public class DynamicTableModelTest {
+ @Before
+ public void setUp() throws Exception {
+ Mockit.setUpMocks(MockRectF.class);
+ }
+
+ @Test
+ public void testConstructor() throws Exception {
+ TableModel model = new DynamicTableModel(5, 5, TableOrder.COLUMN_MAJOR);
+ // TODO
+ }
+
+ @Test
+ public void testGetCellRect() throws Exception {
+
+ // square table, both rows and columns defined:
+ DynamicTableModel model = new DynamicTableModel(5, 5);
+ RectF tableRect = new RectF(0, 0, 1000, 2000);
+ RectF cellRect = model.getCellRect(tableRect, 10);
+ assertEquals(200f, cellRect.width());
+
+ // only rows defined:
+ model = new DynamicTableModel(5, 0);
+ cellRect = model.getCellRect(tableRect, 10);
+ assertEquals(200f, cellRect.width());
+
+ // only columns defined:
+ model = new DynamicTableModel(0, 5);
+ cellRect = model.getCellRect(tableRect, 10);
+ assertEquals(400f, cellRect.height());
+ }
+
+ @Test public void testIterator() throws Exception {
+ TableModel model = new DynamicTableModel(2, 2);
+
+ RectF tableRect = new RectF(0, 0, 1000, 2000);
+
+ // should stop at 4 iterations since the table can only hold that many:
+ Iterator<RectF> it = model.getIterator(tableRect, 10);
+ int iterations = 0;
+ while(it.hasNext()) {
+ it.next();
+ iterations++;
+ }
+ assertEquals(4, iterations);
+
+ // now set a dynamic number of columns. iterations should equal however
+ // many elements we throw at it:
+ model = new DynamicTableModel(2, 0);
+ it = model.getIterator(tableRect, 10);
+ iterations = 0;
+ while(it.hasNext()) {
+ it.next();
+ iterations++;
+ }
+ assertEquals(10, iterations);
+
+
+ }
+
+ @Test
+ public void testRowMajorIteration() throws Exception {
+
+ // square table, both rows and columns defined:
+ TableModel model = new DynamicTableModel(2, 2);
+ RectF tableRect = new RectF(0, 0, 1000, 2000);
+ int createdCells = 4;
+ Iterator<RectF> it = model.getIterator(tableRect, createdCells);
+
+
+ // 2x2:
+ // cell 0 (top-left
+ RectF cellRect = it.next();
+ assertEquals(500f, cellRect.width());
+ assertEquals(1000f, cellRect.height());
+ assertEquals(0f, cellRect.left);
+ assertEquals(0f, cellRect.top);
+ assertEquals(500f, cellRect.right);
+ assertEquals(1000f, cellRect.bottom);
+
+ // cell 1 (top-right)
+ cellRect = it.next();
+ assertEquals(500f, cellRect.width());
+ assertEquals(1000f, cellRect.height());
+ assertEquals(500f, cellRect.left);
+ assertEquals(0f, cellRect.top);
+ assertEquals(1000f, cellRect.right);
+ assertEquals(1000f, cellRect.bottom);
+
+ // cell 2 (bottom-left)
+ cellRect = it.next();
+ assertEquals(500f, cellRect.width());
+ assertEquals(1000f, cellRect.height());
+ assertEquals(0f, cellRect.left);
+ assertEquals(1000f, cellRect.top);
+ assertEquals(500f, cellRect.right);
+ assertEquals(2000f, cellRect.bottom);
+
+ // cell 3 (bottom-right)
+ cellRect = it.next();
+ assertEquals(500f, cellRect.width());
+ assertEquals(1000f, cellRect.height());
+ assertEquals(500f, cellRect.left);
+ assertEquals(1000f, cellRect.top);
+ assertEquals(1000f, cellRect.right);
+ assertEquals(2000f, cellRect.bottom);
+
+ // 2xN:
+ /*model = new DynamicTableModel(2, 0);
+ tableRect = new RectF(0, 0, 1000, 2000);
+ createdCells = 4;
+ it = model.getIterator(tableRect, createdCells);*/
+
+
+
+ }
+
+
+ @Test
+ public void testColumnMajorIteration() throws Exception {
+
+ // square table, both rows and columns defined:
+ TableModel model = new DynamicTableModel(2, 2, TableOrder.COLUMN_MAJOR);
+ RectF tableRect = new RectF(0, 0, 1000, 2000);
+ int createdCells = 4;
+ Iterator<RectF> it = model.getIterator(tableRect, createdCells);
+
+
+ // 2x2 fixed:
+ // cell 0 (top-left
+ RectF cellRect = it.next();
+ assertEquals(500f, cellRect.width());
+ assertEquals(1000f, cellRect.height());
+ assertEquals(0f, cellRect.left);
+ assertEquals(0f, cellRect.top);
+ assertEquals(500f, cellRect.right);
+ assertEquals(1000f, cellRect.bottom);
+
+ // cell 1 (bottom-left)
+ cellRect = it.next();
+ assertEquals(500f, cellRect.width());
+ assertEquals(1000f, cellRect.height());
+ assertEquals(0f, cellRect.left);
+ assertEquals(1000f, cellRect.top);
+ assertEquals(500f, cellRect.right);
+ assertEquals(2000f, cellRect.bottom);
+
+ // cell 2 (bottom-left)
+ cellRect = it.next();
+ assertEquals(500f, cellRect.width());
+ assertEquals(1000f, cellRect.height());
+ assertEquals(500f, cellRect.left);
+ assertEquals(0f, cellRect.top);
+ assertEquals(1000f, cellRect.right);
+ assertEquals(1000f, cellRect.bottom);
+
+ // cell 3 (bottom-right)
+ cellRect = it.next();
+ assertEquals(500f, cellRect.width());
+ assertEquals(1000f, cellRect.height());
+ assertEquals(500f, cellRect.left);
+ assertEquals(1000f, cellRect.top);
+ assertEquals(1000f, cellRect.right);
+ assertEquals(2000f, cellRect.bottom);
+ }
+
+ @Test
+ public void testSingleRowIteration() throws Exception {
+ // square table, both rows and columns defined:
+ TableModel model = new DynamicTableModel(0, 1);
+ RectF tableRect = new RectF(0, 0, 1000, 1000);
+ int createdCells = 4;
+ Iterator<RectF> it = model.getIterator(tableRect, createdCells);
+
+
+
+ // 2x2 fixed:
+ // cell 0 (top-left
+ RectF cellRect = it.next();
+ assertEquals(250f, cellRect.width());
+ assertEquals(1000f, cellRect.height());
+ assertEquals(0f, cellRect.left);
+ assertEquals(0f, cellRect.top);
+ assertEquals(250f, cellRect.right);
+ assertEquals(1000f, cellRect.bottom);
+
+ // cell 1
+ cellRect = it.next();
+ assertEquals(250f, cellRect.width());
+ assertEquals(1000f, cellRect.height());
+ assertEquals(250f, cellRect.left);
+ assertEquals(0f, cellRect.top);
+ assertEquals(500f, cellRect.right);
+ assertEquals(1000f, cellRect.bottom);
+
+ // cell 2
+ cellRect = it.next();
+ assertEquals(250f, cellRect.width());
+ assertEquals(1000f, cellRect.height());
+ assertEquals(500f, cellRect.left);
+ assertEquals(0f, cellRect.top);
+ assertEquals(750f, cellRect.right);
+ assertEquals(1000f, cellRect.bottom);
+
+ // cell 3
+ cellRect = it.next();
+ assertEquals(250f, cellRect.width());
+ assertEquals(1000f, cellRect.height());
+ assertEquals(750f, cellRect.left);
+ assertEquals(0f, cellRect.top);
+ assertEquals(1000f, cellRect.right);
+ assertEquals(1000f, cellRect.bottom);
+ }
+}
diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/ui/FixedTableModelTest.java b/AndroidPlot-Core/src/test/java/com/androidplot/ui/FixedTableModelTest.java
new file mode 100644
index 0000000..42453b6
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/ui/FixedTableModelTest.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.ui;
+
+import android.graphics.RectF;
+import com.androidplot.mock.MockRectF;
+import com.androidplot.ui.FixedTableModel;
+import com.androidplot.ui.TableOrder;
+import mockit.*;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Iterator;
+
+import static junit.framework.Assert.*;
+
+public class FixedTableModelTest {
+ @Before
+ public void setUp() throws Exception {
+ Mockit.setUpMocks(MockRectF.class);
+ }
+
+ @Test
+ public void testConstructor() throws Exception {
+ FixedTableModel model = new FixedTableModel(100, 100, null);
+ // TODO
+ }
+
+ @Test
+ public void testIterator() throws Exception {
+ FixedTableModel model = new FixedTableModel(100, 100, TableOrder.COLUMN_MAJOR);
+
+ RectF tableRect = new RectF(0, 0, 1000, 2000);
+
+ Iterator<RectF> it = model.getIterator(tableRect, 5);
+
+ assertTrue(it.hasNext());
+ RectF cellRect = it.next();
+
+ assertTrue(it.hasNext());
+ cellRect = it.next();
+
+ assertTrue(it.hasNext());
+ cellRect = it.next();
+
+ assertTrue(it.hasNext());
+ cellRect = it.next();
+
+ assertTrue(it.hasNext());
+ cellRect = it.next();
+
+ assertFalse(it.hasNext());
+ try {
+ cellRect = it.next();
+ fail("Expected IndexOutOfBoundsException");
+ } catch(IndexOutOfBoundsException ex) {
+ // this was expected
+ }
+ }
+
+ @Test
+ public void testColumnMajor() throws Exception {
+ FixedTableModel model = new FixedTableModel(300, 500, TableOrder.COLUMN_MAJOR);
+
+ RectF tableRect = new RectF(0, 0, 1000, 2000);
+
+ // test the numElement limit:
+ Iterator<RectF> it = model.getIterator(tableRect, 5);
+ assertTrue(it.hasNext());
+ RectF cellRect = it.next();
+ assertEquals(0f, cellRect.left);
+ assertEquals(0f, cellRect.top);
+ assertEquals(300f, cellRect.right);
+ assertEquals(500f, cellRect.bottom);
+
+ assertTrue(it.hasNext());
+ cellRect = it.next();
+ assertEquals(300f, cellRect.left);
+ assertEquals(0f, cellRect.top);
+ assertEquals(600f, cellRect.right);
+ assertEquals(500f, cellRect.bottom);
+
+ assertTrue(it.hasNext());
+ cellRect = it.next();
+ assertEquals(600f, cellRect.left);
+ assertEquals(0f, cellRect.top);
+ assertEquals(900f, cellRect.right);
+ assertEquals(500f, cellRect.bottom);
+
+ assertTrue(it.hasNext());
+ cellRect = it.next();
+ assertEquals(0f, cellRect.left);
+ assertEquals(500f, cellRect.top);
+ assertEquals(300f, cellRect.right);
+ assertEquals(1000f, cellRect.bottom);
+
+ assertTrue(it.hasNext());
+ cellRect = it.next();
+ assertEquals(300f, cellRect.left);
+ assertEquals(500f, cellRect.top);
+ assertEquals(600f, cellRect.right);
+ assertEquals(1000f, cellRect.bottom);
+
+ assertFalse(it.hasNext());
+
+ // test border limit:
+ it = model.getIterator(tableRect, 25);
+ assertTrue(it.hasNext());
+ cellRect = it.next();
+ assertEquals(0f, cellRect.left);
+ assertEquals(0f, cellRect.top);
+ assertEquals(300f, cellRect.right);
+ assertEquals(500f, cellRect.bottom);
+
+ assertTrue(it.hasNext());
+ cellRect = it.next();
+ assertEquals(300f, cellRect.left);
+ assertEquals(0f, cellRect.top);
+ assertEquals(600f, cellRect.right);
+ assertEquals(500f, cellRect.bottom);
+
+ assertTrue(it.hasNext());
+ cellRect = it.next();
+ assertEquals(600f, cellRect.left);
+ assertEquals(0f, cellRect.top);
+ assertEquals(900f, cellRect.right);
+ assertEquals(500f, cellRect.bottom);
+
+ assertTrue(it.hasNext());
+ cellRect = it.next();
+ assertEquals(0f, cellRect.left);
+ assertEquals(500f, cellRect.top);
+ assertEquals(300f, cellRect.right);
+ assertEquals(1000f, cellRect.bottom);
+
+ assertTrue(it.hasNext());
+ cellRect = it.next();
+ assertEquals(300f, cellRect.left);
+ assertEquals(500f, cellRect.top);
+ assertEquals(600f, cellRect.right);
+ assertEquals(1000f, cellRect.bottom);
+
+ assertTrue(it.hasNext());
+ cellRect = it.next();
+ assertEquals(600f, cellRect.left);
+ assertEquals(500f, cellRect.top);
+ assertEquals(900f, cellRect.right);
+ assertEquals(1000f, cellRect.bottom);
+
+ assertTrue(it.hasNext());
+ cellRect = it.next();
+ assertEquals(0f, cellRect.left);
+ assertEquals(1000f, cellRect.top);
+ assertEquals(300f, cellRect.right);
+ assertEquals(1500f, cellRect.bottom);
+
+ assertTrue(it.hasNext());
+ cellRect = it.next();
+ assertEquals(300f, cellRect.left);
+ assertEquals(1000f, cellRect.top);
+ assertEquals(600f, cellRect.right);
+ assertEquals(1500f, cellRect.bottom);
+
+ assertTrue(it.hasNext());
+ cellRect = it.next();
+ assertEquals(600f, cellRect.left);
+ assertEquals(1000f, cellRect.top);
+ assertEquals(900f, cellRect.right);
+ assertEquals(1500f, cellRect.bottom);
+
+ assertTrue(it.hasNext());
+ cellRect = it.next();
+ assertEquals(0f, cellRect.left);
+ assertEquals(1500f, cellRect.top);
+ assertEquals(300f, cellRect.right);
+ assertEquals(2000f, cellRect.bottom);
+
+ assertTrue(it.hasNext());
+ cellRect = it.next();
+ assertEquals(300f, cellRect.left);
+ assertEquals(1500f, cellRect.top);
+ assertEquals(600f, cellRect.right);
+ assertEquals(2000f, cellRect.bottom);
+
+ assertTrue(it.hasNext());
+ cellRect = it.next();
+ assertEquals(600f, cellRect.left);
+ assertEquals(1500f, cellRect.top);
+ assertEquals(900f, cellRect.right);
+ assertEquals(2000f, cellRect.bottom);
+
+ //we've reached the limit
+ assertFalse(it.hasNext());
+ }
+
+ @Test
+ public void testRowMajor() throws Exception {
+ FixedTableModel model = new FixedTableModel(300, 500, TableOrder.ROW_MAJOR);
+
+ RectF tableRect = new RectF(0, 0, 1000, 2000);
+
+ // test the numElement limit:
+ Iterator<RectF> it = model.getIterator(tableRect, 5);
+ assertTrue(it.hasNext());
+ RectF cellRect = it.next();
+ assertEquals(0f, cellRect.left);
+ assertEquals(0f, cellRect.top);
+ assertEquals(300f, cellRect.right);
+ assertEquals(500f, cellRect.bottom);
+
+ assertTrue(it.hasNext());
+ cellRect = it.next();
+ assertEquals(0f, cellRect.left);
+ assertEquals(500f, cellRect.top);
+ assertEquals(300f, cellRect.right);
+ assertEquals(1000f, cellRect.bottom);
+
+ assertTrue(it.hasNext());
+ cellRect = it.next();
+ assertEquals(0f, cellRect.left);
+ assertEquals(1000f, cellRect.top);
+ assertEquals(300f, cellRect.right);
+ assertEquals(1500f, cellRect.bottom);
+
+ assertTrue(it.hasNext());
+ cellRect = it.next();
+ assertEquals(0f, cellRect.left);
+ assertEquals(1500f, cellRect.top);
+ assertEquals(300f, cellRect.right);
+ assertEquals(2000f, cellRect.bottom);
+
+ // next column over
+ assertTrue(it.hasNext());
+ cellRect = it.next();
+ assertEquals(300f, cellRect.left);
+ assertEquals(0f, cellRect.top);
+ assertEquals(600f, cellRect.right);
+ assertEquals(500f, cellRect.bottom);
+ }
+}
\ No newline at end of file
diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/util/ConfiguratorTest.java b/AndroidPlot-Core/src/test/java/com/androidplot/util/ConfiguratorTest.java
new file mode 100644
index 0000000..b5790e8
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/util/ConfiguratorTest.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.util;
+
+import android.content.Context;
+import android.util.Log;
+import com.androidplot.mock.MockContext;
+import mockit.Mockit;
+import mockit.UsingMocksAndStubs;
+import org.junit.Test;
+
+import java.lang.reflect.Method;
+
+import static junit.framework.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+@UsingMocksAndStubs({Log.class})
+public class ConfiguratorTest {
+
+ class A {
+ int d = 0;
+
+ public int getD() {
+ return d;
+ }
+
+ public void setD(int d) {
+ this.d = d;
+ }
+ }
+
+ class B {
+ A a = new A();
+
+ public A getA() {
+ return a;
+ }
+
+ public void setA(A a) {
+ this.a = a;
+ }
+ }
+
+ class C {
+ B b = new B();
+
+ public B getB() {
+ return b;
+ }
+
+ public void setB(B a) {
+ this.b = b;
+ }
+ }
+
+ @org.junit.Before
+ public void setUp() throws Exception {
+
+ }
+
+ @org.junit.After
+ public void tearDown() throws Exception {
+
+ }
+
+
+ @Test
+ public void testGetFieldAt() throws Exception {
+ Context context = Mockit.setUpMock(new MockContext());
+ C c = new C();
+ assertEquals(c, Configurator.getObjectContaining(c, "b"));
+ assertEquals(c.getB(), Configurator.getObjectContaining(c, "b.a"));
+ assertEquals(c.getB().getA(), Configurator.getObjectContaining(c, "b.a.d"));
+ }
+
+ @Test
+ public void testGetSetter() throws Exception {
+ Context context = Mockit.setUpMock(new MockContext());
+ C c = new C();
+
+ Method m = Configurator.getSetter(c.getClass(), "b");
+ assertEquals(1, m.getParameterTypes().length);
+ assertEquals(B.class, m.getParameterTypes()[0]);
+ }
+}
diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/util/ListOrganizerTest.java b/AndroidPlot-Core/src/test/java/com/androidplot/util/ListOrganizerTest.java
new file mode 100644
index 0000000..5f4e240
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/util/ListOrganizerTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.util;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.LinkedList;
+
+import static junit.framework.Assert.assertEquals;
+
+public class ListOrganizerTest {
+ @Before
+ public void setUp() throws Exception {
+
+ }
+
+ @After
+ public void tearDown() throws Exception {
+
+ }
+
+ @Test
+ public void testMoveToTop() throws Exception {
+
+ }
+
+ @Test
+ public void testMoveAbove() throws Exception {
+
+ }
+
+ @Test
+ public void testMoveBeneath() throws Exception {
+
+ }
+
+ @Test
+ public void testMoveToBottom() throws Exception {
+ Object obj1 = new Object();
+ Object obj2 = new Object();
+ Object obj3 = new Object();
+ LinkedList list = new LinkedList();
+
+ list.add(obj1);
+ list.add(obj2);
+ list.add(obj3);
+
+ assertEquals(obj1, list.getFirst());
+ assertEquals(obj3, list.getLast());
+
+ ListOrganizer organizer = new ListOrganizer(list);
+
+ organizer.moveToBottom(obj3);
+
+ assertEquals(obj2, list.getLast());
+ assertEquals(obj3, list.getFirst());
+
+ }
+
+ @Test
+ public void testMoveUp() throws Exception {
+
+ }
+
+ @Test
+ public void testMoveDown() throws Exception {
+
+ }
+
+ @Test
+ public void testAddFirst() throws Exception {
+
+ }
+
+ @Test
+ public void testAddLast() throws Exception {
+
+ }
+}
diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/util/ValPixConverterTest.java b/AndroidPlot-Core/src/test/java/com/androidplot/util/ValPixConverterTest.java
new file mode 100644
index 0000000..030e30c
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/util/ValPixConverterTest.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.util;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import static junit.framework.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+public class ValPixConverterTest {
+ @org.junit.Before
+ public void setUp() throws Exception {
+
+ }
+
+ @org.junit.After
+ public void tearDown() throws Exception {
+
+ }
+
+ /*
+ @org.junit.Test
+ public void testIndexToPix() throws Exception {
+ int sizeInPix = 100;
+ int itemCount = 10;
+ assertEquals(10.0f, ValPixConverter.indexToPix(1, itemCount, sizeInPix));
+
+ try {
+ ValPixConverter.indexToPix(100, 10, 100);
+ fail("IndexOutOfBoundsException expected.");
+ } catch(IndexOutOfBoundsException ex) {
+
+ }
+
+ }
+ */
+
+ @org.junit.Test
+ public void testValToPix() throws Exception {
+ int sizeInPix = 100;
+ int min = 0;
+ int max = 100;
+
+ int value = 50;
+
+ assertEquals(50.0f, ValPixConverter.valToPix(value, min, max, sizeInPix, true));
+
+ // should be closer to the top:
+ // (remember that 0,0 is the top left pixel)
+ value = 75;
+ assertEquals(25.0f, ValPixConverter.valToPix(value, min, max, sizeInPix, true));
+
+ // should be at the very top:
+ value = 100;
+ assertEquals(0.0f, ValPixConverter.valToPix(value, min, max, sizeInPix, true));
+
+ // should be at the very top:
+ value = 0;
+ assertEquals(100.0f, ValPixConverter.valToPix(value, min, max, sizeInPix, true));
+
+ // should be smack in the middle:
+ min = -100;
+ value = 0;
+ assertEquals(50.0f, ValPixConverter.valToPix(value, min, max, sizeInPix, true));
+
+ // should be at the very bottom:
+ value = 100;
+ assertEquals(0.0f, ValPixConverter.valToPix(value, min, max, sizeInPix, true));
+
+ // should be in the middle:
+ value = 0;
+ assertEquals(50.0f, ValPixConverter.valToPix(value, min, max, sizeInPix, true));
+
+ min = -100;
+ max = 100;
+ sizeInPix = 200;
+ assertEquals(0f, ValPixConverter.valToPix(-100, min, max, sizeInPix, false));
+ assertEquals(200f, ValPixConverter.valToPix(100, min, max, sizeInPix, false));
+ }
+
+ @org.junit.Test
+ public void testpixToVal() throws Exception {
+ int sizeInPix = 100;
+ int min = 0;
+ int max = 100;
+
+ double value = 50;
+
+ float pixel = ValPixConverter.valToPix(value, min, max, sizeInPix, true);
+ assertEquals(value, ValPixConverter.pixToVal(pixel, min, max, sizeInPix, true));
+
+ value = 75;
+ pixel = ValPixConverter.valToPix(value, min, max, sizeInPix, true);
+ assertEquals(value, ValPixConverter.pixToVal(pixel, min, max, sizeInPix, true));
+
+
+ min = -100;
+ value = 50;
+ pixel = ValPixConverter.valToPix(value, min, max, sizeInPix, true);
+ assertEquals(value, ValPixConverter.pixToVal(pixel, min, max, sizeInPix, true));
+
+ min = -100;
+ value = 60;
+ pixel = ValPixConverter.valToPix(value, min, max, sizeInPix, true);
+ assertEquals(value, ValPixConverter.pixToVal(pixel, min, max, sizeInPix, true));
+
+ min = 20;
+ value = 50;
+ pixel = ValPixConverter.valToPix(value, min, max, sizeInPix, true);
+ assertEquals(value, ValPixConverter.pixToVal(pixel, min, max, sizeInPix, true));
+
+ try {
+ ValPixConverter.pixToVal(-5, 0, 0, 0, true);
+ fail("IllegalArgumentException expected.");
+ } catch(IllegalArgumentException ex) {
+
+ }
+
+
+ }
+
+
+ @Test
+ public void testValPerPix() {
+ //double result = ;
+ assertEquals(1.0, ValPixConverter.valPerPix(0, 100, 100));
+ double expected = 200d/100;
+ assertEquals(expected, ValPixConverter.valPerPix(100, 300, 100));
+ expected = 50d/100;
+ assertEquals(expected, ValPixConverter.valPerPix(0, 50, 100));
+ expected = 200d/100;
+ assertEquals(expected, ValPixConverter.valPerPix(-100, 100, 100));
+ }
+}
diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/xy/RectRegionTest.java b/AndroidPlot-Core/src/test/java/com/androidplot/xy/RectRegionTest.java
new file mode 100644
index 0000000..9ae3877
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/xy/RectRegionTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.xy;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class RectRegionTest {
+ @Before
+ public void setUp() throws Exception {
+
+ }
+
+ @After
+ public void tearDown() throws Exception {
+
+ }
+
+ @Test
+ public void testContainsPoint() throws Exception {
+
+ }
+
+ @Test
+ public void testContainsValue() throws Exception {
+
+ }
+
+ @Test
+ public void testContainsDomainValue() throws Exception {
+
+ }
+
+ @Test
+ public void testContainsRangeValue() throws Exception {
+
+ }
+
+
+
+ @Test
+ public void testIsWithin() throws Exception {
+
+ RectRegion region1 = new RectRegion(0, 100, 0, 100, "");
+
+ RectRegion region2 = new RectRegion(5, 10, 5, 10, "");
+
+ assertTrue(region1.intersects(region2));
+ assertTrue(region2.intersects(region1));
+
+ RectRegion region3 = new RectRegion(101, 200, 101, 200, "");
+ assertFalse(region1.intersects(region3));
+ assertFalse(region3.intersects(region1));
+
+ RectRegion region4 = new RectRegion(99, 109, 99, 109, "");
+ assertTrue(region1.intersects(region4));
+ assertTrue(region4.intersects(region1));
+
+ // negative numbers:
+ RectRegion region5 = new RectRegion(-100, 1, -100, 1, "");
+ assertTrue(region1.intersects(region5));
+ assertTrue(region5.intersects(region1));
+ }
+}
diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/xy/SimpleXYSeriesTest.java b/AndroidPlot-Core/src/test/java/com/androidplot/xy/SimpleXYSeriesTest.java
new file mode 100644
index 0000000..3179f6d
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/xy/SimpleXYSeriesTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.xy;
+
+import android.util.Pair;
+import mockit.UsingMocksAndStubs;
+import org.junit.Test;
+
+import java.util.Arrays;
+
+import static junit.framework.Assert.assertEquals;
+
+@UsingMocksAndStubs({Pair.class})
+
+public class SimpleXYSeriesTest {
+
+ @Test
+ public void testYValsOnlyConstructor() throws Exception {
+ Number[] yVals = {5, 6, 7, 8, 9};
+ SimpleXYSeries series = new SimpleXYSeries(Arrays.asList(yVals), SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "test");
+
+ assertEquals(yVals[0], series.getY(0));
+ assertEquals(yVals[1], series.getY(1));
+ assertEquals(yVals[2], series.getY(2));
+ assertEquals(yVals[3], series.getY(3));
+ assertEquals(yVals[4], series.getY(4));
+
+ assertEquals(0, series.getX(0));
+ assertEquals(1, series.getX(1));
+ assertEquals(2, series.getX(2));
+ assertEquals(3, series.getX(3));
+ assertEquals(4, series.getX(4));
+ }
+
+ @Test
+ public void testXYInterleavedConstructor() throws Exception {
+ Number[] yVals = {55, 5, 66, 6, 77, 7, 88, 8, 99, 9};
+ SimpleXYSeries series = new SimpleXYSeries(Arrays.asList(yVals), SimpleXYSeries.ArrayFormat.XY_VALS_INTERLEAVED, "test");
+
+ assertEquals(5, series.getY(0));
+ assertEquals(6, series.getY(1));
+ assertEquals(7, series.getY(2));
+ assertEquals(8, series.getY(3));
+ assertEquals(9, series.getY(4));
+
+ assertEquals(55, series.getX(0));
+ assertEquals(66, series.getX(1));
+ assertEquals(77, series.getX(2));
+ assertEquals(88, series.getX(3));
+ assertEquals(99, series.getX(4));
+ }
+
+ @Test
+ public void testTwoListConstructor() throws Exception {
+ Number[] yVals = {5, 6, 7, 8, 9};
+ Number[] xVals = {1, 2, 3, 4, 5};
+ SimpleXYSeries series = new SimpleXYSeries(Arrays.asList(xVals), Arrays.asList(yVals), "test");
+
+ assertEquals(5, series.getY(0));
+ assertEquals(6, series.getY(1));
+ assertEquals(7, series.getY(2));
+ assertEquals(8, series.getY(3));
+ assertEquals(9, series.getY(4));
+
+ assertEquals(1, series.getX(0));
+ assertEquals(2, series.getX(1));
+ assertEquals(3, series.getX(2));
+ assertEquals(4, series.getX(3));
+ assertEquals(5, series.getX(4));
+ }
+
+ @Test
+ public void testPushPopStuff() throws Exception {
+ Number[] yVals = {5, 6, 7, 8, 9};
+ Number[] xVals = {1, 2, 3, 4, 5};
+ SimpleXYSeries series = new SimpleXYSeries(Arrays.asList(xVals), Arrays.asList(yVals), "test");
+
+ // chop off the tail:
+ series.removeLast();
+ assertEquals(8, series.getY(series.size()-1));
+ assertEquals(4, series.getX(series.size()-1));
+
+ // chop off the head:
+ series.removeFirst();
+ assertEquals(6, series.getY(0));
+ assertEquals(2, series.getX(0));
+
+ // add to the tail:
+ series.addLast(22, 33);
+ assertEquals(33, series.getY(series.size()-1));
+ assertEquals(22, series.getX(series.size()-1));
+
+ // add to the head:
+ series.addFirst(55, 66);
+ assertEquals(66, series.getY(0));
+ assertEquals(55, series.getX(0));
+ }
+
+ @Test
+ public void testSet() throws Exception {
+ Number[] yVals = {5, 6, 7, 8, 9};
+ Number[] xVals = {1, 2, 3, 4, 5};
+ SimpleXYSeries series = new SimpleXYSeries(Arrays.asList(xVals), Arrays.asList(yVals), "test");
+
+ int size = series.size();
+
+ series.setX(22, 2);
+ assertEquals(22, series.getX(2));
+
+ // make sure size has not changed:
+ assertEquals(size, series.size());
+
+ series.setY(23, 2);
+ assertEquals(23, series.getY(2));
+
+ // make sure size has not changed:
+ assertEquals(size, series.size());
+ }
+
+}
diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/xy/XYLegendWidgetTest.java b/AndroidPlot-Core/src/test/java/com/androidplot/xy/XYLegendWidgetTest.java
new file mode 100644
index 0000000..35f091d
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/xy/XYLegendWidgetTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.xy;
+
+import android.content.Context;
+import android.graphics.*;
+import android.os.Handler;
+import android.util.Log;
+import android.util.Pair;
+import android.view.View;
+import com.androidplot.Plot;
+import com.androidplot.mock.*;
+import com.androidplot.ui.SeriesRenderer;
+import com.androidplot.ui.SizeMetric;
+import com.androidplot.ui.SizeMetrics;
+import com.androidplot.ui.widget.TextLabelWidget;
+import com.androidplot.ui.widget.Widget;
+import com.androidplot.util.FontUtils;
+import com.androidplot.util.PixelUtils;
+import com.androidplot.util.ValPixConverter;
+import mockit.*;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+
+import static junit.framework.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+@UsingMocksAndStubs({Log.class,View.class,Handler.class,Paint.class,Color.class, Rect.class,
+ FontUtils.class, Paint.FontMetrics.class,Bitmap.class, Pair.class})
+
+public class XYLegendWidgetTest {
+
+ @MockClass(realClass = Context.class)
+ public static class MockContext {}
+
+ @MockClass(realClass = TextLabelWidget.class)
+ public static class MockTextLabelWidget {
+ @Mock
+ public void doOnDraw(Canvas canvas, RectF widgetRect) {}
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ Mockit.setUpMocks(MockCanvas.class, MockRectF.class, MockSizeMetrics.class,
+ MockPointF.class, MockLooper.class, MockPixelUtils.class, MockTextLabelWidget.class);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+
+ }
+
+
+ @Test
+ public void testDoOnDraw() throws Exception {
+ Context context = Mockit.setUpMock(new MockContext());
+ XYPlot plot = new XYPlot(context, "Test", Plot.RenderMode.USE_MAIN_THREAD);
+
+ SimpleXYSeries s1 = new SimpleXYSeries((Arrays.asList(1, 2, 3)),
+ SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "s1");
+
+ plot.addSeries(s1, new LineAndPointFormatter(
+ Color.RED, Color.GREEN, Color.BLUE, (PointLabelFormatter)null));
+
+ assertEquals(1, plot.getSeriesSet().size());
+
+ Deencapsulation.invoke(plot, "onSizeChanged", 100, 100, 100, 100);
+ plot.redraw();
+ // have to manually invoke this because the invalidate()
+ // invoked by redraw() is a stub and will not result in onDraw being called.
+ Deencapsulation.invoke(plot, "onDraw", new Canvas());
+
+ plot.removeSeries(s1);
+ assertEquals(0, plot.getSeriesSet().size());
+ plot.addSeries(s1, new BarFormatter(Color.RED, Color.GREEN));
+ plot.redraw();
+
+ // throws NullPointerException before fix
+ // for ANDROIDPLOT-166 was applied.
+ Deencapsulation.invoke(plot, "onDraw", new Canvas());
+
+ }
+
+}
diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/xy/XYPlotTest.java b/AndroidPlot-Core/src/test/java/com/androidplot/xy/XYPlotTest.java
new file mode 100644
index 0000000..04d5945
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/xy/XYPlotTest.java
@@ -0,0 +1,478 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.xy;
+
+import android.content.Context;
+import android.graphics.*;
+import android.os.Handler;
+import android.util.Log;
+import android.view.View;
+import com.androidplot.Plot;
+import com.androidplot.PlotTest;
+import com.androidplot.mock.MockContext;
+import com.androidplot.mock.MockPaint;
+import com.androidplot.ui.widget.TextLabelWidget;
+import com.androidplot.util.Configurator;
+import com.androidplot.util.FontUtils;
+import com.androidplot.util.PixelUtils;
+import mockit.*;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+
+import static junit.framework.Assert.assertEquals;
+
+@UsingMocksAndStubs({Log.class,View.class,Context.class,Handler.class,Paint.class,Color.class,
+ Rect.class, RectF.class,FontUtils.class, PixelUtils.class, Canvas.class})
+
+public class XYPlotTest {
+
+ XYPlot plot; // testing
+
+ List<Integer> numList1;
+ List<Integer> numList2;
+ SimpleXYSeries series1;
+
+ @Before
+ public void setUp() throws Exception {
+ Mockit.setUpMocks(MockPaint.class,MockContext.class);
+ new MockUp<View>() {
+ @Mock int getWidth() { return 100;}
+ @Mock int getHeight() { return 100;}
+
+ };
+
+ plot = new XYPlot(null, "test");
+ numList1 = Arrays.asList(0, 1, 3, 5, 10, 15, 25, 50, 75, 100); // 10 elements
+ numList2 = Arrays.asList(-100, 0, 1, 3, 5, 10, 15, 25, 50, 75, 100, 200); // 12 elements
+ series1 = new SimpleXYSeries(numList1, SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "");
+ }
+
+ @After
+ public void tearDown() throws Exception {
+
+ }
+
+ @Test
+ public void testOriginFixedMode() throws Exception {
+ plot.addSeries(series1, new LineAndPointFormatter());
+ plot.centerOnDomainOrigin(5, 2, BoundaryMode.FIXED);
+ plot.calculateMinMaxVals();
+
+
+ assertEquals(3.0, plot.getCalculatedMinX());
+ assertEquals(7.0, plot.getCalculatedMaxX());
+ }
+
+ @Test
+ public void testOriginAutoMode() throws Exception {
+ plot.addSeries(series1, new LineAndPointFormatter());
+ plot.centerOnDomainOrigin(5);
+ plot.calculateMinMaxVals();
+ //plot.updateMinMaxVals();
+
+ assertEquals(10.0, plot.getCalculatedMaxX()); // symmetry is @ 10, not 9
+ assertEquals(0.0, plot.getCalculatedMinX());
+
+ plot.centerOnRangeOrigin(50);
+ plot.calculateMinMaxVals();
+
+ assertEquals(100.0, plot.getCalculatedMaxY());
+ assertEquals(0.0, plot.getCalculatedMinY());
+
+ }
+
+ @Test
+ public void testOriginGrowMode() throws Exception {
+ plot.addSeries(series1, new LineAndPointFormatter());
+ plot.centerOnDomainOrigin(5, null, BoundaryMode.GROW);
+ plot.calculateMinMaxVals();
+
+ assertEquals(0.0, plot.getCalculatedMinX());
+ assertEquals(10.0, plot.getCalculatedMaxX());
+
+ // introduce a larger domain set. boundaries should change
+ series1.setModel(numList2, SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);
+ plot.calculateMinMaxVals();
+
+ assertEquals(-1.0, plot.getCalculatedMinX());
+ assertEquals(11.0, plot.getCalculatedMaxX());
+
+ // revert series model back to the previous set. boundaries should remain the same
+ series1.setModel(numList1, SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);
+ plot.calculateMinMaxVals();
+
+ assertEquals(-1.0, plot.getCalculatedMinX());
+ assertEquals(11.0, plot.getCalculatedMaxX());
+ }
+
+ @Test
+ public void testOriginShrinkMode() throws Exception {
+ plot.addSeries(series1, new LineAndPointFormatter());
+ plot.centerOnDomainOrigin(5, null, BoundaryMode.SHRINNK);
+ plot.calculateMinMaxVals();
+
+ assertEquals(0.0, plot.getCalculatedMinX());
+ assertEquals(10.0, plot.getCalculatedMaxX());
+
+ // update with more extreme values...nothing should change in shrink mode:
+ series1.setModel(numList2,SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);
+
+ assertEquals(0.0, plot.getCalculatedMinX());
+ assertEquals(10.0, plot.getCalculatedMaxX());
+
+ }
+
+
+ // Ifor not sure about filling in test stubs just going to do my own stuff instead.
+ @Test
+ public void testsetDomainBoundaries() throws Exception {
+ plot.addSeries(series1, new LineAndPointFormatter());
+ plot.calculateMinMaxVals();
+
+ // default to auto so check them
+ assertEquals(0, plot.getCalculatedMinX());
+ assertEquals(9, plot.getCalculatedMaxX());
+
+ plot.setDomainBoundaries(2, BoundaryMode.FIXED, 8, BoundaryMode.FIXED);
+ plot.calculateMinMaxVals();
+
+ // fixed
+ assertEquals(2, plot.getCalculatedMinX());
+ assertEquals(8, plot.getCalculatedMaxX());
+
+ // back to auto
+ plot.setDomainBoundaries(2, BoundaryMode.AUTO, 8, BoundaryMode.AUTO);
+ plot.calculateMinMaxVals();
+
+ // check again
+ assertEquals(0, plot.getCalculatedMinX());
+ assertEquals(9, plot.getCalculatedMaxX());
+
+ // we are not testing MinY well with this dataset.
+ // try grow
+ plot.setDomainBoundaries(2, BoundaryMode.GROW, 8, BoundaryMode.GROW);
+ plot.calculateMinMaxVals();
+
+ // check inital
+ assertEquals(0, plot.getCalculatedMinX());
+ assertEquals(9, plot.getCalculatedMaxX());
+
+ // update with more extreme values...
+ series1.setModel(numList2,SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);
+ plot.calculateMinMaxVals();
+
+ // after growing
+ assertEquals(0, plot.getCalculatedMinX());
+ assertEquals(11, plot.getCalculatedMaxX());
+
+ // back to previous
+ series1.setModel(numList1,SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);
+ plot.calculateMinMaxVals();
+
+ // should not of changed.
+ assertEquals(0, plot.getCalculatedMinX());
+ assertEquals(11, plot.getCalculatedMaxX());
+
+ // back to big
+ series1.setModel(numList2,SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);
+
+ plot.setDomainBoundaries(2, BoundaryMode.SHRINNK, 8, BoundaryMode.SHRINNK);
+ plot.calculateMinMaxVals();
+
+ // check inital
+ assertEquals(0, plot.getCalculatedMinX());
+ assertEquals(11, plot.getCalculatedMaxX());
+
+ // now small
+ series1.setModel(numList1,SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);
+ plot.calculateMinMaxVals();
+
+ // after shrinking
+ assertEquals(0, plot.getCalculatedMinX());
+ assertEquals(9, plot.getCalculatedMaxX());
+
+ // back to previous
+ series1.setModel(numList2,SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);
+ plot.calculateMinMaxVals();
+
+ // should not of changed.
+ assertEquals(0, plot.getCalculatedMinX());
+ assertEquals(9, plot.getCalculatedMaxX());
+
+ // back to auto
+ plot.setDomainBoundaries(2, BoundaryMode.AUTO, 8, BoundaryMode.AUTO);
+ plot.calculateMinMaxVals();
+
+ // should of changed.
+ assertEquals(0, plot.getCalculatedMinX());
+ assertEquals(11, plot.getCalculatedMaxX());
+ }
+
+ @Test
+ public void testsetRangeBoundaries() throws Exception {
+ plot.addSeries(series1, new LineAndPointFormatter());
+ plot.calculateMinMaxVals();
+
+ // default to auto so check them
+ assertEquals(0, plot.getCalculatedMinY());
+ assertEquals(100, plot.getCalculatedMaxY());
+
+ plot.setRangeBoundaries(5, BoundaryMode.FIXED, 80, BoundaryMode.FIXED);
+ plot.calculateMinMaxVals();
+
+ // fixed
+ assertEquals(5, plot.getCalculatedMinY());
+ assertEquals(80, plot.getCalculatedMaxY());
+
+ // back to auto
+ plot.setRangeBoundaries(2, BoundaryMode.AUTO, 8, BoundaryMode.AUTO);
+ plot.calculateMinMaxVals();
+
+ // check again
+ assertEquals(0, plot.getCalculatedMinY());
+ assertEquals(100, plot.getCalculatedMaxY());
+
+ // try grow
+ plot.setRangeBoundaries(2, BoundaryMode.GROW, 8, BoundaryMode.GROW);
+ plot.calculateMinMaxVals();
+
+ // check inital
+ assertEquals(0, plot.getCalculatedMinY());
+ assertEquals(100, plot.getCalculatedMaxY());
+
+ // update with more extreme values...
+ series1.setModel(numList2,SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);
+ plot.calculateMinMaxVals();
+
+ // after growing
+ assertEquals(-100, plot.getCalculatedMinY());
+ assertEquals(200, plot.getCalculatedMaxY());
+
+ // back to previous
+ series1.setModel(numList1,SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);
+ plot.calculateMinMaxVals();
+
+ // should not of changed.
+ assertEquals(-100, plot.getCalculatedMinY());
+ assertEquals(200, plot.getCalculatedMaxY());
+
+ // back to big
+ series1.setModel(numList2,SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);
+
+ plot.setRangeBoundaries(2, BoundaryMode.SHRINNK, 8, BoundaryMode.SHRINNK);
+ plot.calculateMinMaxVals();
+
+ // check inital
+ assertEquals(-100, plot.getCalculatedMinY());
+ assertEquals(200, plot.getCalculatedMaxY());
+
+ // now small
+ series1.setModel(numList1,SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);
+ plot.calculateMinMaxVals();
+
+ // after shrinking
+ assertEquals(0, plot.getCalculatedMinY());
+ assertEquals(100, plot.getCalculatedMaxY());
+
+ // back to previous
+ series1.setModel(numList2,SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);
+ plot.calculateMinMaxVals();
+
+ // should not of changed.
+ assertEquals(0, plot.getCalculatedMinY());
+ assertEquals(100, plot.getCalculatedMaxY());
+
+ // back to auto
+ plot.setRangeBoundaries(2, BoundaryMode.AUTO, 8, BoundaryMode.AUTO);
+ plot.calculateMinMaxVals();
+
+ // should of changed.
+ assertEquals(-100, plot.getCalculatedMinY());
+ assertEquals(200, plot.getCalculatedMaxY());
+ }
+
+ @Test
+ public void testSetDomainRightMinMax() throws Exception {
+ plot.addSeries(series1, new LineAndPointFormatter());
+ plot.calculateMinMaxVals();
+
+ // default to auto so check them
+ assertEquals(0, plot.getCalculatedMinX());
+ assertEquals(9, plot.getCalculatedMaxX());
+
+ plot.setDomainRightMax(10);
+ plot.calculateMinMaxVals();
+
+ // same values.
+ assertEquals(0, plot.getCalculatedMinX());
+ assertEquals(9, plot.getCalculatedMaxX());
+
+ series1.setModel(numList2,SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);
+ plot.calculateMinMaxVals();
+
+ // on RightMax
+ assertEquals(0, plot.getCalculatedMinX());
+ assertEquals(10, plot.getCalculatedMaxX());
+
+ plot.setDomainRightMax(null);
+ plot.calculateMinMaxVals();
+
+ // back to full
+ assertEquals(0, plot.getCalculatedMinX());
+ assertEquals(11, plot.getCalculatedMaxX());
+
+ // now the RightMin
+ plot.setDomainRightMin(10);
+ plot.calculateMinMaxVals();
+
+ // still to full
+ assertEquals(0, plot.getCalculatedMinX());
+ assertEquals(11, plot.getCalculatedMaxX());
+
+ // small list
+ series1.setModel(numList1,SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);
+ plot.calculateMinMaxVals();
+
+ // on RightMin
+ assertEquals(0, plot.getCalculatedMinX());
+ assertEquals(10, plot.getCalculatedMaxX());
+
+ // now off again
+ plot.setDomainRightMin(null);
+ plot.calculateMinMaxVals();
+
+ // small values.
+ assertEquals(0, plot.getCalculatedMinX());
+ assertEquals(9, plot.getCalculatedMaxX());
+ }
+
+ @Test
+ public void testSetRangeTopBottomMinMax() throws Exception {
+ plot.addSeries(series1, new LineAndPointFormatter());
+ plot.calculateMinMaxVals();
+
+ // default to auto so check them
+ assertEquals(0, plot.getCalculatedMinY());
+ assertEquals(100, plot.getCalculatedMaxY());
+
+ plot.setRangeTopMax(110);
+ plot.setRangeBottomMin(-50);
+ plot.calculateMinMaxVals();
+
+ // same values.
+ assertEquals(0, plot.getCalculatedMinY());
+ assertEquals(100, plot.getCalculatedMaxY());
+
+ series1.setModel(numList2,SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);
+ plot.calculateMinMaxVals();
+
+ // on Limits
+ assertEquals(-50, plot.getCalculatedMinY());
+ assertEquals(110, plot.getCalculatedMaxY());
+
+ plot.setRangeTopMax(null);
+ plot.setRangeBottomMin(null);
+ plot.calculateMinMaxVals();
+
+ // back to full
+ assertEquals(-100, plot.getCalculatedMinY());
+ assertEquals(200, plot.getCalculatedMaxY());
+
+ // now the Min
+ plot.setRangeTopMin(150);
+ plot.setRangeBottomMax(-60);
+ plot.calculateMinMaxVals();
+
+ // still to full
+ assertEquals(-100, plot.getCalculatedMinY());
+ assertEquals(200, plot.getCalculatedMaxY());
+
+ // small list
+ series1.setModel(numList1,SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);
+ plot.calculateMinMaxVals();
+
+ // on Limits
+ assertEquals(-60, plot.getCalculatedMinY());
+ assertEquals(150, plot.getCalculatedMaxY());
+
+ // now off again
+ plot.setRangeTopMin(null);
+ plot.setRangeBottomMax(null);
+ plot.calculateMinMaxVals();
+
+ // small values.
+ assertEquals(0, plot.getCalculatedMinY());
+ assertEquals(100, plot.getCalculatedMaxY());
+ }
+
+ @Test
+ public void testSetDomainUpperBoundary() throws Exception {
+
+ }
+
+ @Test
+ public void testSetDomainLowerBoundary() throws Exception {
+
+ }
+
+ @Test
+ public void testSetRangeUpperBoundary() throws Exception {
+
+ }
+
+ @Test
+ public void testSetRangeLowerBoundary() throws Exception {
+
+ }
+
+ @Test
+ public void testSetDomainOrigin() throws Exception {
+
+ }
+
+ @Test
+ public void testSetRangeOrigin() throws Exception {
+
+ }
+
+ @Test
+ public void testConfigure() throws Exception {
+ //Context context = Mockit.setUpMock(new MockContext());
+ Context context = new MockContext.MockContext2();
+ HashMap<String, String> params = new HashMap<String, String>();
+ String param1 = "this is a test.";
+ String param2 = Plot.RenderMode.USE_BACKGROUND_THREAD.toString();
+ String param3 = "#FF0000";
+ params.put("title", param1);
+ params.put("renderMode", param2);
+ params.put("backgroundPaint.color", param3);
+ params.put("graphWidget.domainLabelPaint.color", param3);
+
+ Configurator.configure(context, plot, params);
+ assertEquals(param1, plot.getTitle());
+ assertEquals(Plot.RenderMode.USE_BACKGROUND_THREAD, plot.getRenderMode());
+ assertEquals(Color.parseColor(param3), plot.getBackgroundPaint().getColor());
+ assertEquals(Color.parseColor(param3), plot.getGraphWidget().getDomainLabelPaint().getColor());
+ }
+}
diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/xy/XYSeriesRendererTest.java b/AndroidPlot-Core/src/test/java/com/androidplot/xy/XYSeriesRendererTest.java
new file mode 100644
index 0000000..c412e08
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/xy/XYSeriesRendererTest.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.xy;
+
+import android.content.Context;
+import android.graphics.*;
+import android.os.Handler;
+import android.util.Log;
+import android.view.View;
+import com.androidplot.util.FontUtils;
+import com.androidplot.util.PixelUtils;
+import com.androidplot.util.ValPixConverter;
+import mockit.*;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import static junit.framework.Assert.assertEquals;
+
+@UsingMocksAndStubs({Log.class,View.class,Handler.class,Paint.class,Color.class, Rect.class, RectF.class,
+ FontUtils.class, PixelUtils.class, Canvas.class})
+
+public class XYSeriesRendererTest {
+
+ @MockClass(realClass = Context.class)
+ public static class MockContext {}
+
+ @Before
+ public void setUp() throws Exception {
+
+ }
+
+ @After
+ public void tearDown() throws Exception {
+
+ }
+
+
+ @Test
+ public void testDataToGridCorrelation() throws Exception {
+ Context context = Mockit.setUpMock(new MockContext());
+ //RectF gridRect = Mockit.setUpMock(new MockRectF());
+ //Mockit.setUpMock(RectF.class, new MockRectF());
+ //@MockClass(realClass = RectF.class)
+ new MockUp<RectF>()
+ {
+ final float left = 5;
+ final float top = 5;
+ final float right = 104;
+ final float bottom = 104;
+
+ @Mock void $init() {}
+
+ @Mock float width() {return 100;}
+ @Mock float height() {return 100;}
+
+ };
+ RectF gridRect = new RectF();
+ XYPlot plot = new XYPlot(context, "Test");
+ plot.setDomainStepMode(XYStepMode.SUBDIVIDE);
+ plot.setDomainStepValue(10);
+ plot.setDomainBoundaries(0, 100, BoundaryMode.FIXED);
+ plot.setRangeBoundaries(0, 100, BoundaryMode.FIXED);
+ plot.calculateMinMaxVals();
+ XYStep domainStep = XYStepCalculator.getStep(plot, XYAxisType.DOMAIN, gridRect, plot.getCalculatedMinX().doubleValue(), plot.getCalculatedMaxX().doubleValue());
+
+ float width = gridRect.width();
+
+ int x = 0;
+ float val = ValPixConverter.valToPix(x, 0, 9, gridRect.width(), false) + (gridRect.left);
+
+ assertEquals(val, domainStep.getStepPix()*x);
+
+ x = 1;
+ val = ValPixConverter.valToPix(x, 0, 9, gridRect.width(), false) + (gridRect.left);
+
+ assertEquals(val, domainStep.getStepPix()*x);
+
+ x = 9;
+ val = ValPixConverter.valToPix(x, 0, 9, gridRect.width(), false) + (gridRect.left);
+
+ assertEquals(val, domainStep.getStepPix()*x);
+ }
+
+}
diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/xy/XYStepCalculatorTest.java b/AndroidPlot-Core/src/test/java/com/androidplot/xy/XYStepCalculatorTest.java
new file mode 100644
index 0000000..1f93977
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/xy/XYStepCalculatorTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.xy;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import static junit.framework.Assert.assertEquals;
+
+public class XYStepCalculatorTest {
+ @Before
+ public void setUp() throws Exception {
+
+ }
+
+ @After
+ public void tearDown() throws Exception {
+
+ }
+
+ @Test
+ public void testGetStep() throws Exception {
+
+ }
+
+ @Test
+ public void testSubdivide() throws Exception {
+ int numSegments = 10;
+ float plotSize = 100;
+ double minVal = 0;
+ double maxVal = 100;
+ XYStep step = XYStepCalculator.getStep(XYStepMode.SUBDIVIDE, plotSize, numSegments, minVal, maxVal);
+
+ assertEquals(plotSize/(numSegments-1), step.getStepPix());
+ //assertEquals(10, step.getStepVal());
+
+ // make sure large values dont break anything:
+ minVal = 1000000000;
+ maxVal = 2000000000;
+ step = XYStepCalculator.getStep(XYStepMode.SUBDIVIDE, plotSize, numSegments, minVal, maxVal);
+ assertEquals(plotSize/(numSegments-1), step.getStepPix());
+
+ }
+
+ @Test
+ public void testIncrementByVal() throws Exception {
+
+ }
+
+ @Test
+ public void testIncrementByPixels() throws Exception {
+
+ }
+}
diff --git a/DynamicXYPlotExample.iml b/DynamicXYPlotExample.iml
new file mode 100644
index 0000000..cde54cf
--- /dev/null
+++ b/DynamicXYPlotExample.iml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
+ <component name="FacetManager">
+ <facet type="android" name="Android">
+ <configuration>
+ <option name="GEN_FOLDER_RELATIVE_PATH_APT" value="/target/generated-sources/r" />
+ <option name="GEN_FOLDER_RELATIVE_PATH_AIDL" value="/target/generated-sources/aidl" />
+ <option name="MANIFEST_FILE_RELATIVE_PATH" value="/Examples/DynamicXYPlotExample/AndroidManifest.xml" />
+ <option name="RES_FOLDER_RELATIVE_PATH" value="/Examples/DynamicXYPlotExample/res" />
+ <option name="ASSETS_FOLDER_RELATIVE_PATH" value="/Examples/DynamicXYPlotExample/assets" />
+ <option name="LIBS_FOLDER_RELATIVE_PATH" value="/Examples/DynamicXYPlotExample/libs" />
+ <option name="REGENERATE_R_JAVA" value="true" />
+ <option name="REGENERATE_JAVA_BY_AIDL" value="true" />
+ <option name="USE_CUSTOM_APK_RESOURCE_FOLDER" value="false" />
+ <option name="CUSTOM_APK_RESOURCE_FOLDER" value="" />
+ <option name="USE_CUSTOM_COMPILER_MANIFEST" value="false" />
+ <option name="CUSTOM_COMPILER_MANIFEST" value="" />
+ <option name="APK_PATH" value="/target/AndroidPlot.apk" />
+ <option name="LIBRARY_PROJECT" value="false" />
+ <option name="RUN_PROCESS_RESOURCES_MAVEN_TASK" value="true" />
+ <option name="GENERATE_UNSIGNED_APK" value="false" />
+ </configuration>
+ </facet>
+ </component>
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
+ <output url="file://$MODULE_DIR$/Examples/DynamicXYPlotExample" />
+ <output-test url="file://$MODULE_DIR$/target/test-classes" />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
+ <excludeFolder url="file://$MODULE_DIR$/target" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ </component>
+</module>
+
diff --git a/Examples/AndroidPlot-Examples.iml b/Examples/AndroidPlot-Examples.iml
new file mode 100644
index 0000000..0cfe50e
--- /dev/null
+++ b/Examples/AndroidPlot-Examples.iml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+ <component name="NewModuleRootManager" inherit-compiler-output="false">
+ <output url="file://$MODULE_DIR$/target/classes" />
+ <output-test url="file://$MODULE_DIR$/target/test-classes" />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$/gen" isTestSource="false" />
+ <excludeFolder url="file://$MODULE_DIR$/target" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="library" scope="PROVIDED" name="Maven: commons-logging:commons-logging:1.1.1" level="project" />
+ <orderEntry type="library" scope="PROVIDED" name="Maven: org.apache.httpcomponents:httpclient:4.0.1" level="project" />
+ <orderEntry type="library" scope="PROVIDED" name="Maven: org.apache.httpcomponents:httpcore:4.0.1" level="project" />
+ <orderEntry type="library" scope="PROVIDED" name="Maven: commons-codec:commons-codec:1.3" level="project" />
+ <orderEntry type="library" scope="PROVIDED" name="Maven: xerces:xmlParserAPIs:2.6.2" level="project" />
+ <orderEntry type="library" scope="PROVIDED" name="Maven: xpp3:xpp3:1.1.4c" level="project" />
+ </component>
+</module>
+
diff --git a/Examples/DemoApp/.classpath b/Examples/DemoApp/.classpath
new file mode 100644
index 0000000..c64ee78
--- /dev/null
+++ b/Examples/DemoApp/.classpath
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
+ <classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
+ <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
+ <classpathentry kind="src" output="bin/classes" path="src"/>
+ <classpathentry kind="src" output="bin/classes" path="gen"/>
+ <classpathentry combineaccessrules="false" exported="true" kind="src" path="/AndroidPlot-Core"/>
+ <classpathentry kind="output" path="bin/classes"/>
+</classpath>
diff --git a/Examples/DemoApp/.project b/Examples/DemoApp/.project
new file mode 100644
index 0000000..185fcd4
--- /dev/null
+++ b/Examples/DemoApp/.project
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>AndroidPlotDemo</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>com.android.ide.eclipse.adt.ApkBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.m2e.core.maven2Builder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>com.android.ide.eclipse.adt.AndroidNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>org.eclipse.m2e.core.maven2Nature</nature>
+ </natures>
+</projectDescription>
diff --git a/Examples/DemoApp/AndroidManifest.xml b/Examples/DemoApp/AndroidManifest.xml
new file mode 100644
index 0000000..765336b
--- /dev/null
+++ b/Examples/DemoApp/AndroidManifest.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.androidplot.demos"
+ android:versionCode="8"
+ android:versionName="0.6.0.1">
+ <uses-sdk android:minSdkVersion="6" android:targetSdkVersion="16"/>
+
+ <!-- We must disable hardware acceleration otherwise some lines will not appear in our plots. -->
+ <application android:label="AndroidPlot API DemoApp"
+ android:icon="@drawable/icon">
+ <activity android:name=".MainActivity"
+ android:label="@string/app_name">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:name=".SimplePieChartActivity"
+ android:label="@string/app_name">
+ <intent-filter>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ <activity android:name=".SimpleXYPlotActivity"
+ android:label="@string/app_name">
+ <intent-filter>
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:name=".BarPlotExampleActivity"
+ android:label="@string/app_name">
+ <intent-filter>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ <activity android:name=".DynamicXYPlotActivity"
+ android:label="@string/app_name">
+ <intent-filter>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ <activity android:name=".OrientationSensorExampleActivity"
+ android:label="@string/app_name"
+ android:screenOrientation="landscape">
+ <intent-filter>
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:name=".StepChartExampleActivity"
+ android:label="@string/app_name">
+ <intent-filter>
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:name=".TouchZoomExampleActivity"
+ android:label="@string/app_name">
+ <intent-filter>
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:name=".ListViewActivity" android:label="ListView Example"/>
+ <activity android:name=".XYRegionExampleActivity" android:label="XYRegion Example">
+ <intent-filter>
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:name=".TimeSeriesActivity"/>
+ <activity android:name=".DualScaleXYPlotExampleActivity"/>
+ <activity android:name=".XYPlotWithBgImgActivity"/>
+
+ <!-- receiver for demo app widget -->
+ <receiver
+ android:icon="@drawable/icon"
+ android:label="Example Widget"
+ android:name="com.androidplot.demos.widget.DemoAppWidgetProvider" >
+ <intent-filter >
+ <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+ </intent-filter>
+
+ <meta-data
+ android:name="android.appwidget.provider"
+ android:resource="@xml/demo_app_widget_provider_info" />
+ </receiver>
+ </application>
+</manifest>
diff --git a/Examples/DemoApp/DemoApp.iml b/Examples/DemoApp/DemoApp.iml
new file mode 100644
index 0000000..c4b2db2
--- /dev/null
+++ b/Examples/DemoApp/DemoApp.iml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
+ <component name="FacetManager">
+ <facet type="android" name="Android">
+ <configuration>
+ <option name="RUN_PROCESS_RESOURCES_MAVEN_TASK" value="false" />
+ <includeSystemProguardFile>false</includeSystemProguardFile>
+ <includeAssetsFromLibraries>true</includeAssetsFromLibraries>
+ <resOverlayFolders />
+ </configuration>
+ </facet>
+ </component>
+ <component name="NewModuleRootManager" inherit-compiler-output="false">
+ <output url="file://$MODULE_DIR$/target/classes" />
+ <output-test url="file://$MODULE_DIR$/target/test-classes" />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$/gen" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/target/generated-sources/r" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+ <excludeFolder url="file://$MODULE_DIR$/target/classes" />
+ <excludeFolder url="file://$MODULE_DIR$/target/generated-sources/combined-assets" />
+ <excludeFolder url="file://$MODULE_DIR$/target/generated-sources/combined-resources" />
+ <excludeFolder url="file://$MODULE_DIR$/target/generated-sources/extracted-dependencies" />
+ </content>
+ <orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="library" scope="PROVIDED" name="Maven: com.google.android:android:4.1.1.4" level="project" />
+ <orderEntry type="library" scope="PROVIDED" name="Maven: commons-logging:commons-logging:1.1.1" level="project" />
+ <orderEntry type="library" scope="PROVIDED" name="Maven: org.apache.httpcomponents:httpclient:4.0.1" level="project" />
+ <orderEntry type="library" scope="PROVIDED" name="Maven: org.apache.httpcomponents:httpcore:4.0.1" level="project" />
+ <orderEntry type="library" scope="PROVIDED" name="Maven: commons-codec:commons-codec:1.3" level="project" />
+ <orderEntry type="library" scope="PROVIDED" name="Maven: org.khronos:opengl-api:gl1.1-android-2.1_r1" level="project" />
+ <orderEntry type="library" scope="PROVIDED" name="Maven: xerces:xmlParserAPIs:2.6.2" level="project" />
+ <orderEntry type="library" scope="PROVIDED" name="Maven: xpp3:xpp3:1.1.4c" level="project" />
+ <orderEntry type="library" scope="PROVIDED" name="Maven: org.json:json:20080701" level="project" />
+ <orderEntry type="module" module-name="androidplot-core" />
+ </component>
+</module>
+
diff --git a/Examples/DemoApp/ant.properties b/Examples/DemoApp/ant.properties
new file mode 100644
index 0000000..87c820a
--- /dev/null
+++ b/Examples/DemoApp/ant.properties
@@ -0,0 +1,46 @@
+#
+# Copyright (c) 2012 AndroidPlot.com. All rights reserved.
+#
+# Redistribution and use of source without modification and derived binaries with or without modification,
+# are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice, this list of
+# conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice, this list
+# of conditions and the following disclaimer in the documentation and/or other materials
+# provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY ANDROIDPLOT.COM ``AS IS'' AND ANY EXPRESS OR IMPLIED
+# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANDROIDPLOT.COM OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# The views and conclusions contained in the software and documentation are those of the
+# authors and should not be interpreted as representing official policies, either expressed
+# or implied, of AndroidPlot.com.
+#
+
+# This file is used to override default values used by the Ant build system.
+#
+# This file must be checked into Version Control Systems, as it is
+# integral to the build system of your project.
+
+# This file is only used by the Ant script.
+
+# You can use this to override default values such as
+# 'source.dir' for the location of your java source folder and
+# 'out.dir' for the location of your output folder.
+
+# You can also use it define how the release builds are signed by declaring
+# the following properties:
+# 'key.store' for the location of your keystore and
+# 'key.alias' for the name of the key to use.
+# The password will be asked during the build when you use the 'release' target.
+jar.libs.dir=..\\..\\AndroidPlot-Core\\target\\obfuscated
+
diff --git a/Examples/DemoApp/build.xml b/Examples/DemoApp/build.xml
new file mode 100644
index 0000000..45987d1
--- /dev/null
+++ b/Examples/DemoApp/build.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="testapp" default="help">
+
+ <!-- The local.properties file is created and updated by the 'android' tool.
+ It contains the path to the SDK. It should *NOT* be checked into
+ Version Control Systems. -->
+ <property file="local.properties" />
+
+ <!-- The ant.properties file can be created by you. It is only edited by the
+ 'android' tool to add properties to it.
+ This is the place to change some Ant specific build properties.
+ Here are some properties you may want to change/update:
+
+ source.dir
+ The name of the source directory. Default is 'src'.
+ out.dir
+ The name of the output directory. Default is 'bin'.
+
+ For other overridable properties, look at the beginning of the rules
+ files in the SDK, at tools/ant/build.xml
+
+ Properties related to the SDK location or the project target should
+ be updated using the 'android' tool with the 'update' action.
+
+ This file is an integral part of the build system for your
+ application and should be checked into Version Control Systems.
+
+ -->
+ <property file="ant.properties" />
+
+ <!-- if sdk.dir was not set from one of the property file, then
+ get it from the ANDROID_HOME env var.
+ This must be done before we load project.properties since
+ the proguard config can use sdk.dir -->
+ <property environment="env" />
+ <condition property="sdk.dir" value="${env.ANDROID_HOME}">
+ <isset property="env.ANDROID_HOME" />
+ </condition>
+
+ <!-- The project.properties file is created and updated by the 'android'
+ tool, as well as ADT.
+
+ This contains project specific properties such as project target, and library
+ dependencies. Lower level build properties are stored in ant.properties
+ (or in .classpath for Eclipse projects).
+
+ This file is an integral part of the build system for your
+ application and should be checked into Version Control Systems. -->
+ <loadproperties srcFile="project.properties" />
+
+ <!-- quick check on sdk.dir -->
+ <fail
+ message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
+ unless="sdk.dir"
+ />
+
+ <!--
+ Import per project custom build rules if present at the root of the project.
+ This is the place to put custom intermediary targets such as:
+ -pre-build
+ -pre-compile
+ -post-compile (This is typically used for code obfuscation.
+ Compiled code location: ${out.classes.absolute.dir}
+ If this is not done in place, override ${out.dex.input.absolute.dir})
+ -post-package
+ -post-build
+ -pre-clean
+ -->
+ <import file="custom_rules.xml" optional="true" />
+
+ <!-- Import the actual build file.
+
+ To customize existing targets, there are two options:
+ - Customize only one target:
+ - copy/paste the target into this file, *before* the
+ <import> task.
+ - customize it to your needs.
+ - Customize the whole content of build.xml
+ - copy/paste the content of the rules files (minus the top node)
+ into this file, replacing the <import> task.
+ - customize to your needs.
+
+ ***********************
+ ****** IMPORTANT ******
+ ***********************
+ In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
+ in order to avoid having your file be overridden by tools such as "android update project"
+ -->
+ <!-- version-tag: 1 -->
+ <import file="${sdk.dir}/tools/ant/build.xml" />
+
+</project>
diff --git a/Examples/DemoApp/pom.xml b/Examples/DemoApp/pom.xml
new file mode 100644
index 0000000..3ce1b3f
--- /dev/null
+++ b/Examples/DemoApp/pom.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>com.androidplot</groupId>
+ <artifactId>androidplot</artifactId>
+ <version>0.6.0</version>
+ <relativePath>../../pom.xml</relativePath>
+ </parent>
+
+ <groupId>com.androidplot</groupId>
+ <artifactId>androidplot-demoapp</artifactId>
+ <packaging>apk</packaging>
+ <name>AndroidPlot-DemoApp</name>
+ <!--<version>1.0-SNAPSHOT</version>-->
+
+ <dependencies>
+ <!--<dependency>
+ <groupId>com.google.android</groupId>
+ <artifactId>android</artifactId>
+ <version>4.1.1.4</version>
+ <scope>provided</scope>
+ </dependency>-->
+ <dependency>
+ <groupId>com.google.android</groupId>
+ <artifactId>android</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.androidplot</groupId>
+ <artifactId>androidplot-core</artifactId>
+ <version>0.6.0</version>
+ <scope>compile</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <finalName>${project.artifactId}</finalName>
+ <sourceDirectory>src</sourceDirectory>
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>com.jayway.maven.plugins.android.generation2</groupId>
+ <artifactId>android-maven-plugin</artifactId>
+ <version>3.6.0</version>
+ <extensions>true</extensions>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>com.jayway.maven.plugins.android.generation2</groupId>
+ <artifactId>android-maven-plugin</artifactId>
+ <configuration>
+ <sdk>
+ <!-- platform or api level (api level 4 = platform 1.6)-->
+ <platform>16</platform>
+ </sdk>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
diff --git a/Examples/DemoApp/proguard-project.txt b/Examples/DemoApp/proguard-project.txt
new file mode 100644
index 0000000..2c0da18
--- /dev/null
+++ b/Examples/DemoApp/proguard-project.txt
@@ -0,0 +1,26 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# remove all debug and verbose level logging statements:
+-assumenosideeffects class android.util.Log {
+ public static *** d(...);
+ public static *** v(...);
+}
diff --git a/Examples/DemoApp/project.properties b/Examples/DemoApp/project.properties
new file mode 100644
index 0000000..29656f2
--- /dev/null
+++ b/Examples/DemoApp/project.properties
@@ -0,0 +1,43 @@
+#
+# Copyright (c) 2012 AndroidPlot.com. All rights reserved.
+#
+# Redistribution and use of source without modification and derived binaries with or without modification,
+# are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice, this list of
+# conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice, this list
+# of conditions and the following disclaimer in the documentation and/or other materials
+# provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY ANDROIDPLOT.COM ``AS IS'' AND ANY EXPRESS OR IMPLIED
+# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANDROIDPLOT.COM OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# The views and conclusions contained in the software and documentation are those of the
+# authors and should not be interpreted as representing official policies, either expressed
+# or implied, of AndroidPlot.com.
+#
+
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}\tools\proguard\proguard-android.txt:proguard-project.txt
+# using config with optimization enabled so that things like log statement removal will work:
+proguard.config=${sdk.dir}/tools/proguard/proguard-android-optimize.txt:proguard-project.txt
+# Project target.
+target=android-16
diff --git a/Examples/DemoApp/res/drawable/graph_background.png b/Examples/DemoApp/res/drawable/graph_background.png
new file mode 100755
index 0000000..47523c2
--- /dev/null
+++ b/Examples/DemoApp/res/drawable/graph_background.png
Binary files differ
diff --git a/Examples/DemoApp/res/drawable/icon.png b/Examples/DemoApp/res/drawable/icon.png
new file mode 100644
index 0000000..6557d3a
--- /dev/null
+++ b/Examples/DemoApp/res/drawable/icon.png
Binary files differ
diff --git a/Examples/DemoApp/res/layout/bar_plot_example.xml b/Examples/DemoApp/res/layout/bar_plot_example.xml
new file mode 100644
index 0000000..76ac666
--- /dev/null
+++ b/Examples/DemoApp/res/layout/bar_plot_example.xml
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright 2012 AndroidPlot.com
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@style/sample_activity"
+ android:orientation="vertical">
+
+ <com.androidplot.xy.XYPlot
+ android:id="@+id/mySimpleXYPlot"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ androidPlot.title="Growth"
+ androidPlot.domainLabel="Month"
+ androidPlot.rangeLabel="Revenue (millions)"
+ androidPlot.titleWidget.labelPaint.textSize="@dimen/title_font_size"
+ androidPlot.domainLabelWidget.labelPaint.textSize="@dimen/domain_label_font_size"
+ androidPlot.rangeLabelWidget.labelPaint.textSize="@dimen/range_label_font_size"
+ androidPlot.graphWidget.marginTop="20dp"
+ androidPlot.graphWidget.marginLeft="15dp"
+ androidPlot.graphWidget.marginBottom="25dp"
+ androidPlot.graphWidget.marginRight="10dp"
+ androidPlot.graphWidget.gridBackgroundPaint.color="#000000"
+ androidPlot.graphWidget.domainGridLinePaint.alpha="0"
+ androidPlot.graphWidget.domainOriginLinePaint.alpha="0"
+ androidPlot.graphWidget.rangeLabelPaint.textSize="@dimen/range_tick_label_font_size"
+ androidPlot.graphWidget.rangeOriginLabelPaint.textSize="@dimen/range_tick_label_font_size"
+ androidPlot.graphWidget.domainLabelPaint.textSize="@dimen/domain_tick_label_font_size"
+ androidPlot.graphWidget.domainOriginLabelPaint.textSize="@dimen/domain_tick_label_font_size"
+ androidPlot.legendWidget.textPaint.textSize="@dimen/legend_text_font_size"
+ androidPlot.legendWidget.iconSizeMetrics.heightMetric.value="15dp"
+ androidPlot.legendWidget.iconSizeMetrics.widthMetric.value="15dp"
+ androidPlot.legendWidget.heightMetric.value="25dp"
+ androidPlot.legendWidget.positionMetrics.anchor="right_bottom"
+ android:layout_weight="1"
+ androidPlot.graphWidget.gridLinePaint.color="#000000"/>
+
+ <LinearLayout
+ android:layout_height="wrap_content"
+ android:layout_width="fill_parent"
+ android:layout_gravity="center"
+ android:orientation="horizontal">
+ <Spinner
+ android:id="@+id/spRenderStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+ <Spinner
+ android:id="@+id/spSeriesSize"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_height="wrap_content"
+ android:layout_width="fill_parent"
+ android:layout_gravity="center"
+ android:orientation="horizontal">
+ <Spinner
+ android:id="@+id/spWidthStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+ <RelativeLayout
+ android:id="@+id/sectionGraph"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+ <SeekBar
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="10dp"
+ android:paddingRight="10dp"
+ android:max="300"
+ android:progress="10"
+ android:id="@+id/sbFixed"/>
+ <SeekBar
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="10dp"
+ android:paddingRight="10dp"
+ android:max="50"
+ android:progress="1"
+ android:id="@+id/sbVariable"/>
+ </RelativeLayout>
+ </LinearLayout>
+ <LinearLayout
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center"
+ android:orientation="horizontal">
+ <CheckBox android:id="@+id/s1CheckBox"
+ android:text="Series 1"
+ android:checked="true"
+ android:layout_height="wrap_content"
+ android:layout_width="fill_parent"/>
+ <CheckBox android:id="@+id/s2CheckBox"
+ android:text="Series 2"
+ android:checked="true"
+ android:layout_height="wrap_content"
+ android:layout_width="fill_parent"/>
+ </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/Examples/DemoApp/res/layout/demo_app_widget.xml b/Examples/DemoApp/res/layout/demo_app_widget.xml
new file mode 100644
index 0000000..817e0a4
--- /dev/null
+++ b/Examples/DemoApp/res/layout/demo_app_widget.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright 2012 AndroidPlot.com
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@style/sample_activity"
+ android:orientation="vertical">
+
+ <ImageView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/imgView"
+ android:background="@android:drawable/alert_dark_frame"
+ android:scaleType="centerInside"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ </ImageView>
+</LinearLayout>
\ No newline at end of file
diff --git a/Examples/DemoApp/res/layout/dual_scale_xy_plot_example.xml b/Examples/DemoApp/res/layout/dual_scale_xy_plot_example.xml
new file mode 100644
index 0000000..f21eac2
--- /dev/null
+++ b/Examples/DemoApp/res/layout/dual_scale_xy_plot_example.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright 2012 AndroidPlot.com
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@style/sample_activity"
+ android:orientation="vertical">
+
+ <RelativeLayout
+ android:id="@+id/sectionGraph"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1"
+ android:layout_marginTop="0px"
+ android:layout_marginBottom="0px"
+ android:layout_marginLeft="0px"
+ android:layout_marginRight="0px">
+
+ <com.androidplot.xy.XYPlot
+ android:id="@+id/mySimpleXYPlot_L"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ androidPlot.title="A Simple XY Plot"
+ androidPlot.graphWidget.gridLinePaint.color="#000000"/>
+ <com.androidplot.xy.XYPlot
+ android:id="@+id/mySimpleXYPlot_R"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ androidPlot.title="A Simple XY Plot"
+ androidPlot.graphWidget.gridLinePaint.color="#000000"/>
+
+ </RelativeLayout>
+
+ <Button android:id="@+id/toggleSeries2"
+ style="@style/toc_button"
+ android:text="Toggle position of Series2"
+ android:enabled="true"/>
+
+</LinearLayout>
diff --git a/Examples/DemoApp/res/layout/dynamicxyplot_example.xml b/Examples/DemoApp/res/layout/dynamicxyplot_example.xml
new file mode 100644
index 0000000..0276fce
--- /dev/null
+++ b/Examples/DemoApp/res/layout/dynamicxyplot_example.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright 2012 AndroidPlot.com
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@style/sample_activity">
+
+ <com.androidplot.xy.XYPlot
+ android:id="@+id/dynamicXYPlot"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ androidplot.renderMode="use_background_thread"
+ androidPlot.title="A Dynamic XY Plot"
+ androidPlot.domainLabel="Domain"
+ androidPlot.rangeLabel="Range"
+ androidPlot.titleWidget.labelPaint.textSize="@dimen/title_font_size"
+ androidPlot.domainLabelWidget.labelPaint.textSize="@dimen/domain_label_font_size"
+ androidPlot.rangeLabelWidget.labelPaint.textSize="@dimen/range_label_font_size"
+ androidPlot.graphWidget.marginTop="20dp"
+ androidPlot.graphWidget.marginLeft="15dp"
+ androidPlot.graphWidget.marginBottom="25dp"
+ androidPlot.graphWidget.marginRight="10dp"
+ androidPlot.graphWidget.rangeLabelPaint.textSize="@dimen/range_tick_label_font_size"
+ androidPlot.graphWidget.rangeOriginLabelPaint.textSize="@dimen/range_tick_label_font_size"
+ androidPlot.graphWidget.domainLabelPaint.textSize="@dimen/domain_tick_label_font_size"
+ androidPlot.graphWidget.domainOriginLabelPaint.textSize="@dimen/domain_tick_label_font_size"
+ androidPlot.legendWidget.textPaint.textSize="@dimen/legend_text_font_size"
+ androidPlot.legendWidget.iconSizeMetrics.heightMetric.value="15dp"
+ androidPlot.legendWidget.iconSizeMetrics.widthMetric.value="15dp"
+ androidPlot.legendWidget.heightMetric.value="25dp"
+ androidPlot.legendWidget.positionMetrics.anchor="right_bottom"/>
+
+</LinearLayout>
diff --git a/Examples/DemoApp/res/layout/listview_example.xml b/Examples/DemoApp/res/layout/listview_example.xml
new file mode 100644
index 0000000..5ba50bc
--- /dev/null
+++ b/Examples/DemoApp/res/layout/listview_example.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout android:id="@+id/LinearLayout01"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <ListView android:id="@+id/listView1"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/Examples/DemoApp/res/layout/listview_example_item.xml b/Examples/DemoApp/res/layout/listview_example_item.xml
new file mode 100644
index 0000000..d1aa023
--- /dev/null
+++ b/Examples/DemoApp/res/layout/listview_example_item.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright 2012 AndroidPlot.com
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ <com.androidplot.xy.XYPlot
+ android:id="@+id/xyplot"
+ android:layout_width="fill_parent"
+ android:layout_height="250dp"
+ title="an xy plot"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/Examples/DemoApp/res/layout/main.xml b/Examples/DemoApp/res/layout/main.xml
new file mode 100644
index 0000000..779b2f3
--- /dev/null
+++ b/Examples/DemoApp/res/layout/main.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <ScrollView android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1">
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+ <Button android:id="@+id/startSimplePieExButton"
+ style="@style/toc_button"
+ android:text="A Pie Chart"/>
+ <Button android:id="@+id/startSimpleXYExButton"
+ style="@style/toc_button"
+ android:text="A Simple XY Plot"/>
+ <Button android:id="@+id/startDynamicXYExButton"
+ style="@style/toc_button"
+ android:text="A Dynamic XY Plot"/>
+ <Button android:id="@+id/startOrSensorExButton"
+ style="@style/toc_button"
+ android:text="Realtime Orientation Sensor Plot"/>
+ <Button android:id="@+id/startTimeSeriesExButton"
+ style="@style/toc_button"
+ android:text="@string/ts_title"/>
+ <Button android:id="@+id/startStepChartExButton"
+ style="@style/toc_button"
+ android:text="Step Chart"
+ />
+ <Button android:id="@+id/startScrollZoomButton"
+ style="@style/toc_button"
+ android:text="Scroll and Zoom"
+ />
+ <Button android:id="@+id/startBarPlotExButton"
+ style="@style/toc_button"
+ android:text="Bar Plot"/>
+ <Button android:id="@+id/startXyScatterExButton"
+ style="@style/toc_button"
+ android:text="XY Scatter"
+ android:enabled="false"/>
+ <Button android:id="@+id/startXyScatterLineExButton"
+ style="@style/toc_button"
+ android:text="XY Scatter With Lines"
+ android:enabled="false"/>
+ <Button android:id="@+id/startXyRegionExampleButton"
+ style="@style/toc_button"
+ android:text="XYRegion Example"
+ android:enabled="true"/>
+ <Button android:id="@+id/startXyListViewExButton"
+ style="@style/toc_button"
+ android:text="ListView of XYPlots"
+ android:enabled="true"/>
+ <Button android:id="@+id/startDualScaleExampleButton"
+ style="@style/toc_button"
+ android:text="Dual Scale Example"
+ android:enabled="true"/>
+ <Button android:id="@+id/startXYPlotWithBgImgExample"
+ style="@style/toc_button"
+ android:text="XY Background Example"
+ android:enabled="true"/>
+ </LinearLayout>
+ </ScrollView>
+ <TextView android:id="@+id/text"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:autoLink="web"
+ android:gravity="center"
+ android:text="http://androidplot.com"/>
+</LinearLayout>
+
diff --git a/Examples/DemoApp/res/layout/orientation_sensor_example.xml b/Examples/DemoApp/res/layout/orientation_sensor_example.xml
new file mode 100644
index 0000000..85fa45f
--- /dev/null
+++ b/Examples/DemoApp/res/layout/orientation_sensor_example.xml
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <LinearLayout android:orientation="horizontal"
+ android:gravity="center"
+ android:layout_weight="1"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+
+ <com.androidplot.xy.XYPlot
+ android:id="@+id/aprLevelsPlot"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_margin="0dp"
+ android:layout_weight="3"
+ android:layout_marginTop="10px"
+ android:layout_marginLeft="10px"
+ android:layout_marginRight="10px"
+ androidPlot.backgroundPaint.color="#000000"
+ androidPlot.borderPaint.color="#000000"
+ androidplot.renderMode="use_background_thread"
+ androidPlot.title="Levels"
+ androidPlot.titleWidget.labelPaint.textSize="@dimen/title_font_size"
+ androidPlot.domainLabelWidget.labelPaint.textSize="@dimen/domain_label_font_size"
+ androidPlot.rangeLabelWidget.labelPaint.textSize="@dimen/range_label_font_size"
+ androidPlot.graphWidget.backgroundPaint.color="#000000"
+ androidPlot.graphWidget.gridBackgroundPaint.color="#000000"
+ androidPlot.graphWidget.domainGridLinePaint.color="#00000000"
+ androidPlot.graphWidget.domainOriginLinePaint.color="#00000000"
+ androidPlot.graphWidget.domainOriginLabelPaint.color="#00000000"
+ androidPlot.graphWidget.marginTop="20dp"
+ androidPlot.graphWidget.marginLeft="15dp"
+ androidPlot.graphWidget.marginBottom="25dp"
+ androidPlot.graphWidget.marginRight="10dp"
+ androidPlot.graphWidget.rangeLabelPaint.textSize="@dimen/range_tick_label_font_size"
+ androidPlot.graphWidget.rangeOriginLabelPaint.textSize="@dimen/range_tick_label_font_size"
+ androidPlot.graphWidget.domainLabelPaint.textSize="@dimen/domain_tick_label_font_size"
+ androidPlot.graphWidget.domainOriginLabelPaint.textSize="@dimen/domain_tick_label_font_size"
+ androidPlot.legendWidget.textPaint.textSize="10dp"
+ androidPlot.legendWidget.iconSizeMetrics.heightMetric.value="15dp"
+ androidPlot.legendWidget.iconSizeMetrics.widthMetric.value="15dp"
+ androidPlot.legendWidget.heightMetric.value="25dp"
+ />
+ <com.androidplot.xy.XYPlot
+ android:id="@+id/aprHistoryPlot"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_margin="0dp"
+ android:layout_weight="1"
+ android:layout_marginTop="10px"
+ android:layout_marginLeft="10px"
+ android:layout_marginRight="10px"
+ androidPlot.backgroundPaint.color="#000000"
+ androidPlot.borderPaint.color="#000000"
+ androidplot.renderMode="use_background_thread"
+ androidPlot.title="History"
+ androidPlot.domainLabel="Domain"
+ androidPlot.rangeLabel="Range"
+ androidPlot.titleWidget.labelPaint.textSize="@dimen/title_font_size"
+ androidPlot.domainLabelWidget.labelPaint.textSize="@dimen/domain_label_font_size"
+ androidPlot.rangeLabelWidget.labelPaint.textSize="@dimen/range_label_font_size"
+ androidPlot.graphWidget.backgroundPaint.color="#000000"
+ androidPlot.graphWidget.gridBackgroundPaint.color="#000000"
+ androidPlot.graphWidget.marginTop="20dp"
+ androidPlot.graphWidget.marginLeft="15dp"
+ androidPlot.graphWidget.marginBottom="25dp"
+ androidPlot.graphWidget.marginRight="10dp"
+ androidPlot.graphWidget.rangeLabelPaint.textSize="@dimen/range_tick_label_font_size"
+ androidPlot.graphWidget.rangeOriginLabelPaint.textSize="@dimen/range_tick_label_font_size"
+ androidPlot.graphWidget.domainLabelPaint.textSize="@dimen/domain_tick_label_font_size"
+ androidPlot.graphWidget.domainOriginLabelPaint.textSize="@dimen/domain_tick_label_font_size"
+ androidPlot.legendWidget.textPaint.textSize="@dimen/legend_text_font_size"
+ androidPlot.legendWidget.iconSizeMetrics.heightMetric.value="15dp"
+ androidPlot.legendWidget.iconSizeMetrics.widthMetric.value="15dp"
+ androidPlot.legendWidget.heightMetric.value="25dp"
+ />
+
+ </LinearLayout>
+
+ <LinearLayout android:orientation="horizontal"
+ android:gravity="center"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+ <CheckBox android:id="@+id/hwAccelerationCb"
+ android:visibility="gone"
+ android:text="HW Acceleration"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ <CheckBox android:id="@+id/showFpsCb"
+ android:text="Show FPS"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+ </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/Examples/DemoApp/res/layout/pie_chart.xml b/Examples/DemoApp/res/layout/pie_chart.xml
new file mode 100644
index 0000000..010bcfc
--- /dev/null
+++ b/Examples/DemoApp/res/layout/pie_chart.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright 2012 AndroidPlot.com
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@style/sample_activity"
+ android:orientation="vertical">
+
+ <com.androidplot.pie.PieChart
+ android:id="@+id/mySimplePieChart"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1"
+ androidPlot.title="A Simple Pie Chart"
+ androidPlot.titleWidget.labelPaint.textSize="@dimen/title_font_size"/>
+ <LinearLayout
+ android:layout_height="wrap_content"
+ android:layout_width="fill_parent"
+ android:layout_gravity="center"
+ android:orientation="vertical">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_gravity="center"
+ android:text="Donut Size"
+ android:id="@+id/donutSizeSeekLabel"/>
+ <SeekBar
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:paddingLeft="10dp"
+ android:paddingRight="10dp"
+ android:max="90"
+ android:progress="50"
+ android:id="@+id/donutSizeSeekBar"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_gravity="center"
+ android:text="unknown"
+ android:id="@+id/donutSizeTextView"/>
+ </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/Examples/DemoApp/res/layout/simple_xy_plot_example.xml b/Examples/DemoApp/res/layout/simple_xy_plot_example.xml
new file mode 100644
index 0000000..fe54c57
--- /dev/null
+++ b/Examples/DemoApp/res/layout/simple_xy_plot_example.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright 2012 AndroidPlot.com
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@style/sample_activity">
+
+ <com.androidplot.xy.XYPlot
+ android:id="@+id/mySimpleXYPlot"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ androidPlot.title="A Simple XY Plot"
+ androidPlot.domainLabel="Domain"
+ androidPlot.rangeLabel="Range"
+ androidPlot.titleWidget.labelPaint.textSize="@dimen/title_font_size"
+ androidPlot.domainLabelWidget.labelPaint.textSize="@dimen/domain_label_font_size"
+ androidPlot.rangeLabelWidget.labelPaint.textSize="@dimen/range_label_font_size"
+ androidPlot.graphWidget.marginTop="20dp"
+ androidPlot.graphWidget.marginLeft="15dp"
+ androidPlot.graphWidget.marginBottom="25dp"
+ androidPlot.graphWidget.marginRight="10dp"
+ androidPlot.graphWidget.rangeLabelPaint.textSize="@dimen/range_tick_label_font_size"
+ androidPlot.graphWidget.rangeOriginLabelPaint.textSize="@dimen/range_tick_label_font_size"
+ androidPlot.graphWidget.domainLabelPaint.textSize="@dimen/domain_tick_label_font_size"
+ androidPlot.graphWidget.domainOriginLabelPaint.textSize="@dimen/domain_tick_label_font_size"
+ androidPlot.legendWidget.textPaint.textSize="@dimen/legend_text_font_size"
+ androidPlot.legendWidget.iconSizeMetrics.heightMetric.value="15dp"
+ androidPlot.legendWidget.iconSizeMetrics.widthMetric.value="15dp"
+ androidPlot.legendWidget.heightMetric.value="25dp"
+ androidPlot.legendWidget.positionMetrics.anchor="right_bottom"
+ androidPlot.graphWidget.gridLinePaint.color="#000000"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/Examples/DemoApp/res/layout/step_chart_example.xml b/Examples/DemoApp/res/layout/step_chart_example.xml
new file mode 100644
index 0000000..fadc79b
--- /dev/null
+++ b/Examples/DemoApp/res/layout/step_chart_example.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ style="@style/sample_activity"
+ >
+
+ <com.androidplot.xy.XYPlot
+ android:id="@+id/stepChartExamplePlot"
+ style="@style/plotStyle1"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ androidplot.title="HTTP Server State (15 Sec)"
+ androidplot.domainLabel="time (secs)"
+ androidplot.rangeLabel="server state"
+ androidplot.markupEnabled="false"
+ renderMode="use_main_thread"
+ />
+</LinearLayout>
+
diff --git a/Examples/DemoApp/res/layout/time_series_example.xml b/Examples/DemoApp/res/layout/time_series_example.xml
new file mode 100644
index 0000000..847b381
--- /dev/null
+++ b/Examples/DemoApp/res/layout/time_series_example.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright 2012 AndroidPlot.com
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@style/sample_activity"
+ android:orientation="vertical">
+
+ <com.androidplot.xy.XYPlot
+ android:id="@+id/plot1"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ title="@string/ts_plot1_title"
+ renderMode="use_main_thread"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/Examples/DemoApp/res/layout/touch_zoom_example.xml b/Examples/DemoApp/res/layout/touch_zoom_example.xml
new file mode 100644
index 0000000..71d0e33
--- /dev/null
+++ b/Examples/DemoApp/res/layout/touch_zoom_example.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright 2012 AndroidPlot.com
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@style/sample_activity"
+ android:orientation="vertical">
+
+ <com.androidplot.xy.XYPlot
+ android:id="@+id/mySimpleXYPlot"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ androidPlot.title="ScrollZoom Example"
+ androidPlot.titleWidget.labelPaint.textSize="@dimen/title_font_size"
+ androidPlot.domainLabelWidget.labelPaint.textSize="@dimen/domain_label_font_size"
+ androidPlot.rangeLabelWidget.labelPaint.textSize="@dimen/range_label_font_size"
+ androidPlot.graphWidget.marginTop="20dp"
+ androidPlot.graphWidget.marginLeft="15dp"
+ androidPlot.graphWidget.marginBottom="25dp"
+ androidPlot.graphWidget.marginRight="10dp"
+ androidPlot.graphWidget.rangeLabelPaint.textSize="@dimen/range_tick_label_font_size"
+ androidPlot.graphWidget.rangeOriginLabelPaint.textSize="@dimen/range_tick_label_font_size"
+ androidPlot.graphWidget.domainLabelPaint.textSize="@dimen/domain_tick_label_font_size"
+ androidPlot.graphWidget.domainOriginLabelPaint.textSize="@dimen/domain_tick_label_font_size"
+ androidPlot.legendWidget.textPaint.textSize="@dimen/legend_text_font_size"
+ androidPlot.legendWidget.iconSizeMetrics.heightMetric.value="15dp"
+ androidPlot.legendWidget.iconSizeMetrics.widthMetric.value="15dp"
+ androidPlot.legendWidget.widthMetric.value="1"
+ androidPlot.legendWidget.heightMetric.value="25dp"
+ androidPlot.legendWidget.positionMetrics.xPositionMetric.value="0"
+ androidPlot.legendWidget.positionMetrics.anchor="right_bottom"
+ android:layout_weight="1"/>
+
+ <Button android:id="@+id/resetButton"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="Reset"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/Examples/DemoApp/res/layout/xy_plot_with_bq_img_example.xml b/Examples/DemoApp/res/layout/xy_plot_with_bq_img_example.xml
new file mode 100644
index 0000000..04e4315
--- /dev/null
+++ b/Examples/DemoApp/res/layout/xy_plot_with_bq_img_example.xml
@@ -0,0 +1,48 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin"
+ tools:context=".GraphMetricsActivity" >
+
+ <com.androidplot.xy.XYPlot
+ android:id="@+id/graph_metrics"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_centerHorizontal="true"
+ androidPlot.title="BG Image Example"
+ androidPlot.titleWidget.labelPaint.textSize="@dimen/title_font_size"
+ androidPlot.domainLabelWidget.labelPaint.textSize="@dimen/domain_label_font_size"
+ androidPlot.rangeLabelWidget.labelPaint.textSize="@dimen/range_label_font_size"
+ androidPlot.graphWidget.marginTop="20dp"
+ androidPlot.graphWidget.marginLeft="15dp"
+ androidPlot.graphWidget.marginBottom="25dp"
+ androidPlot.graphWidget.marginRight="10dp"
+ androidPlot.graphWidget.rangeLabelPaint.textSize="@dimen/range_tick_label_font_size"
+ androidPlot.graphWidget.rangeOriginLabelPaint.textSize="@dimen/range_tick_label_font_size"
+ androidPlot.graphWidget.domainLabelPaint.textSize="@dimen/domain_tick_label_font_size"
+ androidPlot.graphWidget.domainOriginLabelPaint.textSize="@dimen/domain_tick_label_font_size"
+ androidPlot.legendWidget.textPaint.textSize="@dimen/legend_text_font_size"
+ androidPlot.legendWidget.iconSizeMetrics.heightMetric.value="15dp"
+ androidPlot.legendWidget.iconSizeMetrics.widthMetric.value="15dp"
+ androidPlot.legendWidget.heightMetric.value="25dp"
+ androidPlot.legendWidget.positionMetrics.xPositionMetric.value="0"
+ androidPlot.legendWidget.positionMetrics.anchor="right_bottom"
+ android:layout_centerVertical="true"/>
+
+ <ToggleButton
+ android:id="@+id/style_toggle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignTop="@id/graph_metrics"
+ android:layout_alignRight="@id/graph_metrics"
+ android:layout_marginTop="15dp"
+ android:layout_marginRight="15dp"
+ android:textOn="bg on"
+ android:textOff="bg off"
+ android:onClick="onGraphStyleToggle" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/Examples/DemoApp/res/layout/xyregion_example.xml b/Examples/DemoApp/res/layout/xyregion_example.xml
new file mode 100644
index 0000000..db0bbf8
--- /dev/null
+++ b/Examples/DemoApp/res/layout/xyregion_example.xml
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ >
+
+ <com.androidplot.xy.XYPlot
+ android:id="@+id/xyRegionExamplePlot"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ androidPlot.title="Batting Practice Stats"
+ androidPlot.domainLabel="Pitch #"
+ androidPlot.rangeLabel="Distance"
+ androidPlot.titleWidget.labelPaint.textSize="@dimen/title_font_size"
+ androidPlot.domainLabelWidget.labelPaint.textSize="@dimen/domain_label_font_size"
+ androidPlot.rangeLabelWidget.labelPaint.textSize="@dimen/range_label_font_size"
+ androidPlot.graphWidget.marginTop="20dp"
+ androidPlot.graphWidget.marginLeft="35dp"
+ androidPlot.graphWidget.marginBottom="25dp"
+ androidPlot.graphWidget.marginRight="10dp"
+ androidPlot.graphWidget.rangeLabelPaint.textSize="@dimen/range_tick_label_font_size"
+ androidPlot.graphWidget.rangeOriginLabelPaint.textSize="@dimen/range_tick_label_font_size"
+ androidPlot.graphWidget.domainLabelPaint.textSize="@dimen/domain_tick_label_font_size"
+ androidPlot.graphWidget.domainOriginLabelPaint.textSize="@dimen/domain_tick_label_font_size"
+ androidPlot.legendWidget.textPaint.textSize="12dp"
+ androidPlot.legendWidget.iconSizeMetrics.heightMetric.value="15dp"
+ androidPlot.legendWidget.iconSizeMetrics.widthMetric.value="15dp"
+ androidPlot.legendWidget.heightMetric.value="40dp"
+ androidPlot.legendWidget.widthMetric.value="40dp"
+ android:layout_weight="1"
+ />
+
+ <LinearLayout
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center"
+ android:orientation="horizontal">
+ <CheckBox android:id="@+id/s1CheckBox"
+ android:text="Tim"
+ android:checked="true"
+ android:layout_height="wrap_content"
+ android:layout_width="fill_parent"/>
+ <CheckBox android:id="@+id/s2CheckBox"
+ android:text="Nick"
+ android:checked="true"
+ android:layout_height="wrap_content"
+ android:layout_width="fill_parent"/>
+ <CheckBox android:id="@+id/s3CheckBox"
+ android:text="Joe"
+ android:checked="true"
+ android:layout_height="wrap_content"
+ android:layout_width="fill_parent"/>
+ <CheckBox android:id="@+id/s4CheckBox"
+ android:text="James"
+ android:checked="true"
+ android:layout_height="wrap_content"
+ android:layout_width="fill_parent"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center"
+ android:orientation="horizontal">
+ <CheckBox android:id="@+id/r1CheckBox"
+ android:text="R1"
+ android:checked="true"
+ android:layout_height="wrap_content"
+ android:layout_width="fill_parent"/>
+ <CheckBox android:id="@+id/r2CheckBox"
+ android:text="R2"
+ android:checked="true"
+ android:layout_height="wrap_content"
+ android:layout_width="fill_parent"/>
+ <CheckBox android:id="@+id/r3CheckBox"
+ android:text="R3"
+ android:checked="true"
+ android:layout_height="wrap_content"
+ android:layout_width="fill_parent"/>
+ <CheckBox android:id="@+id/r4CheckBox"
+ android:text="R4"
+ android:checked="true"
+ android:layout_height="wrap_content"
+ android:layout_width="fill_parent"/>
+ <CheckBox android:id="@+id/r5CheckBox"
+ android:text="R5"
+ android:checked="true"
+ android:layout_height="wrap_content"
+ android:layout_width="fill_parent"/>
+ </LinearLayout>
+
+</LinearLayout>
+
diff --git a/Examples/DemoApp/res/values-hdpi/dimens.xml b/Examples/DemoApp/res/values-hdpi/dimens.xml
new file mode 100644
index 0000000..79fac2c
--- /dev/null
+++ b/Examples/DemoApp/res/values-hdpi/dimens.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- HDPI DIMENSIONS -->
+<resources>
+ <dimen name="title_font_size">30dp</dimen>
+ <dimen name="pie_segment_label_font_size">20dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/Examples/DemoApp/res/values-ldpi/dimens.xml b/Examples/DemoApp/res/values-ldpi/dimens.xml
new file mode 100644
index 0000000..81033f3
--- /dev/null
+++ b/Examples/DemoApp/res/values-ldpi/dimens.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- LDPI DIMENSIONS -->
+<resources>
+</resources>
\ No newline at end of file
diff --git a/Examples/DemoApp/res/values/attrs.xml b/Examples/DemoApp/res/values/attrs.xml
new file mode 100644
index 0000000..b88cddf
--- /dev/null
+++ b/Examples/DemoApp/res/values/attrs.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <declare-styleable name="XYPlot">
+ <attr name="androidplot.title" format="string"/>
+ </declare-styleable>
+</resources>
\ No newline at end of file
diff --git a/Examples/DemoApp/res/values/dimens.xml b/Examples/DemoApp/res/values/dimens.xml
new file mode 100644
index 0000000..379e32b
--- /dev/null
+++ b/Examples/DemoApp/res/values/dimens.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- DEFAULT DIMENSIONS -->
+<resources>
+
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="activity_horizontal_margin">16dp</dimen>
+ <dimen name="activity_vertical_margin">16dp</dimen>
+ <dimen name="pie_segment_label_font_size">10dp</dimen>
+ <dimen name="title_font_size">20dp</dimen>
+ <dimen name="domain_label_font_size">13dp</dimen>
+ <dimen name="range_label_font_size">13dp</dimen>
+ <dimen name="range_tick_label_font_size">15dp</dimen>
+ <dimen name="domain_tick_label_font_size">15dp</dimen>
+ <dimen name="legend_text_font_size">20dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/Examples/DemoApp/res/values/strings.xml b/Examples/DemoApp/res/values/strings.xml
new file mode 100644
index 0000000..0bde0cd
--- /dev/null
+++ b/Examples/DemoApp/res/values/strings.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="app_name">AndroidPlot Demo</string>
+
+ <string name="sxy_title">A Simple XY Plot</string>
+
+ <string name="ts_title">Time Series</string>
+ <string name="ts_plot1_title">Yearly UFO Sightings</string>
+</resources>
diff --git a/Examples/DemoApp/res/values/style.xml b/Examples/DemoApp/res/values/style.xml
new file mode 100644
index 0000000..d465715
--- /dev/null
+++ b/Examples/DemoApp/res/values/style.xml
@@ -0,0 +1,34 @@
+<!--
+ ~ Copyright 2012 AndroidPlot.com
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns="http://schemas.android.com/apk/lib/com.androidplot.xy">
+ <style name="sample_activity">
+ <item name="android:layout_width">fill_parent</item>
+ <item name="android:layout_height">fill_parent</item>
+ </style>
+
+ <style name="toc_button">
+ <item name="android:layout_width">fill_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ </style>
+
+ <style name="plotStyle1">
+ <!--<item name="androidplot.title">Default Title</item>
+ <item name="androidplot.domainLabel">my domain</item>-->
+ <!--<item name="XYPlot:androidplot.title">my domain</item>-->
+ </style>
+
+</resources>
\ No newline at end of file
diff --git a/Examples/DemoApp/res/xml/demo_app_widget_provider_info.xml b/Examples/DemoApp/res/xml/demo_app_widget_provider_info.xml
new file mode 100644
index 0000000..771c246
--- /dev/null
+++ b/Examples/DemoApp/res/xml/demo_app_widget_provider_info.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright 2012 AndroidPlot.com
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
+ android:minWidth="294dp"
+ android:minHeight="72dp"
+ android:updatePeriodMillis="86400000"
+ android:previewImage="@drawable/icon"
+ android:initialLayout="@layout/demo_app_widget"
+ android:resizeMode="horizontal|vertical">
+</appwidget-provider>
\ No newline at end of file
diff --git a/Examples/DemoApp/res/xml/line_point_formatter_with_plf1.xml b/Examples/DemoApp/res/xml/line_point_formatter_with_plf1.xml
new file mode 100644
index 0000000..47bf5f5
--- /dev/null
+++ b/Examples/DemoApp/res/xml/line_point_formatter_with_plf1.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<config
+ linePaint.strokeWidth="3dp"
+ linePaint.color="#00AA00"
+ vertexPaint.color="#007700"
+ fillPaint.color="#00000000"
+ pointLabelFormatter.textPaint.color="#FFFFFF"/>
\ No newline at end of file
diff --git a/Examples/DemoApp/res/xml/line_point_formatter_with_plf2.xml b/Examples/DemoApp/res/xml/line_point_formatter_with_plf2.xml
new file mode 100644
index 0000000..31894cd
--- /dev/null
+++ b/Examples/DemoApp/res/xml/line_point_formatter_with_plf2.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<config
+ linePaint.strokeWidth="3dp"
+ linePaint.color="#0000AA"
+ vertexPaint.color="#000077"
+ fillPaint.color="#00000000"
+ pointLabelFormatter.textPaint.color="#FFFFFF"/>
\ No newline at end of file
diff --git a/Examples/DemoApp/res/xml/pie_segment_formatter1.xml b/Examples/DemoApp/res/xml/pie_segment_formatter1.xml
new file mode 100644
index 0000000..1b0c7e0
--- /dev/null
+++ b/Examples/DemoApp/res/xml/pie_segment_formatter1.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<config
+ fillPaint.color="#FF0000"
+ labelPaint.textSize="@dimen/pie_segment_label_font_size"/>
\ No newline at end of file
diff --git a/Examples/DemoApp/res/xml/pie_segment_formatter2.xml b/Examples/DemoApp/res/xml/pie_segment_formatter2.xml
new file mode 100644
index 0000000..ad7a225
--- /dev/null
+++ b/Examples/DemoApp/res/xml/pie_segment_formatter2.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<config
+ fillPaint.color="#00FF00"
+ labelPaint.textSize="@dimen/pie_segment_label_font_size"/>
\ No newline at end of file
diff --git a/Examples/DemoApp/res/xml/pie_segment_formatter3.xml b/Examples/DemoApp/res/xml/pie_segment_formatter3.xml
new file mode 100644
index 0000000..5e02a39
--- /dev/null
+++ b/Examples/DemoApp/res/xml/pie_segment_formatter3.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<config
+ fillPaint.color="#0000FF"
+ labelPaint.textSize="@dimen/pie_segment_label_font_size"/>
\ No newline at end of file
diff --git a/Examples/DemoApp/res/xml/pie_segment_formatter4.xml b/Examples/DemoApp/res/xml/pie_segment_formatter4.xml
new file mode 100644
index 0000000..2cf1171
--- /dev/null
+++ b/Examples/DemoApp/res/xml/pie_segment_formatter4.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<config
+ fillPaint.color="#FF00FF"
+ labelPaint.textSize="@dimen/pie_segment_label_font_size"/>
\ No newline at end of file
diff --git a/Examples/DemoApp/src/com/androidplot/demos/BarPlotExampleActivity.java b/Examples/DemoApp/src/com/androidplot/demos/BarPlotExampleActivity.java
new file mode 100644
index 0000000..4c79dd8
--- /dev/null
+++ b/Examples/DemoApp/src/com/androidplot/demos/BarPlotExampleActivity.java
@@ -0,0 +1,425 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.demos;
+
+import java.text.DateFormatSymbols;
+import java.text.FieldPosition;
+import java.text.NumberFormat;
+import java.text.ParsePosition;
+import java.util.Arrays;
+import java.util.Iterator;
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.PointF;
+import android.os.Bundle;
+import android.util.Pair;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.ArrayAdapter;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.SeekBar;
+import android.widget.Spinner;
+
+import com.androidplot.LineRegion;
+import com.androidplot.ui.AnchorPosition;
+import com.androidplot.ui.SeriesRenderer;
+import com.androidplot.ui.SizeLayoutType;
+import com.androidplot.ui.SizeMetrics;
+import com.androidplot.ui.TextOrientationType;
+import com.androidplot.ui.widget.TextLabelWidget;
+import com.androidplot.util.PixelUtils;
+import com.androidplot.xy.*;
+import com.androidplot.ui.XLayoutStyle;
+import com.androidplot.ui.YLayoutStyle;
+
+/**
+ * The simplest possible example of using AndroidPlot to plot some data.
+ */
+public class BarPlotExampleActivity extends Activity
+{
+
+ private static final String NO_SELECTION_TXT = "Touch bar to select.";
+ private XYPlot plot;
+
+ private CheckBox series1CheckBox;
+ private CheckBox series2CheckBox;
+ private Spinner spRenderStyle, spWidthStyle, spSeriesSize;
+ private SeekBar sbFixedWidth, sbVariableWidth;
+
+ private XYSeries series1;
+ private XYSeries series2;
+ private enum SeriesSize {
+ TEN,
+ TWENTY,
+ SIXTY
+ }
+
+ // Create a couple arrays of y-values to plot:
+ Number[] series1Numbers10 = {2, null, 5, 2, 7, 4, 3, 7, 4, 5};
+ Number[] series2Numbers10 = {4, 6, 3, null, 2, 0, 7, 4, 5, 4};
+ Number[] series1Numbers20 = {2, null, 5, 2, 7, 4, 3, 7, 4, 5, 7, 4, 5, 8, 5, 3, 6, 3, 9, 3};
+ Number[] series2Numbers20 = {4, 6, 3, null, 2, 0, 7, 4, 5, 4, 9, 6, 2, 8, 4, 0, 7, 4, 7, 9};
+ Number[] series1Numbers60 = {2, null, 5, 2, 7, 4, 3, 7, 4, 5, 7, 4, 5, 8, 5, 3, 6, 3, 9, 3, 2, null, 5, 2, 7, 4, 3, 7, 4, 5, 7, 4, 5, 8, 5, 3, 6, 3, 9, 3, 2, null, 5, 2, 7, 4, 3, 7, 4, 5, 7, 4, 5, 8, 5, 3, 6, 3, 9, 3};
+ Number[] series2Numbers60 = {4, 6, 3, null, 2, 0, 7, 4, 5, 4, 9, 6, 2, 8, 4, 0, 7, 4, 7, 9, 4, 6, 3, null, 2, 0, 7, 4, 5, 4, 9, 6, 2, 8, 4, 0, 7, 4, 7, 9, 4, 6, 3, null, 2, 0, 7, 4, 5, 4, 9, 6, 2, 8, 4, 0, 7, 4, 7, 9};
+ Number[] series1Numbers = series1Numbers10;
+ Number[] series2Numbers = series2Numbers10;
+
+ private MyBarFormatter formatter1 =
+ new MyBarFormatter(Color.argb(200, 100, 150, 100), Color.LTGRAY);
+
+ private MyBarFormatter formatter2 =
+ new MyBarFormatter(Color.argb(200, 100, 100, 150), Color.LTGRAY);
+
+ private MyBarFormatter selectionFormatter =
+ new MyBarFormatter(Color.YELLOW, Color.WHITE);
+
+ private TextLabelWidget selectionWidget;
+
+ private Pair<Integer, XYSeries> selection;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState)
+ {
+
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.bar_plot_example);
+
+ // initialize our XYPlot reference:
+ plot = (XYPlot) findViewById(R.id.mySimpleXYPlot);
+
+ selectionWidget = new TextLabelWidget(plot.getLayoutManager(), NO_SELECTION_TXT,
+ new SizeMetrics(
+ PixelUtils.dpToPix(100), SizeLayoutType.ABSOLUTE,
+ PixelUtils.dpToPix(100), SizeLayoutType.ABSOLUTE),
+ TextOrientationType.HORIZONTAL);
+
+ selectionWidget.getLabelPaint().setTextSize(PixelUtils.dpToPix(16));
+
+ // add a dark, semi-transparent background to the selection label widget:
+ Paint p = new Paint();
+ p.setARGB(100, 0, 0, 0);
+ selectionWidget.setBackgroundPaint(p);
+
+ selectionWidget.position(
+ 0, XLayoutStyle.RELATIVE_TO_CENTER,
+ PixelUtils.dpToPix(45), YLayoutStyle.ABSOLUTE_FROM_TOP,
+ AnchorPosition.TOP_MIDDLE);
+ selectionWidget.pack();
+
+
+ // reduce the number of range labels
+ plot.setTicksPerRangeLabel(3);
+ plot.setRangeLowerBoundary(0, BoundaryMode.FIXED);
+ plot.getGraphWidget().setGridPadding(30, 10, 30, 0);
+
+ plot.setTicksPerDomainLabel(2);
+
+
+ // setup checkbox listers:
+ series1CheckBox = (CheckBox) findViewById(R.id.s1CheckBox);
+ series1CheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
+ onS1CheckBoxClicked(b);
+ }
+ });
+
+ series2CheckBox = (CheckBox) findViewById(R.id.s2CheckBox);
+ series2CheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton compoundButton, boolean b) {onS2CheckBoxClicked(b);
+ }
+ });
+
+ plot.setOnTouchListener(new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View view, MotionEvent motionEvent) {
+ if(motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
+ onPlotClicked(new PointF(motionEvent.getX(), motionEvent.getY()));
+ }
+ return true;
+ }
+ });
+
+ spRenderStyle = (Spinner) findViewById(R.id.spRenderStyle);
+ ArrayAdapter <BarRenderer.BarRenderStyle> adapter = new ArrayAdapter <BarRenderer.BarRenderStyle> (this, android.R.layout.simple_spinner_item, BarRenderer.BarRenderStyle.values() );
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ spRenderStyle.setAdapter(adapter);
+ spRenderStyle.setSelection(BarRenderer.BarRenderStyle.OVERLAID.ordinal());
+ spRenderStyle.setOnItemSelectedListener(new OnItemSelectedListener() {
+ public void onItemSelected(AdapterView<?> arg0, View arg1,int arg2, long arg3) {
+ updatePlot();
+ }
+ @Override
+ public void onNothingSelected(AdapterView<?> arg0) {
+ }
+ });
+
+ spWidthStyle = (Spinner) findViewById(R.id.spWidthStyle);
+ ArrayAdapter <BarRenderer.BarWidthStyle> adapter1 = new ArrayAdapter <BarRenderer.BarWidthStyle> (this, android.R.layout.simple_spinner_item, BarRenderer.BarWidthStyle.values() );
+ adapter1.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ spWidthStyle.setAdapter(adapter1);
+ spWidthStyle.setSelection(BarRenderer.BarWidthStyle.FIXED_WIDTH.ordinal());
+ spWidthStyle.setOnItemSelectedListener(new OnItemSelectedListener() {
+ public void onItemSelected(AdapterView<?> arg0, View arg1,int arg2, long arg3) {
+ if (BarRenderer.BarWidthStyle.FIXED_WIDTH.equals(spWidthStyle.getSelectedItem())) {
+ sbFixedWidth.setVisibility(View.VISIBLE);
+ sbVariableWidth.setVisibility(View.INVISIBLE);
+ } else {
+ sbFixedWidth.setVisibility(View.INVISIBLE);
+ sbVariableWidth.setVisibility(View.VISIBLE);
+ }
+ updatePlot();
+ }
+ @Override
+ public void onNothingSelected(AdapterView<?> arg0) {
+ }
+ });
+
+ spSeriesSize = (Spinner) findViewById(R.id.spSeriesSize);
+ ArrayAdapter <SeriesSize> adapter11 = new ArrayAdapter <SeriesSize> (this, android.R.layout.simple_spinner_item, SeriesSize.values() );
+ adapter11.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ spSeriesSize.setAdapter(adapter11);
+ spSeriesSize.setSelection(SeriesSize.TEN.ordinal());
+ spSeriesSize.setOnItemSelectedListener(new OnItemSelectedListener() {
+ public void onItemSelected(AdapterView<?> arg0, View arg1,int arg2, long arg3) {
+ switch ((SeriesSize)arg0.getSelectedItem()) {
+ case TEN:
+ series1Numbers = series1Numbers10;
+ series2Numbers = series2Numbers10;
+ break;
+ case TWENTY:
+ series1Numbers = series1Numbers20;
+ series2Numbers = series2Numbers20;
+ break;
+ case SIXTY:
+ series1Numbers = series1Numbers60;
+ series2Numbers = series2Numbers60;
+ break;
+ default:
+ break;
+ }
+ updatePlot();
+ }
+ @Override
+ public void onNothingSelected(AdapterView<?> arg0) {
+ }
+ });
+
+
+ sbFixedWidth = (SeekBar) findViewById(R.id.sbFixed);
+ sbFixedWidth.setProgress(50);
+ sbFixedWidth.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int i, boolean b) {updatePlot();}
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {}
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {}
+ });
+
+
+ sbVariableWidth = (SeekBar) findViewById(R.id.sbVariable);
+ sbVariableWidth.setProgress(1);
+ sbVariableWidth.setVisibility(View.INVISIBLE);
+ sbVariableWidth.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int i, boolean b) {updatePlot();}
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {}
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {}
+ });
+
+ plot.setDomainValueFormat(new NumberFormat() {
+ @Override
+ public StringBuffer format(double value, StringBuffer buffer, FieldPosition field) {
+ int year = (int) (value + 0.5d) / 12;
+ int month = (int) ((value + 0.5d) % 12);
+ return new StringBuffer(DateFormatSymbols.getInstance().getShortMonths()[month] + " '0" + year);
+ }
+
+ @Override
+ public StringBuffer format(long value, StringBuffer buffer, FieldPosition field) {
+ throw new UnsupportedOperationException("Not yet implemented.");
+ }
+
+ @Override
+ public Number parse(String string, ParsePosition position) {
+ throw new UnsupportedOperationException("Not yet implemented.");
+ }
+ });
+ updatePlot();
+
+ }
+
+ private void updatePlot() {
+
+ // Remove all current series from each plot
+ Iterator<XYSeries> iterator1 = plot.getSeriesSet().iterator();
+ while(iterator1.hasNext()) {
+ XYSeries setElement = iterator1.next();
+ plot.removeSeries(setElement);
+ }
+
+ // Setup our Series with the selected number of elements
+ series1 = new SimpleXYSeries(Arrays.asList(series1Numbers), SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "Us");
+ series2 = new SimpleXYSeries(Arrays.asList(series2Numbers), SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "Them");
+
+ // add a new series' to the xyplot:
+ if (series1CheckBox.isChecked()) plot.addSeries(series1, formatter1);
+ if (series2CheckBox.isChecked()) plot.addSeries(series2, formatter2);
+
+ // Setup the BarRenderer with our selected options
+ MyBarRenderer renderer = ((MyBarRenderer)plot.getRenderer(MyBarRenderer.class));
+ renderer.setBarRenderStyle((BarRenderer.BarRenderStyle)spRenderStyle.getSelectedItem());
+ renderer.setBarWidthStyle((BarRenderer.BarWidthStyle)spWidthStyle.getSelectedItem());
+ renderer.setBarWidth(sbFixedWidth.getProgress());
+ renderer.setBarGap(sbVariableWidth.getProgress());
+
+ if (BarRenderer.BarRenderStyle.STACKED.equals(spRenderStyle.getSelectedItem())) {
+ plot.setRangeTopMin(15);
+ } else {
+ plot.setRangeTopMin(0);
+ }
+
+ plot.redraw();
+
+ }
+
+ private void onPlotClicked(PointF point) {
+
+ // make sure the point lies within the graph area. we use gridrect
+ // because it accounts for margins and padding as well.
+ if (plot.getGraphWidget().getGridRect().contains(point.x, point.y)) {
+ Number x = plot.getXVal(point);
+ Number y = plot.getYVal(point);
+
+
+ selection = null;
+ double xDistance = 0;
+ double yDistance = 0;
+
+ // find the closest value to the selection:
+ for (XYSeries series : plot.getSeriesSet()) {
+ for (int i = 0; i < series.size(); i++) {
+ Number thisX = series.getX(i);
+ Number thisY = series.getY(i);
+ if (thisX != null && thisY != null) {
+ double thisXDistance =
+ LineRegion.measure(x, thisX).doubleValue();
+ double thisYDistance =
+ LineRegion.measure(y, thisY).doubleValue();
+ if (selection == null) {
+ selection = new Pair<Integer, XYSeries>(i, series);
+ xDistance = thisXDistance;
+ yDistance = thisYDistance;
+ } else if (thisXDistance < xDistance) {
+ selection = new Pair<Integer, XYSeries>(i, series);
+ xDistance = thisXDistance;
+ yDistance = thisYDistance;
+ } else if (thisXDistance == xDistance &&
+ thisYDistance < yDistance &&
+ thisY.doubleValue() >= y.doubleValue()) {
+ selection = new Pair<Integer, XYSeries>(i, series);
+ xDistance = thisXDistance;
+ yDistance = thisYDistance;
+ }
+ }
+ }
+ }
+
+ } else {
+ // if the press was outside the graph area, deselect:
+ selection = null;
+ }
+
+ if(selection == null) {
+ selectionWidget.setText(NO_SELECTION_TXT);
+ } else {
+ selectionWidget.setText("Selected: " + selection.second.getTitle() +
+ " Value: " + selection.second.getY(selection.first));
+ }
+ plot.redraw();
+ }
+
+ private void onS1CheckBoxClicked(boolean checked) {
+ if (checked) {
+ plot.addSeries(series1, formatter1);
+ } else {
+ plot.removeSeries(series1);
+ }
+ plot.redraw();
+ }
+
+ private void onS2CheckBoxClicked(boolean checked) {
+ if (checked) {
+ plot.addSeries(series2, formatter2);
+ } else {
+ plot.removeSeries(series2);
+ }
+ plot.redraw();
+ }
+
+ class MyBarFormatter extends BarFormatter {
+ public MyBarFormatter(int fillColor, int borderColor) {
+ super(fillColor, borderColor);
+ }
+
+ @Override
+ public Class<? extends SeriesRenderer> getRendererClass() {
+ return MyBarRenderer.class;
+ }
+
+ @Override
+ public SeriesRenderer getRendererInstance(XYPlot plot) {
+ return new MyBarRenderer(plot);
+ }
+ }
+
+ class MyBarRenderer extends BarRenderer<MyBarFormatter> {
+
+ public MyBarRenderer(XYPlot plot) {
+ super(plot);
+ }
+
+ /**
+ * Implementing this method to allow us to inject our
+ * special selection formatter.
+ * @param index index of the point being rendered.
+ * @param series XYSeries to which the point being rendered belongs.
+ * @return
+ */
+ //@Override
+ // TODO: figure out why using @Override screws up the Maven builds
+ protected MyBarFormatter getFormatter(int index, XYSeries series) {
+ if(selection != null &&
+ selection.second == series &&
+ selection.first == index) {
+ return selectionFormatter;
+ } else {
+ return getFormatter(series);
+ }
+ }
+ }
+}
diff --git a/Examples/DemoApp/src/com/androidplot/demos/DualScaleXYPlotExampleActivity.java b/Examples/DemoApp/src/com/androidplot/demos/DualScaleXYPlotExampleActivity.java
new file mode 100644
index 0000000..d06581e
--- /dev/null
+++ b/Examples/DemoApp/src/com/androidplot/demos/DualScaleXYPlotExampleActivity.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.demos;
+import java.util.Arrays;
+import java.util.Iterator;
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.os.Build;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+
+import com.androidplot.ui.AnchorPosition;
+import com.androidplot.ui.DynamicTableModel;
+import com.androidplot.ui.SizeLayoutType;
+import com.androidplot.ui.SizeMetrics;
+import com.androidplot.xy.LineAndPointFormatter;
+import com.androidplot.xy.PointLabelFormatter;
+import com.androidplot.xy.SimpleXYSeries;
+import com.androidplot.ui.XLayoutStyle;
+import com.androidplot.xy.XYGraphWidget;
+import com.androidplot.xy.XYLegendWidget;
+import com.androidplot.xy.XYPlot;
+import com.androidplot.xy.XYSeries;
+import com.androidplot.ui.YLayoutStyle;
+
+/**
+ * The simplest possible example of using AndroidPlot to plot some data.
+ */
+public class DualScaleXYPlotExampleActivity extends Activity implements OnClickListener
+{
+
+ private XYPlot myXYPlot_LEFT, myXYPlot_RIGHT;
+ private Boolean series2_onRight = true;
+ private LineAndPointFormatter series1Format, series2Format;
+ private Button button;
+
+ // Declare and enable buttons to toggle whether the 2nd series is on left or right.
+
+ // Create a couple arrays of y-values to plot:
+ private Number[] series1Numbers = {1, 8, 5, 2, 7, 4};
+ private Number[] series2Numbers = {444, 613, 353, 876, 924, 1004};
+ XYSeries series1, series2;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState)
+ {
+
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.dual_scale_xy_plot_example);
+
+ // Setup the LEFT Plot as normal
+ myXYPlot_LEFT = (XYPlot) findViewById(R.id.mySimpleXYPlot_L);
+ myXYPlot_RIGHT = (XYPlot) findViewById(R.id.mySimpleXYPlot_R);
+
+ // Disable Hardware Acceleration on the xyPlot view object.
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ myXYPlot_LEFT.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
+ myXYPlot_RIGHT.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
+ }
+
+ XYGraphWidget graphWidget_LEFT = myXYPlot_LEFT.getGraphWidget();
+ graphWidget_LEFT.setRangeAxisPosition(true, false, 4, "10");
+ graphWidget_LEFT.setMarginRight(0);
+ graphWidget_LEFT.setPaddingRight(30);
+ graphWidget_LEFT.setRangeLabelVerticalOffset(-3);
+ graphWidget_LEFT.setRangeLabelWidth(50);
+
+ // Setup the second Plot with Right-hand Scale and otherwise invisible.
+ myXYPlot_RIGHT.getDomainLabelWidget().setVisible(false);
+ myXYPlot_RIGHT.getRangeLabelWidget().setVisible(false);
+ myXYPlot_RIGHT.getTitleWidget().setVisible(false);
+ myXYPlot_RIGHT.getBorderPaint().setAlpha(0);
+ myXYPlot_RIGHT.getBackgroundPaint().setAlpha(0);
+ XYGraphWidget graphWidget_RIGHT = myXYPlot_RIGHT.getGraphWidget();
+ graphWidget_RIGHT.getBackgroundPaint().setAlpha(0);
+ graphWidget_RIGHT.getDomainLabelPaint().setAlpha(0);
+ graphWidget_RIGHT.getGridBackgroundPaint().setAlpha(0);
+ graphWidget_RIGHT.getDomainOriginLabelPaint().setAlpha(0);
+ graphWidget_RIGHT.getRangeOriginLinePaint().setAlpha(0);
+ graphWidget_RIGHT.getDomainGridLinePaint().setAlpha(0);
+ graphWidget_RIGHT.getRangeGridLinePaint().setAlpha(0);
+ graphWidget_RIGHT.setRangeAxisPosition(false, false, 4, "10");
+
+ // Copy where possible from the LEFT plot
+ graphWidget_RIGHT.setRangeLabelVerticalOffset(graphWidget_LEFT.getRangeLabelVerticalOffset());
+ graphWidget_RIGHT.setMarginRight(graphWidget_LEFT.getMarginRight());
+ graphWidget_RIGHT.setPaddingRight(graphWidget_LEFT.getPaddingRight());
+ graphWidget_RIGHT.setRangeLabelWidth(graphWidget_LEFT.getRangeLabelWidth());
+
+ // Position the Graphs
+ myXYPlot_LEFT.getGraphWidget().position(
+ 0 ,XLayoutStyle.ABSOLUTE_FROM_LEFT,10,YLayoutStyle.ABSOLUTE_FROM_TOP,AnchorPosition.LEFT_TOP);
+ myXYPlot_RIGHT.getGraphWidget().position(
+ 49,XLayoutStyle.ABSOLUTE_FROM_LEFT,10,YLayoutStyle.ABSOLUTE_FROM_TOP,AnchorPosition.LEFT_TOP);
+
+ // Setup and Position the LEFT Legend
+ XYLegendWidget legendWidget_LEFT = myXYPlot_LEFT.getLegendWidget();
+ legendWidget_LEFT.setTableModel(new DynamicTableModel(1, 3));
+ legendWidget_LEFT.getTextPaint().setTextSize(20);
+ legendWidget_LEFT.setSize(new SizeMetrics(100, SizeLayoutType.ABSOLUTE, 75, SizeLayoutType.FILL));
+ legendWidget_LEFT.setPadding(1, 1, 1, 1);
+ myXYPlot_LEFT.getGraphWidget().position(
+ 55, XLayoutStyle.ABSOLUTE_FROM_LEFT, 15, YLayoutStyle.ABSOLUTE_FROM_TOP, AnchorPosition.LEFT_TOP);
+
+ // Setup and Position the RIGHT Legend
+ XYLegendWidget legendWidget_RIGHT = myXYPlot_RIGHT.getLegendWidget();
+ legendWidget_RIGHT.setTableModel(new DynamicTableModel(1, 3));
+ legendWidget_RIGHT.getTextPaint().setTextSize(20);
+ legendWidget_RIGHT.setSize(new SizeMetrics(100, SizeLayoutType.ABSOLUTE, 110, SizeLayoutType.ABSOLUTE));
+ legendWidget_RIGHT.setPadding(1, 1, 1, 1);
+ myXYPlot_RIGHT.getGraphWidget().position(
+ 25, XLayoutStyle.ABSOLUTE_FROM_RIGHT, 15, YLayoutStyle.ABSOLUTE_FROM_TOP, AnchorPosition.RIGHT_TOP);
+
+ // Setup the Series
+ series1 = new SimpleXYSeries(Arrays.asList(series1Numbers), SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "Series1");
+ series2 = new SimpleXYSeries(Arrays.asList(series2Numbers), SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "Series2");
+
+ // Setup the formatters
+ series1Format = new LineAndPointFormatter(Color.rgb(0, 200, 0), Color.rgb(0, 100, 0), null, new PointLabelFormatter(Color.WHITE));
+ series2Format = new LineAndPointFormatter(Color.rgb(0, 0, 200), Color.rgb(0, 0, 100), null, new PointLabelFormatter(Color.WHITE));
+
+ // Setup the Button
+ button = (Button)findViewById(R.id.toggleSeries2);
+ button.setOnClickListener(this);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ updateView();
+
+ }
+
+ private void updateView() {
+
+ // Remove all current series from each plot
+ Iterator<XYSeries> iterator1 = myXYPlot_LEFT.getSeriesSet().iterator();
+ while(iterator1.hasNext()) {
+ XYSeries setElement = iterator1.next();
+ myXYPlot_LEFT.removeSeries(setElement);
+ }
+ Iterator<XYSeries> iterator2 = myXYPlot_RIGHT.getSeriesSet().iterator();
+ while(iterator2.hasNext()) {
+ XYSeries setElement = iterator2.next();
+ myXYPlot_RIGHT.removeSeries(setElement);
+ }
+
+ // Add series to each plot as needed.
+ myXYPlot_LEFT.addSeries(series1, series1Format);
+ if (series2_onRight) {
+ myXYPlot_RIGHT.addSeries(series2, series2Format);
+ } else {
+ myXYPlot_LEFT.addSeries(series2, series2Format);
+ }
+
+ // Finalise each Plot based on whether they have any series or not.
+ if (! myXYPlot_RIGHT.getSeriesSet().isEmpty()) {
+ myXYPlot_RIGHT.setVisibility(XYPlot.VISIBLE);
+ myXYPlot_RIGHT.redraw();
+ } else {
+ myXYPlot_RIGHT.setVisibility(XYPlot.INVISIBLE);
+ }
+
+ if (! myXYPlot_LEFT.getSeriesSet().isEmpty()) {
+ myXYPlot_LEFT.setVisibility(XYPlot.VISIBLE);
+ myXYPlot_LEFT.redraw();
+ } else {
+ myXYPlot_LEFT.setVisibility(XYPlot.INVISIBLE);
+ }
+
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (series2_onRight) {
+ series2_onRight = false;
+ } else {
+ series2_onRight = true;
+ }
+ updateView();
+ }
+}
diff --git a/Examples/DemoApp/src/com/androidplot/demos/DynamicXYPlotActivity.java b/Examples/DemoApp/src/com/androidplot/demos/DynamicXYPlotActivity.java
new file mode 100644
index 0000000..b4e6456
--- /dev/null
+++ b/Examples/DemoApp/src/com/androidplot/demos/DynamicXYPlotActivity.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright 2013 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.demos;
+import android.app.Activity;
+import android.graphics.Color;
+import android.graphics.DashPathEffect;
+import android.graphics.Paint;
+import android.os.Bundle;
+import com.androidplot.Plot;
+import com.androidplot.util.PixelUtils;
+import com.androidplot.xy.XYSeries;
+import com.androidplot.xy.*;
+
+import java.text.DecimalFormat;
+import java.util.Observable;
+import java.util.Observer;
+
+public class DynamicXYPlotActivity extends Activity {
+
+ // redraws a plot whenever an update is received:
+ private class MyPlotUpdater implements Observer {
+ Plot plot;
+
+ public MyPlotUpdater(Plot plot) {
+ this.plot = plot;
+ }
+
+ @Override
+ public void update(Observable o, Object arg) {
+ plot.redraw();
+ }
+ }
+
+ private XYPlot dynamicPlot;
+ private MyPlotUpdater plotUpdater;
+ SampleDynamicXYDatasource data;
+ private Thread myThread;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+
+ // android boilerplate stuff
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.dynamicxyplot_example);
+
+ // get handles to our View defined in layout.xml:
+ dynamicPlot = (XYPlot) findViewById(R.id.dynamicXYPlot);
+
+ plotUpdater = new MyPlotUpdater(dynamicPlot);
+
+ // only display whole numbers in domain labels
+ dynamicPlot.getGraphWidget().setDomainValueFormat(new DecimalFormat("0"));
+
+ // getInstance and position datasets:
+ data = new SampleDynamicXYDatasource();
+ SampleDynamicSeries sine1Series = new SampleDynamicSeries(data, 0, "Sine 1");
+ SampleDynamicSeries sine2Series = new SampleDynamicSeries(data, 1, "Sine 2");
+
+ LineAndPointFormatter formatter1 = new LineAndPointFormatter(
+ Color.rgb(0, 0, 0), null, null, null);
+ formatter1.getLinePaint().setStrokeJoin(Paint.Join.ROUND);
+ formatter1.getLinePaint().setStrokeWidth(10);
+ dynamicPlot.addSeries(sine1Series,
+ formatter1);
+
+ LineAndPointFormatter formatter2 =
+ new LineAndPointFormatter(Color.rgb(0, 0, 200), null, null, null);
+ formatter2.getLinePaint().setStrokeWidth(10);
+ formatter2.getLinePaint().setStrokeJoin(Paint.Join.ROUND);
+
+ //formatter2.getFillPaint().setAlpha(220);
+ dynamicPlot.addSeries(sine2Series, formatter2);
+
+ // hook up the plotUpdater to the data model:
+ data.addObserver(plotUpdater);
+
+ // thin out domain tick labels so they dont overlap each other:
+ dynamicPlot.setDomainStepMode(XYStepMode.INCREMENT_BY_VAL);
+ dynamicPlot.setDomainStepValue(5);
+
+ dynamicPlot.setRangeStepMode(XYStepMode.INCREMENT_BY_VAL);
+ dynamicPlot.setRangeStepValue(10);
+
+ dynamicPlot.setRangeValueFormat(new DecimalFormat("###.#"));
+
+ // uncomment this line to freeze the range boundaries:
+ dynamicPlot.setRangeBoundaries(-100, 100, BoundaryMode.FIXED);
+
+ // create a dash effect for domain and range grid lines:
+ DashPathEffect dashFx = new DashPathEffect(
+ new float[] {PixelUtils.dpToPix(3), PixelUtils.dpToPix(3)}, 0);
+ dynamicPlot.getGraphWidget().getDomainGridLinePaint().setPathEffect(dashFx);
+ dynamicPlot.getGraphWidget().getRangeGridLinePaint().setPathEffect(dashFx);
+
+
+ }
+
+ @Override
+ public void onResume() {
+ // kick off the data generating thread:
+ myThread = new Thread(data);
+ myThread.start();
+ super.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ data.stopThread();
+ super.onPause();
+ }
+
+ class SampleDynamicXYDatasource implements Runnable {
+
+ // encapsulates management of the observers watching this datasource for update events:
+ class MyObservable extends Observable {
+ @Override
+ public void notifyObservers() {
+ setChanged();
+ super.notifyObservers();
+ }
+ }
+
+ private static final double FREQUENCY = 5; // larger is lower frequency
+ private static final int MAX_AMP_SEED = 100;
+ private static final int MIN_AMP_SEED = 10;
+ private static final int AMP_STEP = 1;
+ public static final int SINE1 = 0;
+ public static final int SINE2 = 1;
+ private static final int SAMPLE_SIZE = 30;
+ private int phase = 0;
+ private int sinAmp = 1;
+ private MyObservable notifier;
+ private boolean keepRunning = false;
+
+ {
+ notifier = new MyObservable();
+ }
+
+ public void stopThread() {
+ keepRunning = false;
+ }
+
+ //@Override
+ public void run() {
+ try {
+ keepRunning = true;
+ boolean isRising = true;
+ while (keepRunning) {
+
+ Thread.sleep(10); // decrease or remove to speed up the refresh rate.
+ phase++;
+ if (sinAmp >= MAX_AMP_SEED) {
+ isRising = false;
+ } else if (sinAmp <= MIN_AMP_SEED) {
+ isRising = true;
+ }
+
+ if (isRising) {
+ sinAmp += AMP_STEP;
+ } else {
+ sinAmp -= AMP_STEP;
+ }
+ notifier.notifyObservers();
+ }
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public int getItemCount(int series) {
+ return SAMPLE_SIZE;
+ }
+
+ public Number getX(int series, int index) {
+ if (index >= SAMPLE_SIZE) {
+ throw new IllegalArgumentException();
+ }
+ return index;
+ }
+
+ public Number getY(int series, int index) {
+ if (index >= SAMPLE_SIZE) {
+ throw new IllegalArgumentException();
+ }
+ double angle = (index + (phase))/FREQUENCY;
+ double amp = sinAmp * Math.sin(angle);
+ switch (series) {
+ case SINE1:
+ return amp;
+ case SINE2:
+ return -amp;
+ default:
+ throw new IllegalArgumentException();
+ }
+ }
+
+ public void addObserver(Observer observer) {
+ notifier.addObserver(observer);
+ }
+
+ public void removeObserver(Observer observer) {
+ notifier.deleteObserver(observer);
+ }
+
+ }
+
+ class SampleDynamicSeries implements XYSeries {
+ private SampleDynamicXYDatasource datasource;
+ private int seriesIndex;
+ private String title;
+
+ public SampleDynamicSeries(SampleDynamicXYDatasource datasource, int seriesIndex, String title) {
+ this.datasource = datasource;
+ this.seriesIndex = seriesIndex;
+ this.title = title;
+ }
+
+ @Override
+ public String getTitle() {
+ return title;
+ }
+
+ @Override
+ public int size() {
+ return datasource.getItemCount(seriesIndex);
+ }
+
+ @Override
+ public Number getX(int index) {
+ return datasource.getX(seriesIndex, index);
+ }
+
+ @Override
+ public Number getY(int index) {
+ return datasource.getY(seriesIndex, index);
+ }
+ }
+}
diff --git a/Examples/DemoApp/src/com/androidplot/demos/GlobalDefs.java b/Examples/DemoApp/src/com/androidplot/demos/GlobalDefs.java
new file mode 100644
index 0000000..28b74d9
--- /dev/null
+++ b/Examples/DemoApp/src/com/androidplot/demos/GlobalDefs.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.demos;
+
+public interface GlobalDefs {
+ static final float PLOT_TITLE_FONT_SIZE_DP = 15;
+ static final float PLOT_DOMAIN_LABEL_FONT_SIZE_DP = 10;
+ static final float PLOT_RANGE_LABEL_FONT_SIZE_DP = 10;
+ static final float PLOT_TICK_LABEL_FONT_SIZE_DP = 10;
+}
diff --git a/Examples/DemoApp/src/com/androidplot/demos/ListViewActivity.java b/Examples/DemoApp/src/com/androidplot/demos/ListViewActivity.java
new file mode 100644
index 0000000..d80ac50
--- /dev/null
+++ b/Examples/DemoApp/src/com/androidplot/demos/ListViewActivity.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.demos;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import com.androidplot.Plot;
+import com.androidplot.xy.XYSeries;
+import com.androidplot.xy.LineAndPointFormatter;
+import com.androidplot.xy.SimpleXYSeries;
+import com.androidplot.xy.XYPlot;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+public class ListViewActivity extends Activity {
+ private static final int NUM_PLOTS = 10;
+ private static final int NUM_POINTS_PER_SERIES = 10;
+ private static final int NUM_SERIES_PER_PLOT = 5;
+ private ListView lv;
+
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.listview_example);
+ lv = (ListView) findViewById(R.id.listView1);
+ lv.setAdapter(new MyViewAdapter(getApplicationContext(), R.layout.listview_example_item, null));
+ }
+
+ class MyViewAdapter extends ArrayAdapter<View> {
+ public MyViewAdapter(Context context, int resId, List<View> views) {
+ super(context, resId, views);
+ }
+
+ @Override
+ public int getCount() {
+ return 5;
+ }
+
+ @Override
+ public View getView(int pos, View convertView, ViewGroup parent) {
+ LayoutInflater inf = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+ View v = convertView;
+ if (v == null) {
+ v = inf.inflate(R.layout.listview_example_item, parent, false);
+ }
+
+ Plot p = (XYPlot) v.findViewById(R.id.xyplot);
+ Random generator = new Random();
+
+ for (int k = 0; k < NUM_SERIES_PER_PLOT; k++) {
+ ArrayList<Number> nums = new ArrayList<Number>();
+ for (int j = 0; j < NUM_POINTS_PER_SERIES; j++) {
+ nums.add(generator.nextFloat());
+ }
+
+ double rl = Math.random();
+ double gl = Math.random();
+ double bl = Math.random();
+
+ double rp = Math.random();
+ double gp = Math.random();
+ double bp = Math.random();
+
+ XYSeries series = new SimpleXYSeries(nums, SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "S" + k);
+ p.addSeries(series, new LineAndPointFormatter(
+ Color.rgb(new Double(rl * 255).intValue(), new Double(gl * 255).intValue(), new Double(bl * 255).intValue()),
+ Color.rgb(new Double(rp * 255).intValue(), new Double(gp * 255).intValue(), new Double(bp * 255).intValue()),
+ null, null));
+ }
+ return v;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Examples/DemoApp/src/com/androidplot/demos/MainActivity.java b/Examples/DemoApp/src/com/androidplot/demos/MainActivity.java
new file mode 100644
index 0000000..51c1441
--- /dev/null
+++ b/Examples/DemoApp/src/com/androidplot/demos/MainActivity.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.demos;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+
+public class MainActivity extends Activity
+{
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+
+ Button startSimplePieExButton = (Button) findViewById(R.id.startSimplePieExButton);
+ startSimplePieExButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ startActivity(new Intent(MainActivity.this, SimplePieChartActivity.class));
+ }
+ });
+
+ Button startDynamicXYExButton = (Button)findViewById(R.id.startDynamicXYExButton);
+ startDynamicXYExButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ startActivity(new Intent(MainActivity.this, DynamicXYPlotActivity.class));
+ }
+ });
+
+ Button startSimpleXYExButton = (Button) findViewById(R.id.startSimpleXYExButton);
+ startSimpleXYExButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ startActivity(new Intent(MainActivity.this, SimpleXYPlotActivity.class));
+ }
+ });
+
+ Button startBarPlotExButton = (Button) findViewById(R.id.startBarPlotExButton);
+ startBarPlotExButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ startActivity(new Intent(MainActivity.this, BarPlotExampleActivity.class));
+ }
+ });
+
+ Button startOrSensorExButton = (Button) findViewById(R.id.startOrSensorExButton);
+ startOrSensorExButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ startActivity(new Intent(MainActivity.this, OrientationSensorExampleActivity.class));
+ }
+ });
+
+ Button startTimeSeriesExButon = (Button)findViewById(R.id.startTimeSeriesExButton);
+ startTimeSeriesExButon.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ startActivity(new Intent(MainActivity.this, TimeSeriesActivity.class));
+ }
+ });
+
+ Button startStepChartExButton = (Button)findViewById(R.id.startStepChartExButton);
+ startStepChartExButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ startActivity(new Intent(MainActivity.this, StepChartExampleActivity.class));
+ }
+ });
+
+ Button startScrollZoomExButton = (Button)findViewById(R.id.startScrollZoomButton);
+ startScrollZoomExButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ startActivity(new Intent(MainActivity.this, TouchZoomExampleActivity.class));
+ }
+ });
+
+ Button startXyRegionExampleButton = (Button)findViewById(R.id.startXyRegionExampleButton);
+ startXyRegionExampleButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ startActivity(new Intent(MainActivity.this, XYRegionExampleActivity.class));
+ }
+ });
+
+
+ Button listViewExButton = (Button)findViewById(R.id.startXyListViewExButton);
+ listViewExButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ startActivity(new Intent(MainActivity.this, ListViewActivity.class));
+ }
+ });
+
+ Button startDualScaleExampleButton = (Button)findViewById(R.id.startDualScaleExampleButton);
+ startDualScaleExampleButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ startActivity(new Intent(MainActivity.this, DualScaleXYPlotExampleActivity.class));
+ }
+ });
+
+ Button startXYPlotWithBgImgExampleButton = (Button)findViewById(R.id.startXYPlotWithBgImgExample);
+ startXYPlotWithBgImgExampleButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ startActivity(new Intent(MainActivity.this, XYPlotWithBgImgActivity.class));
+ }
+ });
+
+ }
+}
diff --git a/Examples/DemoApp/src/com/androidplot/demos/OrientationSensorExampleActivity.java b/Examples/DemoApp/src/com/androidplot/demos/OrientationSensorExampleActivity.java
new file mode 100644
index 0000000..23012bd
--- /dev/null
+++ b/Examples/DemoApp/src/com/androidplot/demos/OrientationSensorExampleActivity.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.demos;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Color;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import com.androidplot.Plot;
+import com.androidplot.util.PlotStatistics;
+import com.androidplot.util.Redrawer;
+import com.androidplot.xy.*;
+import java.text.DecimalFormat;
+import java.util.Arrays;
+
+// Monitor the phone's orientation sensor and plot the resulting azimuth pitch and roll values.
+// See: http://developer.android.com/reference/android/hardware/SensorEvent.html
+public class OrientationSensorExampleActivity extends Activity implements SensorEventListener
+{
+
+ private static final int HISTORY_SIZE = 300; // number of points to plot in history
+ private SensorManager sensorMgr = null;
+ private Sensor orSensor = null;
+
+ private XYPlot aprLevelsPlot = null;
+ private XYPlot aprHistoryPlot = null;
+
+ private CheckBox hwAcceleratedCb;
+ private CheckBox showFpsCb;
+ //private SimpleXYSeries aprLevelsSeries = null;
+ private SimpleXYSeries aLvlSeries;
+ private SimpleXYSeries pLvlSeries;
+ private SimpleXYSeries rLvlSeries;
+ private SimpleXYSeries azimuthHistorySeries = null;
+ private SimpleXYSeries pitchHistorySeries = null;
+ private SimpleXYSeries rollHistorySeries = null;
+
+ private Redrawer redrawer;
+
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.orientation_sensor_example);
+
+ // setup the APR Levels plot:
+ aprLevelsPlot = (XYPlot) findViewById(R.id.aprLevelsPlot);
+ aprLevelsPlot.setDomainBoundaries(-1, 1, BoundaryMode.FIXED);
+ aprLevelsPlot.getGraphWidget().getDomainLabelPaint().setColor(Color.TRANSPARENT);
+
+ aLvlSeries = new SimpleXYSeries("A");
+ pLvlSeries = new SimpleXYSeries("P");
+ rLvlSeries = new SimpleXYSeries("R");
+
+ aprLevelsPlot.addSeries(aLvlSeries,
+ new BarFormatter(Color.rgb(0, 200, 0), Color.rgb(0, 80, 0)));
+ aprLevelsPlot.addSeries(pLvlSeries,
+ new BarFormatter(Color.rgb(200, 0, 0), Color.rgb(0, 80, 0)));
+ aprLevelsPlot.addSeries(rLvlSeries,
+ new BarFormatter(Color.rgb(0, 0, 200), Color.rgb(0, 80, 0)));
+
+ aprLevelsPlot.setDomainStepValue(3);
+ aprLevelsPlot.setTicksPerRangeLabel(3);
+
+ // per the android documentation, the minimum and maximum readings we can get from
+ // any of the orientation sensors is -180 and 359 respectively so we will fix our plot's
+ // boundaries to those values. If we did not do this, the plot would auto-range which
+ // can be visually confusing in the case of dynamic plots.
+ aprLevelsPlot.setRangeBoundaries(-180, 359, BoundaryMode.FIXED);
+
+ // update our domain and range axis labels:
+ aprLevelsPlot.setDomainLabel("");
+ aprLevelsPlot.getDomainLabelWidget().pack();
+ aprLevelsPlot.setRangeLabel("Angle (Degs)");
+ aprLevelsPlot.getRangeLabelWidget().pack();
+ aprLevelsPlot.setGridPadding(15, 0, 15, 0);
+ aprLevelsPlot.setRangeValueFormat(new DecimalFormat("#"));
+
+ // setup the APR History plot:
+ aprHistoryPlot = (XYPlot) findViewById(R.id.aprHistoryPlot);
+
+ azimuthHistorySeries = new SimpleXYSeries("Az.");
+ azimuthHistorySeries.useImplicitXVals();
+ pitchHistorySeries = new SimpleXYSeries("Pitch");
+ pitchHistorySeries.useImplicitXVals();
+ rollHistorySeries = new SimpleXYSeries("Roll");
+ rollHistorySeries.useImplicitXVals();
+
+ aprHistoryPlot.setRangeBoundaries(-180, 359, BoundaryMode.FIXED);
+ aprHistoryPlot.setDomainBoundaries(0, HISTORY_SIZE, BoundaryMode.FIXED);
+ aprHistoryPlot.addSeries(azimuthHistorySeries,
+ new LineAndPointFormatter(
+ Color.rgb(100, 100, 200), null, null, null));
+ aprHistoryPlot.addSeries(pitchHistorySeries,
+ new LineAndPointFormatter(
+ Color.rgb(100, 200, 100), null, null, null));
+ aprHistoryPlot.addSeries(rollHistorySeries,
+ new LineAndPointFormatter(
+ Color.rgb(200, 100, 100), null, null, null));
+ aprHistoryPlot.setDomainStepMode(XYStepMode.INCREMENT_BY_VAL);
+ aprHistoryPlot.setDomainStepValue(HISTORY_SIZE/10);
+ aprHistoryPlot.setTicksPerRangeLabel(3);
+ aprHistoryPlot.setDomainLabel("Sample Index");
+ aprHistoryPlot.getDomainLabelWidget().pack();
+ aprHistoryPlot.setRangeLabel("Angle (Degs)");
+ aprHistoryPlot.getRangeLabelWidget().pack();
+
+ aprHistoryPlot.setRangeValueFormat(new DecimalFormat("#"));
+ aprHistoryPlot.setDomainValueFormat(new DecimalFormat("#"));
+
+ // setup checkboxes:
+ hwAcceleratedCb = (CheckBox) findViewById(R.id.hwAccelerationCb);
+ final PlotStatistics levelStats = new PlotStatistics(1000, false);
+ final PlotStatistics histStats = new PlotStatistics(1000, false);
+
+ aprLevelsPlot.addListener(levelStats);
+ aprHistoryPlot.addListener(histStats);
+ hwAcceleratedCb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
+ if(b) {
+ aprLevelsPlot.setLayerType(View.LAYER_TYPE_NONE, null);
+ aprHistoryPlot.setLayerType(View.LAYER_TYPE_NONE, null);
+ } else {
+ aprLevelsPlot.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
+ aprHistoryPlot.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
+ }
+ }
+ });
+
+ showFpsCb = (CheckBox) findViewById(R.id.showFpsCb);
+ showFpsCb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
+ levelStats.setAnnotatePlotEnabled(b);
+ histStats.setAnnotatePlotEnabled(b);
+ }
+ });
+
+ // get a ref to the BarRenderer so we can make some changes to it:
+ BarRenderer barRenderer = (BarRenderer) aprLevelsPlot.getRenderer(BarRenderer.class);
+ if(barRenderer != null) {
+ // make our bars a little thicker than the default so they can be seen better:
+ barRenderer.setBarWidth(25);
+ }
+
+ // register for orientation sensor events:
+ sensorMgr = (SensorManager) getApplicationContext().getSystemService(Context.SENSOR_SERVICE);
+ for (Sensor sensor : sensorMgr.getSensorList(Sensor.TYPE_ORIENTATION)) {
+ if (sensor.getType() == Sensor.TYPE_ORIENTATION) {
+ orSensor = sensor;
+ }
+ }
+
+ // if we can't access the orientation sensor then exit:
+ if (orSensor == null) {
+ System.out.println("Failed to attach to orSensor.");
+ cleanup();
+ }
+
+ sensorMgr.registerListener(this, orSensor, SensorManager.SENSOR_DELAY_UI);
+
+ redrawer = new Redrawer(
+ Arrays.asList(new Plot[]{aprHistoryPlot, aprLevelsPlot}),
+ 100, false);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ redrawer.start();
+ }
+
+ @Override
+ public void onPause() {
+ redrawer.pause();
+ super.onPause();
+ }
+
+ @Override
+ public void onDestroy() {
+ redrawer.finish();
+ super.onDestroy();
+ }
+
+ private void cleanup() {
+ // aunregister with the orientation sensor before exiting:
+ sensorMgr.unregisterListener(this);
+ finish();
+ }
+
+
+ // Called whenever a new orSensor reading is taken.
+ @Override
+ public synchronized void onSensorChanged(SensorEvent sensorEvent) {
+
+ // update level data:
+ aLvlSeries.setModel(Arrays.asList(
+ new Number[]{sensorEvent.values[0]}),
+ SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);
+
+ pLvlSeries.setModel(Arrays.asList(
+ new Number[]{sensorEvent.values[1]}),
+ SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);
+
+ rLvlSeries.setModel(Arrays.asList(
+ new Number[]{sensorEvent.values[2]}),
+ SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);
+
+ // get rid the oldest sample in history:
+ if (rollHistorySeries.size() > HISTORY_SIZE) {
+ rollHistorySeries.removeFirst();
+ pitchHistorySeries.removeFirst();
+ azimuthHistorySeries.removeFirst();
+ }
+
+ // add the latest history sample:
+ azimuthHistorySeries.addLast(null, sensorEvent.values[0]);
+ pitchHistorySeries.addLast(null, sensorEvent.values[1]);
+ rollHistorySeries.addLast(null, sensorEvent.values[2]);
+ }
+
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int i) {
+ // Not interested in this event
+ }
+}
\ No newline at end of file
diff --git a/Examples/DemoApp/src/com/androidplot/demos/SimplePieChartActivity.java b/Examples/DemoApp/src/com/androidplot/demos/SimplePieChartActivity.java
new file mode 100644
index 0000000..f90160b
--- /dev/null
+++ b/Examples/DemoApp/src/com/androidplot/demos/SimplePieChartActivity.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.demos;
+
+import android.app.Activity;
+import android.graphics.*;
+import android.os.Bundle;
+import android.widget.SeekBar;
+import android.widget.TextView;
+import com.androidplot.pie.PieChart;
+import com.androidplot.pie.PieRenderer;
+import com.androidplot.pie.Segment;
+import com.androidplot.pie.SegmentFormatter;
+
+/**
+ * The simplest possible example of using AndroidPlot to plot some data.
+ */
+public class SimplePieChartActivity extends Activity
+{
+
+ private TextView donutSizeTextView;
+ private SeekBar donutSizeSeekBar;
+
+ private PieChart pie;
+
+ private Segment s1;
+ private Segment s2;
+ private Segment s3;
+ private Segment s4;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState)
+ {
+
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.pie_chart);
+
+ // initialize our XYPlot reference:
+ pie = (PieChart) findViewById(R.id.mySimplePieChart);
+
+
+ donutSizeSeekBar = (SeekBar) findViewById(R.id.donutSizeSeekBar);
+
+ donutSizeSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int i, boolean b) {}
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {}
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ pie.getRenderer(PieRenderer.class).setDonutSize(seekBar.getProgress()/100f,
+ PieRenderer.DonutMode.PERCENT);
+ pie.redraw();
+ updateDonutText();
+ }
+ });
+
+ donutSizeTextView = (TextView) findViewById(R.id.donutSizeTextView);
+ updateDonutText();
+
+ s1 = new Segment("s1", 10);
+ s2 = new Segment("s2", 1);
+ s3 = new Segment("s3", 10);
+ s4 = new Segment("s4", 10);
+
+ EmbossMaskFilter emf = new EmbossMaskFilter(
+ new float[]{1, 1, 1}, 0.4f, 10, 8.2f);
+
+ SegmentFormatter sf1 = new SegmentFormatter();
+ sf1.configure(getApplicationContext(), R.xml.pie_segment_formatter1);
+
+ sf1.getFillPaint().setMaskFilter(emf);
+
+ SegmentFormatter sf2 = new SegmentFormatter();
+ sf2.configure(getApplicationContext(), R.xml.pie_segment_formatter2);
+
+ sf2.getFillPaint().setMaskFilter(emf);
+
+ SegmentFormatter sf3 = new SegmentFormatter();
+ sf3.configure(getApplicationContext(), R.xml.pie_segment_formatter3);
+
+ sf3.getFillPaint().setMaskFilter(emf);
+
+ SegmentFormatter sf4 = new SegmentFormatter();
+ sf4.configure(getApplicationContext(), R.xml.pie_segment_formatter4);
+
+ sf4.getFillPaint().setMaskFilter(emf);
+
+ pie.addSeries(s1, sf1);
+ pie.addSeries(s2, sf2);
+ pie.addSeries(s3, sf3);
+ pie.addSeries(s4, sf4);
+
+ pie.getBorderPaint().setColor(Color.TRANSPARENT);
+ pie.getBackgroundPaint().setColor(Color.TRANSPARENT);
+ }
+
+ protected void updateDonutText() {
+ donutSizeTextView.setText(donutSizeSeekBar.getProgress() + "%");
+ }
+}
diff --git a/Examples/DemoApp/src/com/androidplot/demos/SimpleXYPlotActivity.java b/Examples/DemoApp/src/com/androidplot/demos/SimpleXYPlotActivity.java
new file mode 100644
index 0000000..985e5ff
--- /dev/null
+++ b/Examples/DemoApp/src/com/androidplot/demos/SimpleXYPlotActivity.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.demos;
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.WindowManager;
+import com.androidplot.xy.SimpleXYSeries;
+import com.androidplot.xy.XYSeries;
+import com.androidplot.xy.*;
+import java.util.Arrays;
+
+/**
+ * A straightforward example of using AndroidPlot to plot some data.
+ */
+public class SimpleXYPlotActivity extends Activity
+{
+
+ private XYPlot plot;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+
+ // fun little snippet that prevents users from taking screenshots
+ // on ICS+ devices :-)
+ getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE,
+ WindowManager.LayoutParams.FLAG_SECURE);
+ setContentView(R.layout.simple_xy_plot_example);
+
+ // initialize our XYPlot reference:
+ plot = (XYPlot) findViewById(R.id.mySimpleXYPlot);
+
+ // Create a couple arrays of y-values to plot:
+ Number[] series1Numbers = {1, 8, 5, 2, 7, 4};
+ Number[] series2Numbers = {4, 6, 3, 8, 2, 10};
+
+ // Turn the above arrays into XYSeries':
+ XYSeries series1 = new SimpleXYSeries(
+ Arrays.asList(series1Numbers), // SimpleXYSeries takes a List so turn our array into a List
+ SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, // Y_VALS_ONLY means use the element index as the x value
+ "Series1"); // Set the display title of the series
+
+ // same as above
+ XYSeries series2 = new SimpleXYSeries(Arrays.asList(series2Numbers), SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "Series2");
+
+ // Create a formatter to use for drawing a series using LineAndPointRenderer
+ // and configure it from xml:
+ LineAndPointFormatter series1Format = new LineAndPointFormatter();
+ series1Format.setPointLabelFormatter(new PointLabelFormatter());
+ series1Format.configure(getApplicationContext(),
+ R.xml.line_point_formatter_with_plf1);
+
+ // add a new series' to the xyplot:
+ plot.addSeries(series1, series1Format);
+
+ // same as above:
+ LineAndPointFormatter series2Format = new LineAndPointFormatter();
+ series2Format.setPointLabelFormatter(new PointLabelFormatter());
+ series2Format.configure(getApplicationContext(),
+ R.xml.line_point_formatter_with_plf2);
+ plot.addSeries(series2, series2Format);
+
+ // reduce the number of range labels
+ plot.setTicksPerRangeLabel(3);
+ plot.getGraphWidget().setDomainLabelOrientation(-45);
+
+ }
+}
diff --git a/Examples/DemoApp/src/com/androidplot/demos/StepChartExampleActivity.java b/Examples/DemoApp/src/com/androidplot/demos/StepChartExampleActivity.java
new file mode 100644
index 0000000..660d03a
--- /dev/null
+++ b/Examples/DemoApp/src/com/androidplot/demos/StepChartExampleActivity.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.demos;
+
+
+import android.app.Activity;
+import android.graphics.*;
+import android.os.Bundle;
+import com.androidplot.xy.SimpleXYSeries;
+import com.androidplot.xy.XYSeries;
+import com.androidplot.xy.*;
+
+import java.text.DecimalFormat;
+import java.text.FieldPosition;
+import java.text.Format;
+import java.text.ParsePosition;
+import java.util.Arrays;
+
+public class StepChartExampleActivity extends Activity
+{
+
+ private XYPlot mySimpleXYPlot;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState)
+ {
+
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.step_chart_example);
+
+ // initialize our XYPlot reference:
+ mySimpleXYPlot = (XYPlot) findViewById(R.id.stepChartExamplePlot);
+
+ // y-vals to plot:
+ Number[] series1Numbers = {1, 2, 3, 4, 2, 3, 4, 2, 2, 2, 3, 4, 2, 3, 2, 2};
+ // create our series from our array of nums:
+ XYSeries series2 = new SimpleXYSeries(
+ Arrays.asList(series1Numbers),
+ SimpleXYSeries.ArrayFormat.Y_VALS_ONLY,
+ "Thread #1");
+
+ //mySimpleXYPlot.getLayoutManager().remove(mySimpleXYPlot.getLegendWidget());
+
+
+ mySimpleXYPlot.getGraphWidget().getGridBackgroundPaint().setColor(Color.WHITE);
+ mySimpleXYPlot.getGraphWidget().getDomainGridLinePaint().setColor(Color.BLACK);
+ mySimpleXYPlot.getGraphWidget().getDomainGridLinePaint().setPathEffect(new DashPathEffect(new float[]{1, 1}, 1));
+ mySimpleXYPlot.getGraphWidget().getRangeGridLinePaint().setColor(Color.BLACK);
+ mySimpleXYPlot.getGraphWidget().getRangeGridLinePaint().setPathEffect(new DashPathEffect(new float[]{1, 1}, 1));
+ mySimpleXYPlot.getGraphWidget().getDomainOriginLinePaint().setColor(Color.BLACK);
+ mySimpleXYPlot.getGraphWidget().getRangeOriginLinePaint().setColor(Color.BLACK);
+ mySimpleXYPlot.getGraphWidget().setMarginRight(5);
+
+ // Create a formatter to use for drawing a series using LineAndPointRenderer:
+ LineAndPointFormatter series1Format = new LineAndPointFormatter(
+ Color.rgb(0, 100, 0), // line color
+ Color.rgb(0, 100, 0), // point color
+ Color.rgb(100, 200, 0), null); // fill color
+
+
+ // setup our line fill paint to be a slightly transparent gradient:
+ Paint lineFill = new Paint();
+ lineFill.setAlpha(200);
+ lineFill.setShader(new LinearGradient(0, 0, 0, 250, Color.WHITE, Color.BLUE, Shader.TileMode.MIRROR));
+
+ StepFormatter stepFormatter = new StepFormatter(Color.rgb(0, 0,0), Color.BLUE);
+ stepFormatter.getLinePaint().setStrokeWidth(1);
+
+ stepFormatter.getLinePaint().setAntiAlias(false);
+ stepFormatter.setFillPaint(lineFill);
+ mySimpleXYPlot.addSeries(series2, stepFormatter);
+
+ // adjust the domain/range ticks to make more sense; label per tick for range and label per 5 ticks domain:
+ mySimpleXYPlot.setRangeStep(XYStepMode.INCREMENT_BY_VAL, 1);
+ mySimpleXYPlot.setDomainStep(XYStepMode.INCREMENT_BY_VAL, 1);
+ mySimpleXYPlot.setTicksPerRangeLabel(1);
+ mySimpleXYPlot.setTicksPerDomainLabel(5);
+
+ // customize our domain/range labels
+ //mySimpleXYPlot.setDomainLabel("Time (Secs)");
+ //mySimpleXYPlot.setRangeLabel("Server State");
+
+ //mySimpleXYPlot.getGraphWidget().getGridLinePaint().setAlpha(0);
+
+
+
+ // get rid of decimal points in our domain labels:
+ mySimpleXYPlot.setDomainValueFormat(new DecimalFormat("0"));
+
+ // create a custom formatter to draw our state names as range tick labels:
+ mySimpleXYPlot.setRangeValueFormat(new Format() {
+ @Override
+ public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
+ Number num = (Number) obj;
+ switch(num.intValue()) {
+ case 1:
+ toAppendTo.append("Init");
+ break;
+ case 2:
+ toAppendTo.append("Idle");
+ break;
+ case 3:
+ toAppendTo.append("Recv");
+ break;
+ case 4:
+ toAppendTo.append("Send");
+ break;
+ default:
+ toAppendTo.append("Unknown");
+ break;
+ }
+ return toAppendTo;
+ }
+
+ @Override
+ public Object parseObject(String source, ParsePosition pos) {
+ return null;
+ }
+ });
+
+ // by default, AndroidPlot displays developer guides to aid in laying out your plot.
+ // To get rid of them call disableAllMarkup():
+ //mySimpleXYPlot.disableAllMarkup();
+
+ }
+}
diff --git a/Examples/DemoApp/src/com/androidplot/demos/TimeSeriesActivity.java b/Examples/DemoApp/src/com/androidplot/demos/TimeSeriesActivity.java
new file mode 100644
index 0000000..0e7904c
--- /dev/null
+++ b/Examples/DemoApp/src/com/androidplot/demos/TimeSeriesActivity.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.demos;
+
+import android.app.Activity;
+import android.graphics.*;
+import android.os.Bundle;
+import com.androidplot.xy.SimpleXYSeries;
+import com.androidplot.xy.XYSeries;
+import com.androidplot.xy.*;
+
+import java.text.*;
+import java.util.Arrays;
+import java.util.Date;
+
+public class TimeSeriesActivity extends Activity
+{
+
+ private XYPlot plot1;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.time_series_example);
+
+ plot1 = (XYPlot) findViewById(R.id.plot1);
+ Number[] numSightings = {5, 8, 9, 2, 5};
+
+ // an array of years in milliseconds:
+ Number[] years = {
+ 978307200, // 2001
+ 1009843200, // 2002
+ 1041379200, // 2003
+ 1072915200, // 2004
+ 1104537600 // 2005
+ };
+ // create our series from our array of nums:
+ XYSeries series2 = new SimpleXYSeries(
+ Arrays.asList(years),
+ Arrays.asList(numSightings),
+ "Sightings in USA");
+
+ plot1.getGraphWidget().getGridBackgroundPaint().setColor(Color.WHITE);
+ plot1.getGraphWidget().getDomainGridLinePaint().setColor(Color.BLACK);
+ plot1.getGraphWidget().getDomainGridLinePaint().
+ setPathEffect(new DashPathEffect(new float[]{1, 1}, 1));
+ plot1.getGraphWidget().getRangeGridLinePaint().setColor(Color.BLACK);
+ plot1.getGraphWidget().getRangeGridLinePaint().
+ setPathEffect(new DashPathEffect(new float[]{1, 1}, 1));
+ plot1.getGraphWidget().getDomainOriginLinePaint().setColor(Color.BLACK);
+ plot1.getGraphWidget().getRangeOriginLinePaint().setColor(Color.BLACK);
+
+ // Create a formatter to use for drawing a series using LineAndPointRenderer:
+ LineAndPointFormatter series1Format = new LineAndPointFormatter(
+ Color.rgb(0, 100, 0), // line color
+ Color.rgb(0, 100, 0), // point color
+ Color.rgb(100, 200, 0), null); // fill color
+
+
+ // setup our line fill paint to be a slightly transparent gradient:
+ Paint lineFill = new Paint();
+ lineFill.setAlpha(200);
+
+ // ugly usage of LinearGradient. unfortunately there's no way to determine the actual size of
+ // a View from within onCreate. one alternative is to specify a dimension in resources
+ // and use that accordingly. at least then the values can be customized for the device type and orientation.
+ lineFill.setShader(new LinearGradient(0, 0, 200, 200, Color.WHITE, Color.GREEN, Shader.TileMode.CLAMP));
+
+ LineAndPointFormatter formatter =
+ new LineAndPointFormatter(Color.rgb(0, 0,0), Color.BLUE, Color.RED, null);
+ formatter.setFillPaint(lineFill);
+ plot1.getGraphWidget().setPaddingRight(2);
+ plot1.addSeries(series2, formatter);
+
+ // draw a domain tick for each year:
+ plot1.setDomainStep(XYStepMode.SUBDIVIDE, years.length);
+
+ // customize our domain/range labels
+ plot1.setDomainLabel("Year");
+ plot1.setRangeLabel("# of Sightings");
+
+ // get rid of decimal points in our range labels:
+ plot1.setRangeValueFormat(new DecimalFormat("0"));
+
+ plot1.setDomainValueFormat(new Format() {
+
+ // create a simple date format that draws on the year portion of our timestamp.
+ // see http://download.oracle.com/javase/1.4.2/docs/api/java/text/SimpleDateFormat.html
+ // for a full description of SimpleDateFormat.
+ private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy");
+
+ @Override
+ public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
+
+ // because our timestamps are in seconds and SimpleDateFormat expects milliseconds
+ // we multiply our timestamp by 1000:
+ long timestamp = ((Number) obj).longValue() * 1000;
+ Date date = new Date(timestamp);
+ return dateFormat.format(date, toAppendTo, pos);
+ }
+
+ @Override
+ public Object parseObject(String source, ParsePosition pos) {
+ return null;
+
+ }
+ });
+
+ // by default, AndroidPlot displays developer guides to aid in laying out your plot.
+ // To get rid of them call disableAllMarkup():
+ //plot1.disableAllMarkup();
+ }
+}
\ No newline at end of file
diff --git a/Examples/DemoApp/src/com/androidplot/demos/TouchZoomExampleActivity.java b/Examples/DemoApp/src/com/androidplot/demos/TouchZoomExampleActivity.java
new file mode 100644
index 0000000..65c5c71
--- /dev/null
+++ b/Examples/DemoApp/src/com/androidplot/demos/TouchZoomExampleActivity.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.demos;
+
+import java.text.DecimalFormat;
+import java.util.Random;
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.PointF;
+import android.os.Bundle;
+import android.util.FloatMath;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnTouchListener;
+import android.widget.Button;
+
+import com.androidplot.Plot;
+import com.androidplot.xy.BoundaryMode;
+import com.androidplot.xy.LineAndPointFormatter;
+import com.androidplot.xy.SimpleXYSeries;
+import com.androidplot.xy.XYPlot;
+
+/***********************************
+ * @author David Buezas (david.buezas at gmail.com)
+ * Feel free to copy, modify and use the source as it fits you.
+ * 09/27/2012 nfellows - updated for 0.5.1 and made a few simplifications
+ */
+public class TouchZoomExampleActivity extends Activity implements OnTouchListener {
+ private static final int SERIES_SIZE = 200;
+ private XYPlot mySimpleXYPlot;
+ private Button resetButton;
+ private SimpleXYSeries[] series = null;
+ private PointF minXY;
+ private PointF maxXY;
+
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.touch_zoom_example);
+ resetButton = (Button) findViewById(R.id.resetButton);
+ resetButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ minXY.x = series[0].getX(0).floatValue();
+ maxXY.x = series[3].getX(series[3].size() - 1).floatValue();
+ mySimpleXYPlot.setDomainBoundaries(minXY.x, maxXY.x, BoundaryMode.FIXED);
+
+ // pre 0.5.1 users should use postRedraw() instead.
+ mySimpleXYPlot.redraw();
+ }
+ });
+ mySimpleXYPlot = (XYPlot) findViewById(R.id.mySimpleXYPlot);
+ mySimpleXYPlot.setOnTouchListener(this);
+ mySimpleXYPlot.getGraphWidget().setTicksPerRangeLabel(2);
+ mySimpleXYPlot.getGraphWidget().setTicksPerDomainLabel(2);
+ mySimpleXYPlot.getGraphWidget().getBackgroundPaint().setColor(Color.TRANSPARENT);
+ mySimpleXYPlot.getGraphWidget().setRangeValueFormat(
+ new DecimalFormat("#####"));
+ mySimpleXYPlot.getGraphWidget().setDomainValueFormat(
+ new DecimalFormat("#####.#"));
+ mySimpleXYPlot.getGraphWidget().setRangeLabelWidth(25);
+ mySimpleXYPlot.setRangeLabel("");
+ mySimpleXYPlot.setDomainLabel("");
+
+ mySimpleXYPlot.setBorderStyle(Plot.BorderStyle.NONE, null, null);
+ //mySimpleXYPlot.disableAllMarkup();
+ series = new SimpleXYSeries[4];
+ int scale = 1;
+ for (int i = 0; i < 4; i++, scale *= 5) {
+ series[i] = new SimpleXYSeries("S" + i);
+ populateSeries(series[i], scale);
+ }
+ mySimpleXYPlot.addSeries(series[3],
+ new LineAndPointFormatter(Color.rgb(50, 0, 0), null,
+ Color.rgb(100, 0, 0), null));
+ mySimpleXYPlot.addSeries(series[2],
+ new LineAndPointFormatter(Color.rgb(50, 50, 0), null,
+ Color.rgb(100, 100, 0), null));
+ mySimpleXYPlot.addSeries(series[1],
+ new LineAndPointFormatter(Color.rgb(0, 50, 0), null,
+ Color.rgb(0, 100, 0), null));
+ mySimpleXYPlot.addSeries(series[0],
+ new LineAndPointFormatter(Color.rgb(0, 0, 0), null,
+ Color.rgb(0, 0, 150), null));
+ mySimpleXYPlot.redraw();
+ mySimpleXYPlot.calculateMinMaxVals();
+ minXY = new PointF(mySimpleXYPlot.getCalculatedMinX().floatValue(),
+ mySimpleXYPlot.getCalculatedMinY().floatValue());
+ maxXY = new PointF(mySimpleXYPlot.getCalculatedMaxX().floatValue(),
+ mySimpleXYPlot.getCalculatedMaxY().floatValue());
+ }
+
+ private void populateSeries(SimpleXYSeries series, int max) {
+ Random r = new Random();
+ for(int i = 0; i < SERIES_SIZE; i++) {
+ series.addLast(i, r.nextInt(max));
+ }
+ }
+
+ // Definition of the touch states
+ static final int NONE = 0;
+ static final int ONE_FINGER_DRAG = 1;
+ static final int TWO_FINGERS_DRAG = 2;
+ int mode = NONE;
+
+ PointF firstFinger;
+ float distBetweenFingers;
+ boolean stopThread = false;
+
+ @Override
+ public boolean onTouch(View arg0, MotionEvent event) {
+ switch (event.getAction() & MotionEvent.ACTION_MASK) {
+ case MotionEvent.ACTION_DOWN: // Start gesture
+ firstFinger = new PointF(event.getX(), event.getY());
+ mode = ONE_FINGER_DRAG;
+ stopThread = true;
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_POINTER_UP:
+ mode = NONE;
+ break;
+ case MotionEvent.ACTION_POINTER_DOWN: // second finger
+ distBetweenFingers = spacing(event);
+ // the distance check is done to avoid false alarms
+ if (distBetweenFingers > 5f) {
+ mode = TWO_FINGERS_DRAG;
+ }
+ break;
+ case MotionEvent.ACTION_MOVE:
+ if (mode == ONE_FINGER_DRAG) {
+ PointF oldFirstFinger = firstFinger;
+ firstFinger = new PointF(event.getX(), event.getY());
+ scroll(oldFirstFinger.x - firstFinger.x);
+ mySimpleXYPlot.setDomainBoundaries(minXY.x, maxXY.x,
+ BoundaryMode.FIXED);
+ mySimpleXYPlot.redraw();
+
+ } else if (mode == TWO_FINGERS_DRAG) {
+ float oldDist = distBetweenFingers;
+ distBetweenFingers = spacing(event);
+ zoom(oldDist / distBetweenFingers);
+ mySimpleXYPlot.setDomainBoundaries(minXY.x, maxXY.x,
+ BoundaryMode.FIXED);
+ mySimpleXYPlot.redraw();
+ }
+ break;
+ }
+ return true;
+ }
+
+ private void zoom(float scale) {
+ float domainSpan = maxXY.x - minXY.x;
+ float domainMidPoint = maxXY.x - domainSpan / 2.0f;
+ float offset = domainSpan * scale / 2.0f;
+
+ minXY.x = domainMidPoint - offset;
+ maxXY.x = domainMidPoint + offset;
+
+ minXY.x = Math.min(minXY.x, series[3].getX(series[3].size() - 3)
+ .floatValue());
+ maxXY.x = Math.max(maxXY.x, series[0].getX(1).floatValue());
+ clampToDomainBounds(domainSpan);
+ }
+
+ private void scroll(float pan) {
+ float domainSpan = maxXY.x - minXY.x;
+ float step = domainSpan / mySimpleXYPlot.getWidth();
+ float offset = pan * step;
+ minXY.x = minXY.x + offset;
+ maxXY.x = maxXY.x + offset;
+ clampToDomainBounds(domainSpan);
+ }
+
+ private void clampToDomainBounds(float domainSpan) {
+ float leftBoundary = series[0].getX(0).floatValue();
+ float rightBoundary = series[3].getX(series[3].size() - 1).floatValue();
+ // enforce left scroll boundary:
+ if (minXY.x < leftBoundary) {
+ minXY.x = leftBoundary;
+ maxXY.x = leftBoundary + domainSpan;
+ } else if (maxXY.x > series[3].getX(series[3].size() - 1).floatValue()) {
+ maxXY.x = rightBoundary;
+ minXY.x = rightBoundary - domainSpan;
+ }
+ }
+
+ private float spacing(MotionEvent event) {
+ float x = event.getX(0) - event.getX(1);
+ float y = event.getY(0) - event.getY(1);
+ return FloatMath.sqrt(x * x + y * y);
+ }
+}
+
diff --git a/Examples/DemoApp/src/com/androidplot/demos/XYPlotWithBgImgActivity.java b/Examples/DemoApp/src/com/androidplot/demos/XYPlotWithBgImgActivity.java
new file mode 100644
index 0000000..2f9de9c
--- /dev/null
+++ b/Examples/DemoApp/src/com/androidplot/demos/XYPlotWithBgImgActivity.java
@@ -0,0 +1,131 @@
+package com.androidplot.demos;
+
+import android.app.Activity;
+import android.graphics.*;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.ToggleButton;
+import com.androidplot.xy.*;
+
+import java.text.DecimalFormat;
+import java.util.Arrays;
+
+public class XYPlotWithBgImgActivity extends Activity {
+ private static final String TAG = XYPlotWithBgImgActivity.class.getName();
+
+ private int SERIES_LEN = 50;
+ private Shader WHITE_SHADER = new LinearGradient(1, 1, 1, 1, Color.WHITE, Color.WHITE, Shader.TileMode.REPEAT);
+
+ private XYPlot plot;
+ private SimpleXYSeries series;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.xy_plot_with_bq_img_example);
+
+ plot = (XYPlot) findViewById(R.id.graph_metrics);
+
+ //For debugging.
+ //plot.setMarkupEnabled(true);
+
+ // Format Graph
+ plot.getGraphWidget().getBackgroundPaint().setColor(Color.TRANSPARENT);
+ plot.getGraphWidget().getGridBackgroundPaint().setShader(WHITE_SHADER);
+ plot.getGraphWidget().getDomainGridLinePaint().setColor(Color.BLACK);
+ plot.getGraphWidget().getDomainGridLinePaint().setPathEffect(new DashPathEffect(new float[]{3, 3}, 1));
+ plot.getGraphWidget().getRangeGridLinePaint().setColor(Color.BLACK);
+ plot.getGraphWidget().getRangeGridLinePaint().setPathEffect(new DashPathEffect(new float[]{3, 3}, 1));
+ plot.getGraphWidget().getDomainOriginLinePaint().setColor(Color.BLACK);
+ plot.getGraphWidget().getRangeOriginLinePaint().setColor(Color.BLACK);
+ //plot.getGraphWidget().setMarginTop(10);
+
+ // Customize domain and range labels.
+ plot.setDomainLabel("x-vals");
+ plot.setRangeLabel("y-vals");
+ plot.setRangeValueFormat(new DecimalFormat("0"));
+
+ // Make the domain and range step correctly
+ plot.setRangeBoundaries(40, 160, BoundaryMode.FIXED);
+ plot.setRangeStep(XYStepMode.INCREMENT_BY_VAL, 20);
+ plot.setDomainStep(XYStepMode.INCREMENT_BY_VAL, 60);
+ plot.setTicksPerDomainLabel(2);
+
+ series = (SimpleXYSeries) getSeries();
+ LineAndPointFormatter lpFormat = new LineAndPointFormatter(
+ Color.BLACK,
+ Color.BLACK,
+ null, // No fill
+ new PointLabelFormatter(Color.TRANSPARENT) // Don't show text at points
+ );
+ plot.addSeries(series, lpFormat);
+ plot.redraw();
+ }
+
+ private SimpleXYSeries getSeries() {
+ Integer[] xVals = new Integer[SERIES_LEN];
+ Integer[] yVals = new Integer[SERIES_LEN];
+
+ xVals[0] = 0;
+ yVals[0] = 0;
+
+ for (int i = 1; i < SERIES_LEN; i += 1){
+ xVals[i] = xVals[i-1] + (int)(Math.random() * i);
+ yVals[i] = (int)(Math.random() * 140);
+ }
+
+ return new SimpleXYSeries(
+ Arrays.asList(xVals),
+ Arrays.asList(yVals),
+ "Sample Series");
+ }
+
+ public void onGraphStyleToggle(View v) {
+ boolean styleOn = ((ToggleButton) v).isChecked();
+
+ /*RectF graphRect = plot.getGraphWidget().getGridRect();
+ float segmentSize = 1.0f/6.0f;
+ LinearGradient lg = new LinearGradient(
+ 0,
+ graphRect.top,
+ 0,
+ graphRect.bottom,
+ new int[]{
+ Color.RED,
+ Color.YELLOW,
+ Color.GREEN,
+ Color.WHITE},
+ new float[]{
+ 0,
+ segmentSize*2,
+ segmentSize*3,
+ segmentSize*5
+ },
+ Shader.TileMode.REPEAT
+ );
+ plot.getGraphWidget().getGridBackgroundPaint().setShader(lg);*/
+
+ RectF rect = plot.getGraphWidget().getGridRect();
+ BitmapShader myShader = new BitmapShader(
+ Bitmap.createScaledBitmap(
+ BitmapFactory.decodeResource(
+ getResources(),
+ R.drawable.graph_background),
+ 1,
+ (int) rect.height(),
+ false),
+ Shader.TileMode.REPEAT,
+ Shader.TileMode.REPEAT);
+ Matrix m = new Matrix();
+ m.setTranslate(rect.left, rect.top);
+ myShader.setLocalMatrix(m);
+ if (styleOn)
+ plot.getGraphWidget().getGridBackgroundPaint().setShader(
+ myShader);
+ else
+ plot.getGraphWidget().getGridBackgroundPaint().setShader(WHITE_SHADER);
+
+ plot.redraw();
+
+ }
+}
diff --git a/Examples/DemoApp/src/com/androidplot/demos/XYRegionExampleActivity.java b/Examples/DemoApp/src/com/androidplot/demos/XYRegionExampleActivity.java
new file mode 100644
index 0000000..67e1b1b
--- /dev/null
+++ b/Examples/DemoApp/src/com/androidplot/demos/XYRegionExampleActivity.java
@@ -0,0 +1,433 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.demos;
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.graphics.DashPathEffect;
+import android.graphics.Paint;
+import android.os.Bundle;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import com.androidplot.util.PixelUtils;
+import com.androidplot.xy.XYSeries;
+import com.androidplot.ui.*;
+import com.androidplot.xy.*;
+
+import java.text.DecimalFormat;
+import java.text.FieldPosition;
+import java.text.NumberFormat;
+import java.text.ParsePosition;
+import java.util.Arrays;
+
+/**
+ * Demonstration of the usage of Marker and RectRegion.
+ */
+public class XYRegionExampleActivity extends Activity {
+
+ private static final float HOME_RUN_DIST = 325;
+ private static final int LINE_THICKNESS_DP = 2;
+ private static final int POINT_SIZE_DP = 6;
+ private XYPlot plot;
+ private final Number[] timHits = {105, 252, 220, 350, 12, 250, 353};
+ private final Number[] nickHits = {110, 191, 61, 371, 289, 101, 10};
+ private final Number[] joeHits = {25, 375, 364, 128, 178, 289, 346};
+ private final Number[] jamesHits = {250, 285, 295, 211, 311, 365, 241};
+ private LineAndPointFormatter timFormatter;
+ private LineAndPointFormatter nickFormatter;
+ private LineAndPointFormatter joeFormatter;
+ private LineAndPointFormatter jamesFormatter;
+
+ private XYSeries timSeries;
+ private XYSeries nickSeries;
+ private XYSeries joeSeries;
+ private XYSeries jamesSeries;
+
+ private RectRegion shortRegion;
+ private RectRegion warmupRegion;
+ private RectRegion homeRunRegion;
+
+ //private XYRegionFormatter rf1;
+ private XYRegionFormatter shortRegionFormatter;
+ private XYRegionFormatter warmupRegionFormatter;
+ private XYRegionFormatter homeRunRegionFormatter;
+ //private XYRegionFormatter rf5;
+
+ private CheckBox timCB;
+ private CheckBox nickCB;
+ private CheckBox joeCB;
+ private CheckBox jamesCB;
+
+ private CheckBox r2CheckBox;
+ private CheckBox r3CheckBox;
+ private CheckBox r4CheckBox;
+
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.xyregion_example);
+ plot = (XYPlot) findViewById(R.id.xyRegionExamplePlot);
+ timCB = (CheckBox) findViewById(R.id.s1CheckBox);
+ timCB.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
+ onS1CheckBoxClicked();
+ }
+ });
+
+ nickCB = (CheckBox) findViewById(R.id.s2CheckBox);
+ nickCB.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
+ onS2CheckBoxClicked();
+ }
+ });
+
+ joeCB = (CheckBox) findViewById(R.id.s3CheckBox);
+ joeCB.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
+ onS3CheckBoxClicked();
+ }
+ });
+
+ jamesCB = (CheckBox) findViewById(R.id.s4CheckBox);
+ jamesCB.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
+ onS4CheckBoxClicked();
+ }
+ });
+
+
+
+ r2CheckBox = (CheckBox) findViewById(R.id.r2CheckBox);
+ r2CheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
+ onCheckBoxClicked(r2CheckBox, timFormatter, shortRegionFormatter, shortRegion);
+ }
+ });
+
+ r3CheckBox = (CheckBox) findViewById(R.id.r3CheckBox);
+ r3CheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
+ onCheckBoxClicked(r3CheckBox, nickFormatter, warmupRegionFormatter, warmupRegion);
+ }
+ });
+
+ r4CheckBox = (CheckBox) findViewById(R.id.r4CheckBox);
+ r4CheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
+ onCheckBoxClicked(r4CheckBox, nickFormatter, homeRunRegionFormatter, homeRunRegion);
+ }
+ });
+
+ seriesSetup();
+ markerSetup();
+ axisLabelSetup();
+ regionSetup();
+ makePlotPretty();
+ }
+
+ private void onS1CheckBoxClicked() {
+ if(timCB.isChecked()) {
+ plot.addSeries(timSeries, timFormatter);
+ r2CheckBox.setEnabled(true);
+ } else {
+ plot.removeSeries(timSeries);
+ r2CheckBox.setEnabled(false);
+ }
+ plot.redraw();
+ }
+
+ private void onS2CheckBoxClicked() {
+ if(nickCB.isChecked()) {
+ plot.addSeries(nickSeries, nickFormatter);
+ r3CheckBox.setEnabled(true);
+ r4CheckBox.setEnabled(true);
+ } else {
+ plot.removeSeries(nickSeries);
+ r3CheckBox.setEnabled(false);
+ r4CheckBox.setEnabled(false);
+ }
+ plot.redraw();
+ }
+
+ private void onS3CheckBoxClicked() {
+ if(joeCB.isChecked()) {
+ plot.addSeries(joeSeries, joeFormatter);
+ } else {
+ plot.removeSeries(joeSeries);
+ }
+ plot.redraw();
+ }
+
+ private void onS4CheckBoxClicked() {
+ if(jamesCB.isChecked()) {
+ plot.addSeries(jamesSeries, jamesFormatter);
+ } else {
+ plot.removeSeries(jamesSeries);
+ }
+ plot.redraw();
+ }
+
+ /**
+ * Processes a check box event
+ * @param cb The checkbox event origin
+ * @param lpf LineAndPointFormatter with which rr and rf are to be added/removed
+ * @param rf The XYRegionFormatter with which rr should be rendered
+ * @param rr The RectRegion to add/remove
+ */
+ private void onCheckBoxClicked(CheckBox cb, LineAndPointFormatter lpf,
+ XYRegionFormatter rf, RectRegion rr) {
+ if(cb.isChecked()) {
+ lpf.removeRegion(rr);
+ } else {
+ lpf.addRegion(rr, rf);
+ }
+ }
+
+ /**
+ * Cleans up the plot's general layout and color scheme
+ */
+ private void makePlotPretty() {
+ // use a 2x5 grid with room for 10 items:
+ plot.getLegendWidget().setTableModel(new DynamicTableModel(4, 2));
+
+ // add a semi-transparent black background to the legend
+ // so it's easier to see overlaid on top of our plot:
+ Paint bgPaint = new Paint();
+ bgPaint.setColor(Color.BLACK);
+ bgPaint.setStyle(Paint.Style.FILL);
+ bgPaint.setAlpha(40);
+
+ plot.getLegendWidget().setBackgroundPaint(bgPaint);
+
+ // adjust the padding of the legend widget to look a little nicer:
+ plot.getLegendWidget().setPadding(5, 5, 5, 5);
+
+ plot.setRangeValueFormat(new NumberFormat() {
+ @Override
+ public StringBuffer format(double value, StringBuffer buffer, FieldPosition field) {
+ return new StringBuffer(value + "'");
+ }
+
+ @Override
+ public StringBuffer format(long value, StringBuffer buffer, FieldPosition field) {
+ throw new UnsupportedOperationException("Not yet implemented.");
+ }
+
+ @Override
+ public Number parse(String string, ParsePosition position) {
+ throw new UnsupportedOperationException("Not yet implemented.");
+ }
+ });
+
+ plot.setDomainValueFormat(new DecimalFormat("#"));
+
+ plot.getLegendWidget().setWidth(PixelUtils.dpToPix(100), SizeLayoutType.FILL);
+
+
+ // adjust the legend size so there is enough room
+ // to draw the new legend grid:
+ //plot.getLegendWidget().getHeightMetric().setLayoutType(SizeLayoutType.ABSOLUTE);
+ //plot.getLegendWidget().getWidthMetric().setLayoutType(SizeLayoutType.ABSOLUTE);
+ //plot.getLegendWidget().setSize(
+ // new SizeMetrics(70, SizeLayoutType.ABSOLUTE, 80, SizeLayoutType.ABSOLUTE));
+
+ // reposition the grid so that it rests above the bottom-left
+ // edge of the graph widget:
+
+ plot.getLegendWidget().position(
+ 125,
+ XLayoutStyle.ABSOLUTE_FROM_LEFT,
+ 65,
+ YLayoutStyle.ABSOLUTE_FROM_TOP,
+ AnchorPosition.LEFT_TOP);
+
+ plot.getGraphWidget().setRangeLabelHorizontalOffset(-1);
+
+ // add enough space to ensure range value labels arent cut off on the left/right:
+ plot.getGraphWidget().setRangeLabelWidth(25);
+
+ // add enough space to make sure domain value labels arent cut off on the bottom:
+ plot.getGraphWidget().setDomainLabelWidth(15);
+
+ plot.getGraphWidget().setDomainLabelVerticalOffset(-6);
+
+ plot.setRangeBoundaries(0, BoundaryMode.FIXED, 500, BoundaryMode.FIXED);
+ }
+
+ /**
+ * Create 4 XYSeries from the values defined above add add them to the plot.
+ * Also add some arbitrary regions.
+ */
+ private void seriesSetup() {
+
+
+ // TIM
+ timFormatter = new LineAndPointFormatter(
+ Color.rgb(100, 25, 20),
+ Color.rgb(100, 25, 20),
+ null, null);
+ timFormatter.getLinePaint().setStrokeWidth(PixelUtils.dpToPix(LINE_THICKNESS_DP));
+ timFormatter.getVertexPaint().setStrokeWidth(PixelUtils.dpToPix(POINT_SIZE_DP));
+
+ timSeries = new SimpleXYSeries(Arrays.asList(timHits),
+ SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "Tim");
+
+ plot.addSeries(timSeries, timFormatter);
+
+ // SERIES #2:
+ nickFormatter = new LineAndPointFormatter(
+ Color.rgb(100, 25, 200),
+ Color.rgb(100, 25, 200),
+ null, null);
+ nickFormatter.getLinePaint().setStrokeWidth(PixelUtils.dpToPix(LINE_THICKNESS_DP));
+ nickFormatter.getVertexPaint().setStrokeWidth(PixelUtils.dpToPix(POINT_SIZE_DP));
+
+
+
+ nickSeries = new SimpleXYSeries(Arrays.asList(nickHits),
+ SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "Nick");
+
+ plot.addSeries(nickSeries, nickFormatter);
+
+ // SERIES #3:
+ joeFormatter = new LineAndPointFormatter(
+ Color.rgb(200, 25, 200),
+ Color.rgb(200, 25, 200),
+ null, null);
+ joeFormatter.getLinePaint().setStrokeWidth(PixelUtils.dpToPix(LINE_THICKNESS_DP));
+ joeFormatter.getVertexPaint().setStrokeWidth(PixelUtils.dpToPix(POINT_SIZE_DP));
+
+ joeSeries = new SimpleXYSeries(Arrays.asList(joeHits),
+ SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "Joe");
+
+ plot.addSeries(joeSeries, joeFormatter);
+
+ // SERIES #4:
+ jamesFormatter = new LineAndPointFormatter(
+ Color.rgb(220, 25, 20),
+ Color.rgb(220, 25, 20),
+ null, null);
+
+ jamesFormatter.getLinePaint().setStrokeWidth(PixelUtils.dpToPix(LINE_THICKNESS_DP));
+ jamesFormatter.getVertexPaint().setStrokeWidth(PixelUtils.dpToPix(POINT_SIZE_DP));
+
+ jamesSeries = new SimpleXYSeries(Arrays.asList(jamesHits),
+ SimpleXYSeries.ArrayFormat.Y_VALS_ONLY,"James");
+ plot.addSeries(jamesSeries, jamesFormatter);
+
+ plot.setRangeStep(XYStepMode.INCREMENT_BY_VAL, 100);
+ //plot.setTicksPerRangeLabel(1);
+ plot.setDomainStep(XYStepMode.INCREMENT_BY_VAL, 1);
+ }
+
+ /**
+ * Add some color coded regions to our axis labels.
+ */
+ private void axisLabelSetup() {
+ // DOMAIN
+ plot.getGraphWidget().addDomainAxisValueLabelRegion(
+ Double.NEGATIVE_INFINITY, 2, new AxisValueLabelFormatter(Color.GRAY));
+ plot.getGraphWidget().addDomainAxisValueLabelRegion(
+ 2, Double.POSITIVE_INFINITY, new AxisValueLabelFormatter(Color.WHITE));
+ // RANGE
+ plot.getGraphWidget().addRangeAxisValueLabelRegion(
+ Double.NEGATIVE_INFINITY, HOME_RUN_DIST, new AxisValueLabelFormatter(Color.RED));
+ plot.getGraphWidget().addRangeAxisValueLabelRegion(
+ HOME_RUN_DIST, Double.POSITIVE_INFINITY, new AxisValueLabelFormatter(Color.GREEN));
+ }
+
+ /**
+ * Add some markers to our plot.
+ */
+ private void markerSetup() {
+
+ YValueMarker fenwayLfMarker = new YValueMarker(
+ 380, // y-val to mark
+ "Fenway Park LF Wall", // marker label
+ new XPositionMetric( // object instance to set text positioning on the marker
+ PixelUtils.dpToPix(5), // 5dp offset
+ XLayoutStyle.ABSOLUTE_FROM_RIGHT), // offset origin
+ Color.BLUE, // line paint color
+ Color.BLUE); // text paint color
+
+ YValueMarker attRfMarker = new YValueMarker(
+ 309, // y-val to mark
+ "ATT Park RF Wall", // marker label
+ new XPositionMetric( // object instance to set text positioning on the marker
+ PixelUtils.dpToPix(5), // 5dp offset
+ XLayoutStyle.ABSOLUTE_FROM_RIGHT), // offset origin
+ Color.CYAN, // line paint color
+ Color.CYAN); // text paint color
+
+
+ fenwayLfMarker.getTextPaint().setTextSize(PixelUtils.dpToPix(14));
+ attRfMarker.getTextPaint().setTextSize(PixelUtils.dpToPix(14));
+
+ DashPathEffect dpe = new DashPathEffect(
+ new float[]{PixelUtils.dpToPix(2), PixelUtils.dpToPix(2)}, 0);
+
+ fenwayLfMarker.getLinePaint().setPathEffect(dpe);
+ attRfMarker.getLinePaint().setPathEffect(dpe);
+
+ plot.addMarker(fenwayLfMarker);
+ plot.addMarker(attRfMarker);
+ }
+
+ /**
+ * Add some fill regions to our series data
+ */
+ private void regionSetup() {
+
+
+ // and another region:
+ shortRegionFormatter = new XYRegionFormatter(Color.RED);
+ shortRegionFormatter.getPaint().setAlpha(75);
+ shortRegion = new RectRegion(2, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, HOME_RUN_DIST, "Short");
+ timFormatter.addRegion(shortRegion, shortRegionFormatter);
+ nickFormatter.addRegion(shortRegion, shortRegionFormatter);
+ joeFormatter.addRegion(shortRegion, shortRegionFormatter);
+ jamesFormatter.addRegion(shortRegion, shortRegionFormatter);
+
+ // the next three regions are horizontal regions with minY/maxY
+ // set to negative and positive infinity respectively.
+ warmupRegionFormatter = new XYRegionFormatter(Color.WHITE);
+ warmupRegionFormatter.getPaint().setAlpha(75);
+
+ warmupRegion = new RectRegion(0, 2, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, "Warmup");
+ timFormatter.addRegion(warmupRegion, warmupRegionFormatter);
+ nickFormatter.addRegion(warmupRegion, warmupRegionFormatter);
+ joeFormatter.addRegion(warmupRegion, warmupRegionFormatter);
+ jamesFormatter.addRegion(warmupRegion, warmupRegionFormatter);
+
+ homeRunRegionFormatter = new XYRegionFormatter(Color.GREEN);
+ homeRunRegionFormatter.getPaint().setAlpha(75);
+
+ homeRunRegion = new RectRegion(2, Double.POSITIVE_INFINITY, HOME_RUN_DIST, Double.POSITIVE_INFINITY, "H. Run");
+ timFormatter.addRegion(homeRunRegion, homeRunRegionFormatter);
+ nickFormatter.addRegion(homeRunRegion, homeRunRegionFormatter);
+ joeFormatter.addRegion(homeRunRegion, homeRunRegionFormatter);
+ jamesFormatter.addRegion(homeRunRegion, homeRunRegionFormatter);
+
+ nickFormatter.setFillDirection(FillDirection.RANGE_ORIGIN);
+ }
+}
\ No newline at end of file
diff --git a/Examples/DemoApp/src/com/androidplot/demos/widget/DemoAppWidgetProvider.java b/Examples/DemoApp/src/com/androidplot/demos/widget/DemoAppWidgetProvider.java
new file mode 100644
index 0000000..f44a277
--- /dev/null
+++ b/Examples/DemoApp/src/com/androidplot/demos/widget/DemoAppWidgetProvider.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.androidplot.demos.widget;
+
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProvider;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.widget.RemoteViews;
+import com.androidplot.demos.R;
+import com.androidplot.xy.XYSeries;
+import com.androidplot.xy.LineAndPointFormatter;
+import com.androidplot.xy.SimpleXYSeries;
+import com.androidplot.xy.XYPlot;
+
+import java.util.Arrays;
+
+public class DemoAppWidgetProvider extends AppWidgetProvider {
+
+ @Override
+ public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
+ for (int widgetId : appWidgetIds) {
+ XYPlot plot = new XYPlot(context, "Widget Example");
+ //plot.getLayoutParams().height = 100;
+ //plot.getLayoutParams().width = 100;
+ plot.measure(150,150);
+ plot.layout(0,0,150,150);
+ plot.setDrawingCacheEnabled(true);
+
+ // Create a couple arrays of y-values to plot:
+ Number[] series1Numbers = {1, 8, 5, 2, 7, 4};
+ Number[] series2Numbers = {4, 6, 3, 8, 2, 10};
+
+ // Turn the above arrays into XYSeries':
+ XYSeries series1 = new SimpleXYSeries(
+ Arrays.asList(series1Numbers), // SimpleXYSeries takes a List so turn our array into a List
+ SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, // Y_VALS_ONLY means use the element index as the x value
+ "Series1"); // Set the display title of the series
+
+ // same as above
+ XYSeries series2 = new SimpleXYSeries(Arrays.asList(series2Numbers), SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "Series2");
+
+ // Create a formatter to use for drawing a series using LineAndPointRenderer:
+ LineAndPointFormatter series1Format = new LineAndPointFormatter(
+ Color.rgb(0, 200, 0), // line color
+ Color.rgb(0, 100, 0), // point color
+ null, null); // fill color (none)
+
+ // add a new series' to the xyplot:
+ plot.addSeries(series1, series1Format);
+
+ // same as above:
+ plot.addSeries(series2,
+ new LineAndPointFormatter(
+ Color.rgb(0, 0, 200), Color.rgb(0, 0, 100), null, null));
+
+
+ // reduce the number of range labels
+ plot.setTicksPerRangeLabel(3);
+
+ // by default, AndroidPlot displays developer guides to aid in laying out your plot.
+ // To get rid of them call disableAllMarkup():
+ //plot.disableAllMarkup();
+
+ Bitmap bmp = plot.getDrawingCache();
+
+ RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.demo_app_widget);
+ rv.setBitmap(R.id.imgView, "setImageBitmap", bmp);
+ appWidgetManager.updateAppWidget(widgetId, rv);
+ }
+ }
+}
diff --git a/Examples/pom.xml b/Examples/pom.xml
new file mode 100644
index 0000000..c706493
--- /dev/null
+++ b/Examples/pom.xml
@@ -0,0 +1,150 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2012 AndroidPlot.com
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>androidplot</artifactId>
+ <groupId>com.androidplot</groupId>
+ <version>0.6.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>androidplot-examples</artifactId>
+ <name>AndroidPlot-Examples</name>
+ <packaging>pom</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.google.android</groupId>
+ <artifactId>android</artifactId>
+ </dependency>
+ <!--<dependency>
+ <groupId>com.google.android</groupId>
+ <artifactId>android</artifactId>
+ <version>2.1_r1</version>
+ <scope>provided</scope>
+ </dependency>-->
+ <!-- TODO: figure out how to use this dependency without breaking the clean target;
+ used as-is, the clean target can fail because the dependency is erased by maven.
+ <dependency>
+ <groupId>com.androidplot</groupId>
+ <artifactId>androidplot-core</artifactId>
+ <version>${applicationVersion}</version>
+ </dependency>
+ -->
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <version>1.6</version>
+ <executions>
+ <execution>
+ <id>compile</id>
+ <phase>compile</phase>
+ <goals>
+ <goal>run</goal>
+ </goals>
+
+ <configuration>
+ <target name="build-examples">
+ <!--
+ <property name="key.store" value="test"/>
+ <property name="key.alias" value="test"/>
+ -->
+ <!--<property name="sdk.dir" value="C:/android-sdk"/>-->
+ <property environment="env"/>
+ <property name="sdk.dir" value="${env.ANDROID_HOME}"/>
+ <!-- todo: include only the release jar, not everything inside
+ the artifacts dir.-->
+ <!--<property name="jar.libs.dir" value="../../AndroidPlot-Core/target/obfuscated/"/>-->
+ <property name="build.compiler" value="extJavac"/>
+ <property environment="env"/>
+ <property name="sdk.dir" value="${env.ANDROID_HOME}"/>
+ <!--<property name="apkeystore" value="${env.AP_KEYSTORE}"/>-->
+
+ <!-- DemoApp-->
+ <ant antfile="build.xml" target="debug" dir="DemoApp"/>
+
+ <!-- DynamicXYPlotExample-->
+ <ant antfile="build.xml" target="debug" dir="DynamicXYPlotExample"/>
+
+ <!-- HelperExample-->
+ <ant antfile="build.xml" target="debug" dir="HelperExample"/>
+
+ <!-- OrientationSensorExample-->
+ <ant antfile="build.xml" target="debug" dir="OrientationSensorExample"/>
+
+ <!-- SimpleXYPlotExample-->
+ <ant antfile="build.xml" target="debug" dir="SimpleXYPlotExample"/>
+
+ <!-- StepChartExample-->
+ <ant antfile="build.xml" target="debug" dir="StepChartExample"/>
+
+ <!-- TimedXYPlotExample
+ <ant antfile="build.xml" target="debug" dir="TimedXYPlotExample"/>
+ -->
+ </target>
+
+ </configuration>
+
+ </execution>
+ <execution>
+ <id>clean</id>
+ <phase>clean</phase>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ <configuration>
+ <tasks>
+ <property environment="env"/>
+ <property name="sdk.dir" value="${env.ANDROID_HOME}"/>
+
+ <!-- DemoApp-->
+ <ant antfile="build.xml" target="clean" dir="DemoApp"/>
+
+ <!-- DynamicXYPlotExample-->
+ <ant antfile="build.xml" target="clean" dir="DynamicXYPlotExample"/>
+
+ <!-- HelperExample-->
+ <ant antfile="build.xml" target="clean" dir="HelperExample"/>
+
+ <!-- OrientationSensorExample-->
+ <ant antfile="build.xml" target="clean" dir="OrientationSensorExample"/>
+
+ <!-- SimpleXYPlotExample-->
+ <ant antfile="build.xml" target="clean" dir="SimpleXYPlotExample"/>
+
+ <!-- StepChartExample-->
+ <ant antfile="build.xml" target="clean" dir="StepChartExample"/>
+
+ <!-- TimedXYPlotExample
+ <ant antfile="build.xml" target="clean" dir="TimedXYPlotExample"/>
+-->
+ </tasks>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+
+</project>
\ No newline at end of file
diff --git a/androidplot.iml b/androidplot.iml
new file mode 100644
index 0000000..9b79bdb
--- /dev/null
+++ b/androidplot.iml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
+ <component name="NewModuleRootManager" inherit-compiler-output="false">
+ <output url="file://$MODULE_DIR$/target/classes" />
+ <output-test url="file://$MODULE_DIR$/target/test-classes" />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$/gen" isTestSource="false" />
+ <excludeFolder url="file://$MODULE_DIR$/target" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ </component>
+</module>
+
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..fd4c3de
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2012 AndroidPlot.com
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.sonatype.oss</groupId>
+ <artifactId>oss-parent</artifactId>
+ <version>7</version>
+ </parent>
+
+ <groupId>com.androidplot</groupId>
+ <artifactId>androidplot</artifactId>
+ <version>0.6.0</version>
+ <name>AndroidPlot-Parent</name>
+ <description>A charting library for the Android platform</description>
+ <url>http://androidplot.com</url>
+ <inceptionYear>2010</inceptionYear>
+ <organization>
+ <name>androidplot.com</name>
+ <url>http://androidplot.com</url>
+ </organization>
+ <licenses>
+ <license>
+ <name>The Apache Software License, Version 2.0</name>
+ <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+ <distribution>repo</distribution>
+ <comments>A business-friendly OSS license</comments>
+ </license>
+ </licenses>
+ <developers>
+ <developer>
+ <id>Nick</id>
+ <name>Nick Fellows</name>
+ <email>[email protected]</email>
+ <organization>AndroidPlot</organization>
+ <organizationUrl>http://androidplot.com</organizationUrl>
+ <roles>
+ <role>architect</role>
+ <role>developer</role>
+ </roles>
+ </developer>
+ <developer>
+ <id>Tim</id>
+ <name>Tim Hepner</name>
+ <email>[email protected]</email>
+ <organization>AndroidPlot</organization>
+ <organizationUrl>http://androidplot.com</organizationUrl>
+ <roles>
+ <role>developer</role>
+ </roles>
+ </developer>
+ </developers>
+ <packaging>pom</packaging>
+
+ <properties>
+ <!--<applicationVersion>0.5.1-SNAPSHOT</applicationVersion>-->
+ <examplesDir>Examples</examplesDir>
+ <!--<obfuscatedJarPath>${project.build.directory}/obfuscated</obfuscatedJarPath>
+ <obfuscatedJarName>${project.build.finalName}.jar</obfuscatedJarName>-->
+ </properties>
+
+ <modules>
+ <module>AndroidPlot-Core</module>
+ <!--<module>Examples</module>-->
+ <module>Examples/DemoApp</module>
+ </modules>
+
+ <!--<repositories>
+ <repository>
+ <id>java.net2</id>
+ <url>http://download.java.net/maven/2/</url>
+ </repository>
+ </repositories>-->
+
+ <scm>
+ <connection>scm:git:https://bitbucket.org/androidplot/androidplot.git</connection>
+ <developerConnection>scm:git:https://bitbucket.org/androidplot/androidplot.git</developerConnection>
+ <url>https://bitbucket.org/androidplot/androidplot.git</url>
+ <tag>0.6.0</tag>
+ </scm>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-release-plugin</artifactId>
+ <version>2.4</version>
+ <configuration>
+ <tagNameFormat>v@{project.version}</tagNameFormat>
+ <!-- Added to fix an issue with hidden gpg signing prompts
+ described here: http://jira.codehaus.org/browse/MGPG-9-->
+ <mavenExecutorId>forked-path</mavenExecutorId>
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <version>2.7</version>
+ <!--<configuration>
+ <skip>true</skip>
+ </configuration>-->
+ </plugin>
+ </plugins>
+ </build>
+
+ <!-- dependencies to be inherited -->
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>com.google.android</groupId>
+ <artifactId>android</artifactId>
+ <version>4.1.1.4</version>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+</project>
\ No newline at end of file