mirror of
https://github.com/dzeiocom/charts.git
synced 2025-06-14 03:49:20 +00:00
feat: Origin
This commit is contained in:
BIN
.gradle/7.5.1/checksums/checksums.lock
Normal file
BIN
.gradle/7.5.1/checksums/checksums.lock
Normal file
Binary file not shown.
BIN
.gradle/7.5.1/checksums/md5-checksums.bin
Normal file
BIN
.gradle/7.5.1/checksums/md5-checksums.bin
Normal file
Binary file not shown.
BIN
.gradle/7.5.1/checksums/sha1-checksums.bin
Normal file
BIN
.gradle/7.5.1/checksums/sha1-checksums.bin
Normal file
Binary file not shown.
BIN
.gradle/7.5.1/dependencies-accessors/dependencies-accessors.lock
Normal file
BIN
.gradle/7.5.1/dependencies-accessors/dependencies-accessors.lock
Normal file
Binary file not shown.
0
.gradle/7.5.1/dependencies-accessors/gc.properties
Normal file
0
.gradle/7.5.1/dependencies-accessors/gc.properties
Normal file
BIN
.gradle/7.5.1/executionHistory/executionHistory.bin
Normal file
BIN
.gradle/7.5.1/executionHistory/executionHistory.bin
Normal file
Binary file not shown.
BIN
.gradle/7.5.1/executionHistory/executionHistory.lock
Normal file
BIN
.gradle/7.5.1/executionHistory/executionHistory.lock
Normal file
Binary file not shown.
BIN
.gradle/7.5.1/fileChanges/last-build.bin
Normal file
BIN
.gradle/7.5.1/fileChanges/last-build.bin
Normal file
Binary file not shown.
BIN
.gradle/7.5.1/fileHashes/fileHashes.bin
Normal file
BIN
.gradle/7.5.1/fileHashes/fileHashes.bin
Normal file
Binary file not shown.
BIN
.gradle/7.5.1/fileHashes/fileHashes.lock
Normal file
BIN
.gradle/7.5.1/fileHashes/fileHashes.lock
Normal file
Binary file not shown.
BIN
.gradle/7.5.1/fileHashes/resourceHashesCache.bin
Normal file
BIN
.gradle/7.5.1/fileHashes/resourceHashesCache.bin
Normal file
Binary file not shown.
0
.gradle/7.5.1/gc.properties
Normal file
0
.gradle/7.5.1/gc.properties
Normal file
BIN
.gradle/buildOutputCleanup/buildOutputCleanup.lock
Normal file
BIN
.gradle/buildOutputCleanup/buildOutputCleanup.lock
Normal file
Binary file not shown.
2
.gradle/buildOutputCleanup/cache.properties
Normal file
2
.gradle/buildOutputCleanup/cache.properties
Normal file
@ -0,0 +1,2 @@
|
||||
#Tue Jan 10 11:53:18 CET 2023
|
||||
gradle.version=7.5.1
|
BIN
.gradle/buildOutputCleanup/outputFiles.bin
Normal file
BIN
.gradle/buildOutputCleanup/outputFiles.bin
Normal file
Binary file not shown.
BIN
.gradle/configuration-cache/configuration-cache.lock
Normal file
BIN
.gradle/configuration-cache/configuration-cache.lock
Normal file
Binary file not shown.
0
.gradle/configuration-cache/gc.properties
Normal file
0
.gradle/configuration-cache/gc.properties
Normal file
Binary file not shown.
BIN
.gradle/configuration-cache/umgqhbtquvzegbsmdt6q62f/entry.bin
Normal file
BIN
.gradle/configuration-cache/umgqhbtquvzegbsmdt6q62f/entry.bin
Normal file
Binary file not shown.
@ -0,0 +1 @@
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
BIN
.gradle/configuration-cache/umgqhbtquvzegbsmdt6q62f/work.bin
Normal file
BIN
.gradle/configuration-cache/umgqhbtquvzegbsmdt6q62f/work.bin
Normal file
Binary file not shown.
0
.gradle/vcs-1/gc.properties
Normal file
0
.gradle/vcs-1/gc.properties
Normal file
3
.idea/.gitignore
generated
vendored
Normal file
3
.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
1
.idea/.name
generated
Normal file
1
.idea/.name
generated
Normal file
@ -0,0 +1 @@
|
||||
Dzeio Charts
|
6
.idea/compiler.xml
generated
Normal file
6
.idea/compiler.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel target="17" />
|
||||
</component>
|
||||
</project>
|
19
.idea/gradle.xml
generated
Normal file
19
.idea/gradle.xml
generated
Normal file
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="testRunner" value="GRADLE" />
|
||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/library" />
|
||||
<option value="$PROJECT_DIR$/sample" />
|
||||
</set>
|
||||
</option>
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
6
.idea/kotlinc.xml
generated
Normal file
6
.idea/kotlinc.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="KotlinJpsPluginSettings">
|
||||
<option name="version" value="1.7.0" />
|
||||
</component>
|
||||
</project>
|
10
.idea/misc.xml
generated
Normal file
10
.idea/misc.xml
generated
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
<option name="id" value="Android" />
|
||||
</component>
|
||||
</project>
|
4
.idea/modules/library/Crash_Handler.library.androidTest.iml
generated
Normal file
4
.idea/modules/library/Crash_Handler.library.androidTest.iml
generated
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module version="4">
|
||||
<component name="TestModuleProperties" production-module="Crash_Handler.library.main" />
|
||||
</module>
|
4
.idea/modules/library/Crash_Handler.library.testFixtures.iml
generated
Normal file
4
.idea/modules/library/Crash_Handler.library.testFixtures.iml
generated
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module version="4">
|
||||
<component name="TestModuleProperties" production-module="Crash_Handler.library.main" />
|
||||
</module>
|
4
.idea/modules/library/Crash_Handler.library.unitTest.iml
generated
Normal file
4
.idea/modules/library/Crash_Handler.library.unitTest.iml
generated
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module version="4">
|
||||
<component name="TestModuleProperties" production-module="Crash_Handler.library.main" />
|
||||
</module>
|
4
.idea/modules/sample/Crash_Handler.sample.androidTest.iml
generated
Normal file
4
.idea/modules/sample/Crash_Handler.sample.androidTest.iml
generated
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module version="4">
|
||||
<component name="TestModuleProperties" production-module="Crash_Handler.sample.main" />
|
||||
</module>
|
4
.idea/modules/sample/Crash_Handler.sample.unitTest.iml
generated
Normal file
4
.idea/modules/sample/Crash_Handler.sample.unitTest.iml
generated
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module version="4">
|
||||
<component name="TestModuleProperties" production-module="Crash_Handler.sample.main" />
|
||||
</module>
|
31
.idea/navEditor.xml
generated
Normal file
31
.idea/navEditor.xml
generated
Normal file
@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="navEditor-manualLayoutAlgorithm2">
|
||||
<option name="myPositions">
|
||||
<map>
|
||||
<entry key="nav_graph.xml">
|
||||
<value>
|
||||
<LayoutPositions>
|
||||
<option name="myPositions">
|
||||
<map>
|
||||
<entry key="MainFragment">
|
||||
<value>
|
||||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="12" />
|
||||
<option name="y" value="12" />
|
||||
</Point>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
128
CODE_OF_CONDUCT.md
Normal file
128
CODE_OF_CONDUCT.md
Normal file
@ -0,0 +1,128 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
the `contact@dze.io` email or on Discord to `Avior#3872`.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
174
CONTRIBUTING.md
Normal file
174
CONTRIBUTING.md
Normal file
@ -0,0 +1,174 @@
|
||||
<!-- omit in toc -->
|
||||
|
||||
# Contributing to Crash handler
|
||||
|
||||
First off, thanks for taking the time to contribute! ❤
|
||||
|
||||
All types of contributions are encouraged and valued. See the [Table of Contents](#table-of-contents) for different ways to help and details about how this project handles them. Please make sure to read the relevant section before making your contribution. It will make it a lot easier for us maintainers and smooth out the experience for all involved. The community looks forward to your contributions. 🎉
|
||||
|
||||
> And if you like the project, but just don't have time to contribute, that's fine. There are other easy ways to support the project and show your appreciation, which we would also be very happy about:
|
||||
>
|
||||
> - Star the project
|
||||
> - Tweet about it
|
||||
> - Refer this project in your project's readme
|
||||
> - Mention the project at local meetups and tell your friends/colleagues
|
||||
<!-- omit in toc -->
|
||||
## Table of Contents
|
||||
|
||||
- [Code of Conduct](#code-of-conduct)
|
||||
- [I Have a Question](#i-have-a-question)
|
||||
- [I Want To Contribute](#i-want-to-contribute)
|
||||
- [Reporting Issues](#reporting-issues)
|
||||
- [Suggesting Enhancements](#suggesting-enhancements)
|
||||
- [Your First Code Contribution](#your-first-code-contribution)
|
||||
- [Improving The Documentation](#improving-the-documentation)
|
||||
- [Styleguides](#styleguides)
|
||||
- [Coding Guidlines](#coding-guidelines)
|
||||
- [Commit Messages](#commit-messages)
|
||||
- [Join The Project Team](#join-the-project-team)
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
This project and everyone participating in it is governed by the
|
||||
[DZEIO Code of Conduct](https://github.com/dzeiocom/crashhandler/blob/master/CODE_OF_CONDUCT.md).
|
||||
By participating, you are expected to uphold this code. Please report unacceptable behavior to <contact.crashhandler@dze.io>.
|
||||
|
||||
## I Have a Question
|
||||
|
||||
|
||||
You can ask them on the Github Repository, it is best to search for existing [Issues](https://github.com/dzeiocom/crashhandler/issues) that might help you.
|
||||
In case you have found a suitable issue and still need clarification, you can write your question in a new issue. It is also advisable to search the internet for answers first.
|
||||
|
||||
If you then still feel the need to ask a question and need clarification, we recommend the following:
|
||||
|
||||
- Open an [Issue](https://github.com/dzeiocom/crashhandler/issues/new).
|
||||
- Provide as much context as you can about what you're running into.
|
||||
- Provide project and platform versions (Android Version, Application Version), depending on what seems relevant.
|
||||
|
||||
We will then take care of the issue as soon as possible.
|
||||
|
||||
<!--
|
||||
You might want to create a separate issue tag for questions and include it in this description. People should then tag their issues accordingly.
|
||||
Depending on how large the project is, you may want to outsource the questioning, e.g. to Stack Overflow or Gitter. You may add additional contact and information possibilities:
|
||||
- IRC
|
||||
- Slack
|
||||
- Gitter
|
||||
- Stack Overflow tag
|
||||
- Blog
|
||||
- FAQ
|
||||
- Roadmap
|
||||
- E-Mail List
|
||||
- Forum
|
||||
-->
|
||||
|
||||
## I Want To Contribute
|
||||
|
||||
### Reporting Issues
|
||||
|
||||
#### Before Submitting an Issue Report
|
||||
|
||||
A good issue report shouldn't leave others needing to chase you up for more information. Therefore, we ask you to investigate carefully, collect information and describe the issue in detail in your report. Please complete the following steps in advance to help us fix any potential bug as fast as possible.
|
||||
|
||||
- Determine if your issue is not an error on your side e.g. using incompatible environment components/versions. If you are looking for support, you might want to check [this section](#i-have-a-question)).
|
||||
- To see if other users have experienced (and potentially already solved) the same issue you are having, check if there is not already an issue report existing for your bug or error in the [bug tracker](https://github.com/dzeiocom/crashhandler/issues).
|
||||
- Also make sure to search the internet (including Stack Overflow) to see if users outside of the GitHub community have discussed the issue.
|
||||
- Collect information about the bug:
|
||||
- Stack trace (Traceback)
|
||||
- OS, Platform and Version (Phone, OS Version, App Version, etc...)
|
||||
- Possibly your input and the output
|
||||
- Can you reliably reproduce the issue? And can you also reproduce it with older versions?
|
||||
|
||||
<!-- omit in toc -->
|
||||
|
||||
#### How Do I Submit a Good Bug Report?
|
||||
|
||||
> You must never report security related issues, vulnerabilities or bugs to the issue tracker, or elsewhere in public. Instead sensitive bugs must be sent by email to <security@dze.io>.
|
||||
<!-- You may add a PGP key to allow the messages to be sent encrypted as well. -->
|
||||
We use GitHub issues to track bugs and errors. If you run into an issue with the project:
|
||||
|
||||
- Open an [Issue](https://github.com/dzeiocom/crashhandler/issues/new). (Since we can't be sure at this point whether it is a bug or not, we ask you not to talk about a bug yet and not to label the issue.)
|
||||
- Explain the behavior you would expect and the actual behavior.
|
||||
- Please provide as much context as possible and describe the *reproduction steps* that someone else can follow to recreate the issue on their own. This usually includes your code. For good bug reports you should isolate the problem and create a reduced test case.
|
||||
- Provide the information you collected in the previous section.
|
||||
|
||||
Once it's filled:
|
||||
|
||||
- The project team will label the issue accordingly.
|
||||
- A team member will try to reproduce the issue with your provided steps. If there are no reproduction steps or no obvious way to reproduce the issue, the team will ask you for those steps and mark the issue as `needs-repro`. Bugs with the `needs-repro` tag will not be addressed until they are reproduced.
|
||||
- If the team is able to reproduce the issue, it will be marked `needs-fix`, as well as possibly other tags (such as `critical`), and the issue will be left to be [implemented by someone](#your-first-code-contribution).
|
||||
|
||||
<!-- You might want to create an issue template for bugs and errors that can be used as a guide and that defines the structure of the information to be included. If you do so, reference it here in the description. -->
|
||||
|
||||
### Suggesting Enhancements
|
||||
|
||||
This section guides you through submitting an enhancement suggestion, **including completely new features and minor improvements to existing functionality**. Following these guidelines will help maintainers and the community to understand your suggestion and find related suggestions.
|
||||
|
||||
<!-- omit in toc -->
|
||||
|
||||
#### Before Submitting an Enhancement
|
||||
|
||||
- Make sure that you are using the latest version.
|
||||
- Perform a [search](https://github.com/dzeiocom/crashhandler/issues) to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one.
|
||||
- Find out whether your idea fits with the scope and aims of the project. It's up to you to make a strong case to convince the project's developers of the merits of this feature. Keep in mind that we want features that will be useful to the majority of our users and not just a small subset. If you're just targeting a minority of users, consider writing an add-on/plugin library.
|
||||
|
||||
<!-- omit in toc -->
|
||||
|
||||
#### How Do I Submit a Good Enhancement Suggestion?
|
||||
|
||||
Enhancement suggestions are tracked as [GitHub issues](https://github.com/dzeiocom/crashhandler/issues).
|
||||
|
||||
- Use a **clear and descriptive title** for the issue to identify the suggestion.
|
||||
- Provide a **step-by-step description of the suggested enhancement** in as many details as possible.
|
||||
- **Describe the current behavior** and **explain which behavior you expected to see instead** and why. At this point you can also tell which alternatives do not work for you.
|
||||
- You may want to **include screenshots and animated GIFs** which help you demonstrate the steps or point out the part which the suggestion is related to.
|
||||
- **Explain why this enhancement would be useful**. You may also want to point out the other projects that solved it better and which could serve as inspiration.
|
||||
|
||||
<!-- You might want to create an issue template for enhancement suggestions that can be used as a guide and that defines the structure of the information to be included. If you do so, reference it here in the description. -->
|
||||
|
||||
### Your First Code Contribution
|
||||
|
||||
_note: Follow the different styleguides listed below when contributing_
|
||||
|
||||
- Fork 🍴 the project. _see the `fork` button at the top right of the screen_
|
||||
- make the changes you want in your repository.
|
||||
- Create a Pull request here https://github.com/dzeiocom/crashhandler/compare by selecting your repository patch with our `master` branch _If it's not finished put WIP: before_
|
||||
- we don't like ❌, so if your pull request has its automated checks ending with the red cross, please double check your changes until it show the awesome 🟢, or ask for help !
|
||||
- If your pull request is ready for review remove WIP: put it s ready for review and we will handle the rest !
|
||||
|
||||
## Styleguides
|
||||
|
||||
### Coding Guidelines
|
||||
|
||||
Install the plugin `.editorconfig` in your dev environment if it's not already done and thats it !
|
||||
|
||||
### Commit Messages
|
||||
|
||||
We follow the [Conventional Commit specification](conventionalcommits.org) and are using the [Angular format](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#commit) with some changes.
|
||||
|
||||
In short, please name your Pull Requests/Commits following this format
|
||||
```
|
||||
<type>(<scope>): <short summary>
|
||||
<BLANK LINE>
|
||||
<body>
|
||||
<BLANK LINE>
|
||||
<footer>
|
||||
```
|
||||
|
||||
- `type` MUST be one of
|
||||
- build: Changes affecting the CI at `.github/workflows` or `Dockerfile`
|
||||
- ci: same as build
|
||||
- docs: Changes affecting the differents Documents
|
||||
- feat: a new feature being added
|
||||
- fix: fixes
|
||||
- pref: Performance related fixes
|
||||
- refactor: Misc code changes
|
||||
- test: same as `ci`
|
||||
- the field `(<scope>)` is optional
|
||||
- `short summary` MUST follow thoses rules
|
||||
- Capitalize the first letter
|
||||
- use the imperative, present tense: "Change" not "Changed" nor "Changes"
|
||||
- `<footer>` and `<body>` are both optional
|
||||
|
||||
## Attribution
|
||||
|
||||
This guide is based on [Contribution Gen]((https://github.com/bttger/contributing-gen)) and was adapted by the community !
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Florian Bouillon
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
32
README.md
Normal file
32
README.md
Normal file
@ -0,0 +1,32 @@
|
||||
# Crash Handler
|
||||
|
||||
Highly customizable and easy to use Chart library for android
|
||||
|
||||
## Install
|
||||
|
||||
- Add Jitpack.io to your `settings.gradle` file `maven { url 'https://jitpack.io' }`
|
||||
|
||||
Add to you dependencies (check the latest release for the version):
|
||||
- (Gradle Kotlin DSL) Add `implementation("com.dzeio:charts:1.0.0")`
|
||||
- (Gradle Groovy DSL) Add `implementation "com.dzeio:charts:1.0.0" `
|
||||
|
||||
## Build
|
||||
|
||||
- Install Android Studio
|
||||
- Build the app
|
||||
- it will be running on your emulator/device
|
||||
- test it!
|
||||
|
||||
## Contributing
|
||||
|
||||
See [CONTRIBUTING.md](https://github.com/dzeiocom/charts/blob/master/CONTRIBUTING.md)
|
||||
|
||||
TL::DR
|
||||
|
||||
- Fork
|
||||
- Commit your changes
|
||||
- Pull Request on this Repository
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License. A copy of the license is available at [LICENSE.md](https://github.com/dzeiocom/charts/blob/master/LICENSE.md)
|
23
build.gradle.kts
Normal file
23
build.gradle.kts
Normal file
@ -0,0 +1,23 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath("com.android.tools.build:gradle:7.3.1")
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id("com.android.application") version "7.3.1" apply false
|
||||
id("com.android.library") version "7.3.1" apply false
|
||||
id("org.jetbrains.kotlin.android") version "1.7.0" apply false
|
||||
}
|
||||
|
||||
task("clean") {
|
||||
delete(rootProject.buildDir)
|
||||
delete(project.buildDir)
|
||||
}
|
File diff suppressed because one or more lines are too long
18
gradle.properties
Normal file
18
gradle.properties
Normal file
@ -0,0 +1,18 @@
|
||||
## For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
#
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
# Default value: -Xmx1024m -XX:MaxPermSize=256m
|
||||
org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||
#
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
org.gradle.parallel=true
|
||||
#Tue Jul 19 18:42:00 CEST 2022
|
||||
android.nonTransitiveRClass=true
|
||||
kotlin.code.style=official
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=false
|
||||
org.gradle.unsafe.configuration-cache=true
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
#Sun Aug 07 22:38:24 CEST 2022
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
|
||||
distributionPath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
185
gradlew
vendored
Normal file
185
gradlew
vendored
Normal file
@ -0,0 +1,185 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
#
|
||||
# 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
|
||||
#
|
||||
# https://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.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
case $i in
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
exec "$JAVACMD" "$@"
|
89
gradlew.bat
vendored
Normal file
89
gradlew.bat
vendored
Normal file
@ -0,0 +1,89 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
1
library/.gitignore
vendored
Normal file
1
library/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/build
|
34
library/build.gradle.kts
Normal file
34
library/build.gradle.kts
Normal file
@ -0,0 +1,34 @@
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
kotlin("android")
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdk = 33
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 21
|
||||
targetSdk = 33
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
consumerProguardFiles("consumer-rules.pro")
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = true
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
namespace = "com.dzeio.charts"
|
||||
}
|
0
library/consumer-rules.pro
Normal file
0
library/consumer-rules.pro
Normal file
21
library/proguard-rules.pro
vendored
Normal file
21
library/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.kts.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
@ -0,0 +1,24 @@
|
||||
package com.dzeio.charts
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ExampleInstrumentedTest {
|
||||
@Test
|
||||
fun useAppContext() {
|
||||
// Context of the app under test.
|
||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
assertEquals("com.dzeio.charts.test", appContext.packageName)
|
||||
}
|
||||
}
|
2
library/src/main/AndroidManifest.xml
Normal file
2
library/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest></manifest>
|
66
library/src/main/java/com/dzeio/charts/Animation.kt
Normal file
66
library/src/main/java/com/dzeio/charts/Animation.kt
Normal file
@ -0,0 +1,66 @@
|
||||
package com.dzeio.charts
|
||||
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
|
||||
data class Animation(
|
||||
/**
|
||||
* Enable / Disable the Chart Animations
|
||||
*/
|
||||
var enabled: Boolean = true,
|
||||
|
||||
/**
|
||||
* Number of milliseconds the animation is running before it ends
|
||||
*/
|
||||
var duration: Int = 1000,
|
||||
|
||||
/**
|
||||
* Number of updates per seconds
|
||||
*/
|
||||
var refreshRate: Int = 50
|
||||
) {
|
||||
/**
|
||||
* Update the value depending on the maximum obtainable value
|
||||
*
|
||||
* @param maxValue the maximum value the item can obtain
|
||||
* @param targetValue the value you want to obtain at the end of the animation
|
||||
* @param currentValue the current value
|
||||
*
|
||||
* @return the new updated value
|
||||
*/
|
||||
fun updateValue(
|
||||
maxValue: Float,
|
||||
targetValue: Float,
|
||||
currentValue: Float,
|
||||
minValue: Float,
|
||||
minStep: Float
|
||||
): Float {
|
||||
if (!enabled) {
|
||||
return targetValue
|
||||
}
|
||||
|
||||
if (currentValue < minValue) {
|
||||
return minValue
|
||||
}
|
||||
|
||||
val moveValue = max(minStep, (maxValue - targetValue) / refreshRate)
|
||||
|
||||
var result = targetValue
|
||||
if (currentValue < targetValue) {
|
||||
result = currentValue + moveValue
|
||||
} else if (currentValue > targetValue) {
|
||||
result = currentValue - moveValue
|
||||
}
|
||||
|
||||
if (
|
||||
abs(targetValue - currentValue) <= moveValue ||
|
||||
result < minValue ||
|
||||
result > maxValue
|
||||
) {
|
||||
return targetValue
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
fun getDelay() = this.duration / this.refreshRate
|
||||
}
|
150
library/src/main/java/com/dzeio/charts/ChartView.kt
Normal file
150
library/src/main/java/com/dzeio/charts/ChartView.kt
Normal file
@ -0,0 +1,150 @@
|
||||
package com.dzeio.charts
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
import android.graphics.RectF
|
||||
import android.util.AttributeSet
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import com.dzeio.charts.axis.XAxis
|
||||
import com.dzeio.charts.axis.YAxis
|
||||
import com.dzeio.charts.components.ChartScroll
|
||||
import com.dzeio.charts.series.SerieInterface
|
||||
|
||||
class ChartView @JvmOverloads constructor(context: Context?, attrs: AttributeSet? = null) :
|
||||
View(context, attrs), ChartViewInterface {
|
||||
|
||||
private companion object {
|
||||
const val TAG = "Charts/ChartView"
|
||||
}
|
||||
|
||||
override var debug: Boolean = false
|
||||
|
||||
override val xAxis = XAxis(this)
|
||||
|
||||
override val yAxis = YAxis(this)
|
||||
|
||||
override var series: ArrayList<SerieInterface> = arrayListOf()
|
||||
|
||||
override var padding: Float = 8f
|
||||
|
||||
private val scroller = ChartScroll(this).apply {
|
||||
var lastMovement = 0.0
|
||||
setOnChartMoved { movementX, _ ->
|
||||
xAxis.x += (movementX - lastMovement) * xAxis.getDataWidth() / width
|
||||
lastMovement = movementX.toDouble()
|
||||
refresh()
|
||||
}
|
||||
// setOnZoomChanged {
|
||||
// Log.d(TAG, "New Zoom: $it")
|
||||
// zoom = (it * 1.2).toFloat()
|
||||
// refresh()
|
||||
// }
|
||||
}
|
||||
|
||||
// val animator: Runnable = object : Runnable {
|
||||
// override fun run() {
|
||||
// var needNewFrame = false
|
||||
// for (serie in series) {
|
||||
// val result = serie.onUpdate()
|
||||
// if (result) {
|
||||
// needNewFrame = true
|
||||
// }
|
||||
// }
|
||||
// if (needNewFrame) {
|
||||
// postDelayed(this, animation.getDelay().toLong())
|
||||
// }
|
||||
// invalidate()
|
||||
// }
|
||||
// }
|
||||
|
||||
// rect used for calculations
|
||||
private val rect = RectF()
|
||||
|
||||
// stroke used while in debug
|
||||
private val debugStrokePaint = Paint().apply {
|
||||
style = Paint.Style.STROKE
|
||||
strokeWidth = 8f
|
||||
color = Color.parseColor("#654321")
|
||||
}
|
||||
|
||||
override fun refresh() {
|
||||
// run Axis logics
|
||||
xAxis.refresh()
|
||||
yAxis.refresh()
|
||||
|
||||
// run series logic
|
||||
for (serie in series) {
|
||||
serie.refresh()
|
||||
}
|
||||
|
||||
// invalidate the view
|
||||
invalidate()
|
||||
// removeCallbacks(animator)
|
||||
// post(animator)
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
|
||||
// don't draw anything if everything is empty
|
||||
if (series.isEmpty() || series.maxOf { it.entries.size } == 0) {
|
||||
super.onDraw(canvas)
|
||||
return
|
||||
}
|
||||
|
||||
if (debug) {
|
||||
// draw corners
|
||||
canvas.drawRect(rect.apply {
|
||||
set(
|
||||
padding / 2,
|
||||
padding / 2,
|
||||
width.toFloat() - padding / 2,
|
||||
height.toFloat() - padding / 2
|
||||
)
|
||||
}, debugStrokePaint)
|
||||
}
|
||||
|
||||
|
||||
val bottom = xAxis.onDraw(canvas, rect.apply {
|
||||
set(padding, 0f, width.toFloat() - padding, height.toFloat() - padding)
|
||||
})
|
||||
|
||||
// right distance from the yAxis
|
||||
val rightDistance = yAxis.onDraw(canvas, rect.apply {
|
||||
set(padding, padding, width.toFloat() - padding, height.toFloat() - bottom - padding)
|
||||
})
|
||||
|
||||
// chart draw rectangle
|
||||
rect.apply {
|
||||
set(
|
||||
padding,
|
||||
padding,
|
||||
width.toFloat() - padding - rightDistance,
|
||||
height - bottom - padding
|
||||
)
|
||||
}
|
||||
|
||||
for (serie in series) {
|
||||
serie.onDraw(canvas, rect)
|
||||
}
|
||||
super.onDraw(canvas)
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onTouchEvent(event: MotionEvent): Boolean {
|
||||
performClick()
|
||||
return scroller.onTouchEvent(event)
|
||||
}
|
||||
|
||||
override fun getDataset(): ArrayList<Entry> {
|
||||
val data: ArrayList<Entry> = arrayListOf()
|
||||
for (serie in series) {
|
||||
data.addAll(serie.entries)
|
||||
}
|
||||
data.sortBy { it.x }
|
||||
return data.filterIndexed { index, entry -> data.indexOf(entry) == index } as ArrayList<Entry>
|
||||
}
|
||||
}
|
47
library/src/main/java/com/dzeio/charts/ChartViewInterface.kt
Normal file
47
library/src/main/java/com/dzeio/charts/ChartViewInterface.kt
Normal file
@ -0,0 +1,47 @@
|
||||
package com.dzeio.charts
|
||||
|
||||
import com.dzeio.charts.axis.XAxisInterface
|
||||
import com.dzeio.charts.axis.YAxisInterface
|
||||
import com.dzeio.charts.series.SerieInterface
|
||||
|
||||
interface ChartViewInterface {
|
||||
|
||||
/**
|
||||
* Make the whole view in debug mode
|
||||
*
|
||||
* add debug texts, logs, and more
|
||||
*/
|
||||
var debug: Boolean
|
||||
|
||||
/**
|
||||
* the padding inside the view
|
||||
*/
|
||||
var padding: Float
|
||||
|
||||
/**
|
||||
* Hold metadata about the X axis
|
||||
*/
|
||||
val xAxis: XAxisInterface
|
||||
|
||||
/**
|
||||
* Hold informations about the Y axis
|
||||
*/
|
||||
val yAxis: YAxisInterface
|
||||
|
||||
/**
|
||||
* handle the series
|
||||
*/
|
||||
var series: ArrayList<SerieInterface>
|
||||
|
||||
/**
|
||||
* refresh and run pre-display logic the chart
|
||||
*
|
||||
* this function should be run if you change parameters in the view
|
||||
*/
|
||||
fun refresh()
|
||||
|
||||
/**
|
||||
* @return the whole dataset (sorted and cleaned up of dupps)
|
||||
*/
|
||||
fun getDataset(): ArrayList<Entry>
|
||||
}
|
10
library/src/main/java/com/dzeio/charts/Entry.kt
Normal file
10
library/src/main/java/com/dzeio/charts/Entry.kt
Normal file
@ -0,0 +1,10 @@
|
||||
package com.dzeio.charts
|
||||
|
||||
/**
|
||||
* A Base entry for any charts
|
||||
*/
|
||||
data class Entry(
|
||||
var x: Double,
|
||||
var y: Float,
|
||||
var color: Int? = null
|
||||
)
|
140
library/src/main/java/com/dzeio/charts/axis/XAxis.kt
Normal file
140
library/src/main/java/com/dzeio/charts/axis/XAxis.kt
Normal file
@ -0,0 +1,140 @@
|
||||
package com.dzeio.charts.axis
|
||||
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
import android.graphics.Rect
|
||||
import android.graphics.RectF
|
||||
import android.util.Log
|
||||
import com.dzeio.charts.ChartViewInterface
|
||||
import com.dzeio.charts.Entry
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class XAxis(
|
||||
private val view: ChartViewInterface
|
||||
) : XAxisInterface {
|
||||
|
||||
private companion object {
|
||||
const val TAG = "Charts/XAxis"
|
||||
}
|
||||
|
||||
override var x: Double = 0.0
|
||||
set(value) {
|
||||
val max = getXMax() - getDataWidth()
|
||||
val min = getXMin()
|
||||
if (value > max && min <= max) {
|
||||
field = max
|
||||
return
|
||||
}
|
||||
|
||||
if (value < min) {
|
||||
field = min
|
||||
return
|
||||
}
|
||||
|
||||
field = value
|
||||
}
|
||||
|
||||
override var enabled = true
|
||||
|
||||
override var dataWidth: Double? = null
|
||||
get() = field ?: getXMax()
|
||||
|
||||
override var labelCount: Int = 2
|
||||
|
||||
var spacing = 16.0
|
||||
|
||||
override val textPaint = Paint().apply {
|
||||
isAntiAlias = true
|
||||
color = Color.parseColor("#FC496D")
|
||||
textSize = 30f
|
||||
textAlign = Paint.Align.LEFT
|
||||
}
|
||||
|
||||
private val rect = Rect()
|
||||
|
||||
override fun getPositionOnRect(entry: Entry, drawableSpace: RectF): Double {
|
||||
return translatePositionToRect(entry.x, drawableSpace)
|
||||
}
|
||||
|
||||
fun translatePositionToRect(value: Double, drawableSpace: RectF): Double {
|
||||
return drawableSpace.width() * (value - x) / getDataWidth()
|
||||
}
|
||||
|
||||
override fun getXMax(): Double {
|
||||
return view.series.maxOf { serie ->
|
||||
if (serie.entries.isEmpty()) {
|
||||
return 0.0
|
||||
}
|
||||
serie.entries.maxOf { entry -> entry.x }
|
||||
}
|
||||
}
|
||||
|
||||
override fun getXMin(): Double {
|
||||
return view.series.minOf { serie ->
|
||||
if (serie.entries.isEmpty()) {
|
||||
return 0.0
|
||||
}
|
||||
serie.entries.minOf { entry -> entry.x }
|
||||
}
|
||||
}
|
||||
|
||||
var onValueFormat: (value: Double) -> String = { it -> it.roundToInt().toString() }
|
||||
|
||||
override fun onDraw(canvas: Canvas, space: RectF): Float {
|
||||
if (!enabled) {
|
||||
return 0f
|
||||
}
|
||||
|
||||
var maxHeight = 0f
|
||||
|
||||
val graphIncrement = space.width() / (labelCount - 1)
|
||||
val valueIncrement = (getDataWidth() / (labelCount - 1)).toDouble()
|
||||
for (index in 0 until labelCount) {
|
||||
val text = onValueFormat(x + valueIncrement * index)
|
||||
textPaint.getTextBounds(text, 0, text.length, rect)
|
||||
maxHeight = maxHeight.coerceAtLeast(rect.height().toFloat() + 1)
|
||||
|
||||
var xPos = space.left + graphIncrement * index
|
||||
|
||||
if (xPos + rect.width() > space.right) {
|
||||
xPos = space.right - rect.width()
|
||||
}
|
||||
|
||||
canvas.drawText(
|
||||
text,
|
||||
xPos,
|
||||
space.bottom,
|
||||
textPaint
|
||||
)
|
||||
}
|
||||
return maxHeight + 32f
|
||||
}
|
||||
|
||||
override fun refresh() {
|
||||
// TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun getEntryWidth(drawableSpace: RectF): Double {
|
||||
var smallest = Double.MAX_VALUE
|
||||
val dataset = view.getDataset()
|
||||
for (idx in 0 until dataset.size - 1) {
|
||||
val distance = dataset[idx + 1].x - dataset[idx].x
|
||||
if (smallest > distance && distance > 0.0) {
|
||||
smallest = distance
|
||||
}
|
||||
}
|
||||
|
||||
return clamp(drawableSpace.width() * smallest / getDataWidth() - spacing, 1.0, drawableSpace.width().toDouble())
|
||||
}
|
||||
|
||||
|
||||
override fun getDataWidth(): Double {
|
||||
// TODO: handle the auto dataWidth better
|
||||
return dataWidth ?: getXMax()
|
||||
}
|
||||
|
||||
private fun clamp(value: Double, min: Double, max: Double): Double {
|
||||
return if (value < min) min else if (value > max) max else value
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
package com.dzeio.charts.axis
|
||||
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Paint
|
||||
import android.graphics.RectF
|
||||
import com.dzeio.charts.Entry
|
||||
|
||||
sealed interface XAxisInterface {
|
||||
|
||||
/**
|
||||
* enable/disable the display of the xAxis
|
||||
*/
|
||||
var enabled: Boolean
|
||||
|
||||
/**
|
||||
* set X position
|
||||
*/
|
||||
var x: Double
|
||||
|
||||
/**
|
||||
* the "width" of the graph
|
||||
*
|
||||
* if not set it will be `XMax - XMin`
|
||||
*
|
||||
* ex: to display a 7 days graph history with x values being timestamp in secs, use 7*24*60*60
|
||||
*/
|
||||
var dataWidth: Double?
|
||||
|
||||
/**
|
||||
* text Paint
|
||||
*/
|
||||
val textPaint: Paint
|
||||
|
||||
/**
|
||||
* indicate the number of labels displayed
|
||||
*/
|
||||
var labelCount: Int
|
||||
|
||||
/**
|
||||
* run when manually refreshing the system
|
||||
*
|
||||
* this is where the pre-logic is handled to make [onDraw] quicker
|
||||
*/
|
||||
fun refresh()
|
||||
|
||||
/**
|
||||
* get the entry position on the rect
|
||||
*
|
||||
* @return the left side of the position of the entry
|
||||
*/
|
||||
fun getPositionOnRect(entry: Entry, drawableSpace: RectF): Double
|
||||
|
||||
/**
|
||||
* get the maximum the X can get to
|
||||
*/
|
||||
fun getXMax(): Double
|
||||
|
||||
/**
|
||||
* get the minimum the X can get to
|
||||
*/
|
||||
fun getXMin(): Double
|
||||
|
||||
/**
|
||||
* get the size of an entry in the graph
|
||||
*
|
||||
* @return the size in [drawableSpace] px
|
||||
*/
|
||||
fun getEntryWidth(drawableSpace: RectF): Double
|
||||
|
||||
/**
|
||||
* return the currently used dataWidth
|
||||
*/
|
||||
fun getDataWidth(): Double
|
||||
|
||||
/**
|
||||
* onDraw event that will draw the XAxis
|
||||
*
|
||||
* @param canvas the canvas to draw on
|
||||
* @param space the space where it is allowed to draw
|
||||
*
|
||||
* @return the final height of the XAxis
|
||||
*/
|
||||
fun onDraw(canvas: Canvas, space: RectF): Float
|
||||
}
|
146
library/src/main/java/com/dzeio/charts/axis/YAxis.kt
Normal file
146
library/src/main/java/com/dzeio/charts/axis/YAxis.kt
Normal file
@ -0,0 +1,146 @@
|
||||
package com.dzeio.charts.axis
|
||||
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
import android.graphics.Rect
|
||||
import android.graphics.RectF
|
||||
import com.dzeio.charts.ChartViewInterface
|
||||
import com.dzeio.charts.utils.drawDottedLine
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class YAxis(
|
||||
private val view: ChartViewInterface
|
||||
) : YAxisInterface {
|
||||
|
||||
override var enabled = true
|
||||
|
||||
override val textLabel = Paint().apply {
|
||||
isAntiAlias = true
|
||||
color = Color.parseColor("#FC496D")
|
||||
textSize = 30f
|
||||
textAlign = Paint.Align.LEFT
|
||||
}
|
||||
|
||||
override val linePaint = Paint().apply {
|
||||
isAntiAlias = true
|
||||
color = Color.BLUE
|
||||
}
|
||||
|
||||
override val goalLinePaint = Paint().apply {
|
||||
isAntiAlias = true
|
||||
color = Color.RED
|
||||
strokeWidth = 4f
|
||||
}
|
||||
|
||||
var onValueFormat: (value: Float) -> String = { it -> it.roundToInt().toString() }
|
||||
|
||||
override var labelCount = 5
|
||||
|
||||
private var min: Float? = 0f
|
||||
private var max: Float? = null
|
||||
|
||||
private val rect = Rect()
|
||||
|
||||
override fun setYMin(yMin: Float?): YAxisInterface {
|
||||
min = yMin
|
||||
return this
|
||||
}
|
||||
|
||||
override fun setYMax(yMax: Float?): YAxisInterface {
|
||||
max = yMax
|
||||
return this
|
||||
}
|
||||
|
||||
override fun getYMax(): Float {
|
||||
if (max != null) {
|
||||
return max!!
|
||||
}
|
||||
if (view.series.isEmpty()) {
|
||||
return (this.goalLine ?: 90f) + 10f
|
||||
}
|
||||
val seriesMax = view.series
|
||||
.maxOf { serie ->
|
||||
if (serie.getDisplayedEntries().isEmpty()) {
|
||||
return@maxOf 0f
|
||||
}
|
||||
return@maxOf serie.getDisplayedEntries().maxOf { entry -> entry.y }
|
||||
}
|
||||
if (this.goalLine != null) {
|
||||
return if (seriesMax > this.goalLine!!) seriesMax else this.goalLine!! + 1000f
|
||||
}
|
||||
return seriesMax
|
||||
}
|
||||
|
||||
override fun getYMin(): Float {
|
||||
if (min != null) {
|
||||
return min!!
|
||||
}
|
||||
if (view.series.isEmpty()) {
|
||||
return this.goalLine ?: 0f
|
||||
}
|
||||
return view.series
|
||||
.minOf { serie ->
|
||||
if (serie.getDisplayedEntries().isEmpty()) {
|
||||
return@minOf 0f
|
||||
}
|
||||
return@minOf serie.getDisplayedEntries().minOf { entry -> entry.y }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas, space: RectF): Float {
|
||||
if (!enabled) {
|
||||
return 0f
|
||||
}
|
||||
|
||||
val min = getYMin()
|
||||
val max = getYMax() - min
|
||||
val top = space.top
|
||||
val bottom = space.bottom
|
||||
var maxWidth = 0f
|
||||
|
||||
val increment = (bottom - top) / labelCount
|
||||
val valueIncrement = (max - min) / labelCount
|
||||
for (index in 0 until labelCount) {
|
||||
val text = onValueFormat((valueIncrement * (index + 1)))
|
||||
textLabel.getTextBounds(text, 0, text.length, rect)
|
||||
maxWidth = maxWidth.coerceAtLeast(rect.width().toFloat())
|
||||
|
||||
val posY = bottom - (index + 1) * increment
|
||||
|
||||
canvas.drawText(
|
||||
text,
|
||||
space.width() - rect.width().toFloat(),
|
||||
(posY + rect.height() / 2).coerceAtLeast(rect.height().toFloat()),
|
||||
textLabel
|
||||
)
|
||||
// canvas.drawDottedLine(0f, posY, canvas.width.toFloat(), posY, 40f, linePaint)
|
||||
canvas.drawLine(space.left, posY, space.right - maxWidth - 32f, posY, linePaint)
|
||||
|
||||
}
|
||||
|
||||
if (this.goalLine != null) {
|
||||
val pos = (1 - this.goalLine!! / max) * space.height() + space.top
|
||||
canvas.drawDottedLine(
|
||||
0f,
|
||||
pos,
|
||||
space.right - maxWidth - 32f,
|
||||
pos,
|
||||
space.right / 20,
|
||||
goalLinePaint
|
||||
)
|
||||
}
|
||||
|
||||
return maxWidth + 32f
|
||||
}
|
||||
|
||||
override fun refresh() {
|
||||
// TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
private var goalLine: Float? = null
|
||||
|
||||
override fun setGoalLine(height: Float?) {
|
||||
goalLine = height
|
||||
}
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
package com.dzeio.charts.axis
|
||||
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Paint
|
||||
import android.graphics.RectF
|
||||
|
||||
sealed interface YAxisInterface {
|
||||
|
||||
/**
|
||||
* whether or not this axis is displayed
|
||||
*/
|
||||
var enabled: Boolean
|
||||
|
||||
/**
|
||||
* get/set the number of label of this Y axis
|
||||
*
|
||||
* the first/last labels are at the bottom/top of the chart
|
||||
*/
|
||||
var labelCount: Int
|
||||
|
||||
/**
|
||||
* text label paint
|
||||
*/
|
||||
val textLabel: Paint
|
||||
|
||||
/**
|
||||
* paint for the lines
|
||||
*/
|
||||
val linePaint: Paint
|
||||
|
||||
/**
|
||||
* Goal line paint
|
||||
*/
|
||||
val goalLinePaint: Paint
|
||||
|
||||
/**
|
||||
* run when manually refreshing the system
|
||||
*
|
||||
* this is where the pre-logic is handled to make [onDraw] quicker
|
||||
*/
|
||||
fun refresh()
|
||||
|
||||
/**
|
||||
* override Y minimum
|
||||
*
|
||||
* @param yMin is set the min will ba at the value, if null it is calculated
|
||||
*/
|
||||
fun setYMin(yMin: Float?): YAxisInterface
|
||||
|
||||
/**
|
||||
* override Y maximum
|
||||
*
|
||||
* @param yMax is set the max will ba at the value, if null it is calculated
|
||||
*/
|
||||
fun setYMax(yMax: Float?): YAxisInterface
|
||||
|
||||
/**
|
||||
* get Y maximum
|
||||
*
|
||||
* @return the maximum value Y can get (for displayed values)
|
||||
*/
|
||||
fun getYMax(): Float
|
||||
|
||||
/**
|
||||
* get Y minimum
|
||||
*
|
||||
* @return the minimum value Y can get (for displayed values)
|
||||
*/
|
||||
fun getYMin(): Float
|
||||
|
||||
/**
|
||||
* function that draw our legend
|
||||
*
|
||||
* @param canvas the canvas to draw on
|
||||
* @param space the space where it is allowed to draw on
|
||||
*
|
||||
* @return the width of the sidebar
|
||||
*/
|
||||
fun onDraw(canvas: Canvas, space: RectF): Float
|
||||
|
||||
/**
|
||||
* Add a Goal line
|
||||
*/
|
||||
fun setGoalLine(height: Float?)
|
||||
}
|
11
library/src/main/java/com/dzeio/charts/axis/YAxisPosition.kt
Normal file
11
library/src/main/java/com/dzeio/charts/axis/YAxisPosition.kt
Normal file
@ -0,0 +1,11 @@
|
||||
package com.dzeio.charts.axis
|
||||
|
||||
/**
|
||||
* CURRENTLY UNUSED
|
||||
*
|
||||
* declare where the YAxis for the graph will be
|
||||
*/
|
||||
enum class YAxisPosition {
|
||||
LEFT,
|
||||
RIGHT
|
||||
}
|
144
library/src/main/java/com/dzeio/charts/components/ChartScroll.kt
Normal file
144
library/src/main/java/com/dzeio/charts/components/ChartScroll.kt
Normal file
@ -0,0 +1,144 @@
|
||||
package com.dzeio.charts.components
|
||||
|
||||
import android.view.MotionEvent
|
||||
import android.view.MotionEvent.INVALID_POINTER_ID
|
||||
import android.view.ScaleGestureDetector
|
||||
import android.view.View
|
||||
|
||||
/**
|
||||
* Class handling the scroll/zoom for the library
|
||||
*/
|
||||
class ChartScroll(view: View) {
|
||||
|
||||
/**
|
||||
* Enabled the zoom/unzoom of datas
|
||||
*/
|
||||
var zoomEnabled = true
|
||||
|
||||
/**
|
||||
* Enable the horizontal scroll feature
|
||||
*/
|
||||
var scrollEnabled = true
|
||||
|
||||
// The ‘active pointer’ is the one currently moving our object.
|
||||
private var activePointerId = INVALID_POINTER_ID
|
||||
|
||||
private var lastTouchX: Float = 0f
|
||||
private var lastTouchY: Float = 0f
|
||||
|
||||
private var posX: Float = 0f
|
||||
private var posY: Float = 0f
|
||||
|
||||
private var lastZoom: Float = 100f
|
||||
private var currentZoom: Float = 0f
|
||||
|
||||
private var onChartMoved: ((movementX: Float, movementY: Float) -> Unit)? = null
|
||||
fun setOnChartMoved(fn: (movementX: Float, movementY: Float) -> Unit) {
|
||||
onChartMoved = fn
|
||||
}
|
||||
|
||||
private var onZoomChanged: ((scale: Float) -> Unit)? = null
|
||||
|
||||
/**
|
||||
* @param fn.scale Float starting from 100%
|
||||
*
|
||||
* 99-% zoom out,
|
||||
* 101+% zoom in
|
||||
*/
|
||||
fun setOnZoomChanged(fn: (scale: Float) -> Unit) {
|
||||
onZoomChanged = fn
|
||||
}
|
||||
|
||||
private val scaleGestureDetector = ScaleGestureDetector(
|
||||
view.context,
|
||||
object : ScaleGestureDetector.SimpleOnScaleGestureListener() {
|
||||
override fun onScale(detector: ScaleGestureDetector): Boolean {
|
||||
if (currentZoom != detector.scaleFactor) {
|
||||
currentZoom = detector.scaleFactor
|
||||
onZoomChanged?.invoke(lastZoom + -currentZoom + 1)
|
||||
}
|
||||
|
||||
return super.onScale(detector)
|
||||
}
|
||||
|
||||
override fun onScaleEnd(detector: ScaleGestureDetector) {
|
||||
super.onScaleEnd(detector)
|
||||
|
||||
lastZoom += -currentZoom + 1
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
* Code mostly stolen from https://developer.android.com/training/gestures/scale#drag
|
||||
*/
|
||||
fun onTouchEvent(ev: MotionEvent): Boolean {
|
||||
|
||||
if (zoomEnabled) {
|
||||
scaleGestureDetector.onTouchEvent(ev)
|
||||
}
|
||||
|
||||
when (ev.actionMasked) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
onToggleScroll?.invoke(false)
|
||||
ev.actionIndex.also { pointerIndex ->
|
||||
// Remember where we started (for dragging)
|
||||
lastTouchX = ev.getX(pointerIndex)
|
||||
lastTouchY = ev.getY(pointerIndex)
|
||||
}
|
||||
|
||||
// Save the ID of this pointer (for dragging)
|
||||
activePointerId = ev.getPointerId(0)
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
// Find the index of the active pointer and fetch its position
|
||||
val (x: Float, y: Float) =
|
||||
ev.findPointerIndex(activePointerId).let { pointerIndex ->
|
||||
// Calculate the distance moved
|
||||
ev.getX(pointerIndex) to ev.getY(pointerIndex)
|
||||
}
|
||||
|
||||
posX += x - lastTouchX
|
||||
posY += y - lastTouchY
|
||||
|
||||
if (scrollEnabled) {
|
||||
onChartMoved?.invoke(-posX, posY)
|
||||
}
|
||||
|
||||
// Remember this touch position for the next move event
|
||||
lastTouchX = x
|
||||
lastTouchY = y
|
||||
}
|
||||
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
|
||||
onToggleScroll?.invoke(true)
|
||||
activePointerId = INVALID_POINTER_ID
|
||||
}
|
||||
MotionEvent.ACTION_POINTER_UP -> {
|
||||
onToggleScroll?.invoke(true)
|
||||
ev.actionIndex.also { pointerIndex ->
|
||||
ev.getPointerId(pointerIndex)
|
||||
.takeIf { it == activePointerId }
|
||||
?.run {
|
||||
// This was our active pointer going up. Choose a new
|
||||
// active pointer and adjust accordingly.
|
||||
val newPointerIndex = if (pointerIndex == 0) 1 else 0
|
||||
lastTouchX = ev.getX(newPointerIndex)
|
||||
lastTouchY = ev.getY(newPointerIndex)
|
||||
activePointerId = ev.getPointerId(newPointerIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private var onToggleScroll: ((Boolean) -> Unit)? = null
|
||||
|
||||
/**
|
||||
* @param ev if input is false disable scroll
|
||||
*/
|
||||
fun setOnToggleScroll(ev: (Boolean) -> Unit) {
|
||||
onToggleScroll = ev
|
||||
}
|
||||
}
|
125
library/src/main/java/com/dzeio/charts/series/BarSerie.kt
Normal file
125
library/src/main/java/com/dzeio/charts/series/BarSerie.kt
Normal file
@ -0,0 +1,125 @@
|
||||
package com.dzeio.charts.series
|
||||
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
import android.graphics.Rect
|
||||
import android.graphics.RectF
|
||||
import android.util.Log
|
||||
import com.dzeio.charts.ChartView
|
||||
import com.dzeio.charts.utils.drawRoundRect
|
||||
|
||||
class BarSerie(
|
||||
private val view: ChartView
|
||||
) : BaseSerie(view) {
|
||||
|
||||
private companion object {
|
||||
const val TAG = "Charts/BarSerie"
|
||||
}
|
||||
|
||||
init {
|
||||
view.series.add(this)
|
||||
}
|
||||
|
||||
val barPaint = Paint().apply {
|
||||
isAntiAlias = true
|
||||
color = Color.parseColor("#123456")
|
||||
}
|
||||
|
||||
val textPaint = Paint().apply {
|
||||
isAntiAlias = true
|
||||
color = Color.parseColor("#FC496D")
|
||||
textSize = 30f
|
||||
textAlign = Paint.Align.CENTER
|
||||
}
|
||||
|
||||
private val rect = Rect()
|
||||
|
||||
override fun onDraw(canvas: Canvas, drawableSpace: RectF) {
|
||||
val displayedEntries = getDisplayedEntries()
|
||||
val barWidth = view.xAxis.getEntryWidth(drawableSpace).toFloat()
|
||||
val max = view.yAxis.getYMax()
|
||||
val min = view.yAxis.getYMin()
|
||||
|
||||
// Log.d(TAG, "${space.left}, ${space.right}")
|
||||
|
||||
for (entry in displayedEntries) {
|
||||
// calculated height in percent from 0 to 100
|
||||
val top = (1 - entry.y / max) * drawableSpace.height() + drawableSpace.top
|
||||
var posX = drawableSpace.left + view.xAxis.getPositionOnRect(
|
||||
entry,
|
||||
drawableSpace
|
||||
).toFloat()
|
||||
|
||||
val right = (posX + barWidth).coerceAtMost(drawableSpace.right)
|
||||
|
||||
if (posX > right) {
|
||||
continue
|
||||
} else if (posX < drawableSpace.left) {
|
||||
posX = drawableSpace.left
|
||||
}
|
||||
|
||||
if (right < drawableSpace.left) {
|
||||
continue
|
||||
}
|
||||
|
||||
// handle color recoloration
|
||||
val paint = Paint(barPaint)
|
||||
|
||||
if (entry.color != null) {
|
||||
paint.color = entry.color!!
|
||||
}
|
||||
|
||||
canvas.drawRoundRect(
|
||||
posX,
|
||||
top,
|
||||
right,
|
||||
drawableSpace.bottom,
|
||||
// 8f, 8f,
|
||||
32f,
|
||||
32f,
|
||||
0f,
|
||||
0f,
|
||||
paint
|
||||
)
|
||||
|
||||
// handle text display
|
||||
val text = view.yAxis.onValueFormat(entry.y)
|
||||
|
||||
textPaint.getTextBounds(text, 0, text.length, rect)
|
||||
|
||||
val textLeft = (posX + barWidth / 2)
|
||||
|
||||
if (
|
||||
// handle right side
|
||||
textLeft + rect.width() / 2 > right ||
|
||||
// handle left sie
|
||||
textLeft - rect.width() / 2 < drawableSpace.left
|
||||
) {
|
||||
continue
|
||||
}
|
||||
|
||||
val doDisplayIn =
|
||||
rect.height() < drawableSpace.bottom - top &&
|
||||
rect.width() < barWidth
|
||||
|
||||
var textY = if (doDisplayIn) top + rect.height() + 16f else top - 16f
|
||||
|
||||
if (textY < 0) {
|
||||
textY = drawableSpace.top + rect.height()
|
||||
}
|
||||
|
||||
|
||||
canvas.drawText(
|
||||
text,
|
||||
textLeft,
|
||||
textY,
|
||||
textPaint
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun refresh() {
|
||||
// TODO("Not yet implemented")
|
||||
}
|
||||
}
|
160
library/src/main/java/com/dzeio/charts/series/BarSerie.kt.old
Normal file
160
library/src/main/java/com/dzeio/charts/series/BarSerie.kt.old
Normal file
@ -0,0 +1,160 @@
|
||||
package com.dzeio.charts.series
|
||||
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Paint
|
||||
import android.graphics.Rect
|
||||
import android.graphics.RectF
|
||||
import android.util.Log
|
||||
import kotlin.math.max
|
||||
|
||||
class BarSerie : SerieAbstract() {
|
||||
|
||||
companion object {
|
||||
const val TAG = "DzeioCharts/BarSerie"
|
||||
}
|
||||
|
||||
var spacing: Float = 8f
|
||||
|
||||
/**
|
||||
* Values displayed on the grapd
|
||||
*/
|
||||
var displayedDatas = arrayListOf<Float>()
|
||||
|
||||
/**
|
||||
* Target values
|
||||
*/
|
||||
var targetDatas = arrayListOf<Float>()
|
||||
|
||||
var targetPercentList = arrayListOf<Float>()
|
||||
var percentList = arrayListOf<Float>()
|
||||
|
||||
var previousRefresh = 0
|
||||
|
||||
private var fgPaint: Paint = Paint().apply {
|
||||
isAntiAlias = true
|
||||
}
|
||||
|
||||
private val r = Rect()
|
||||
|
||||
override fun onUpdate(): Boolean {
|
||||
var needNewFrame = false
|
||||
for (i in targetPercentList.indices) {
|
||||
val value = view.animation.updateValue(
|
||||
1f,
|
||||
targetPercentList[i],
|
||||
percentList[i],
|
||||
0f,
|
||||
0.00f
|
||||
)
|
||||
|
||||
if (value != percentList[i]) {
|
||||
needNewFrame = true
|
||||
percentList[i] = value
|
||||
}
|
||||
}
|
||||
return needNewFrame
|
||||
}
|
||||
|
||||
override fun prepareData() {
|
||||
val max: Float = if (view.yAxis.max != null) view.yAxis.max!! else {
|
||||
getYMax(true)
|
||||
}
|
||||
|
||||
targetPercentList = arrayListOf()
|
||||
|
||||
// Log.d(TAG, "offset: ${view.getXOffset()}, displayed: ${view.getDisplayedEntries()}")
|
||||
for (item in getDisplayedEntries()) {
|
||||
// // // Process bottom texts
|
||||
// val text = view.xAxis.onValueFormat(item.x)
|
||||
// bottomTexts.add(text)
|
||||
//
|
||||
// // get Text boundaries
|
||||
// view.xAxis.labels.build().getTextBounds(text, 0, text.length, r)
|
||||
//
|
||||
// // get height of text
|
||||
// if (bottomTextHeight < r.height()) {
|
||||
// bottomTextHeight = r.height()
|
||||
// }
|
||||
//
|
||||
// // get text descent
|
||||
// val descent = abs(r.bottom)
|
||||
// if (bottomTextDescent < descent) {
|
||||
// bottomTextDescent = descent
|
||||
// }
|
||||
|
||||
// process values
|
||||
// Log.d(TAG, item.y.toString())
|
||||
|
||||
// add to animations the values
|
||||
targetPercentList.add(1 - item.y / max)
|
||||
}
|
||||
|
||||
// post list
|
||||
val offset = view.getXOffset()
|
||||
val movement = offset - previousRefresh
|
||||
Log.d(TAG, "$offset - $previousRefresh = $movement")
|
||||
if (movement != 0) {
|
||||
previousRefresh = offset
|
||||
}
|
||||
// if (movement != 0) {
|
||||
// Log.d(TAG, movement.toString())
|
||||
// }
|
||||
if (movement >= 1) {
|
||||
percentList = percentList.subList(1, percentList.size).toCollection(ArrayList())
|
||||
percentList.add(1f)
|
||||
} else if (movement <= -1) {
|
||||
percentList = percentList.subList(0, percentList.size - 1).toCollection(ArrayList())
|
||||
percentList.add(0, 1f)
|
||||
}
|
||||
|
||||
if (percentList.isEmpty() || percentList.size < targetPercentList.size) {
|
||||
val temp = targetPercentList.size - percentList.size
|
||||
for (i in 0 until temp) {
|
||||
percentList.add(1f)
|
||||
}
|
||||
} else if (percentList.size > targetPercentList.size) {
|
||||
val temp = percentList.size - targetPercentList.size
|
||||
for (i in 0 until temp) {
|
||||
percentList.removeAt(percentList.size - 1)
|
||||
}
|
||||
}
|
||||
|
||||
fgPaint.color = view.yAxis.color
|
||||
}
|
||||
|
||||
override fun displayData(canvas: Canvas, rect: RectF) {
|
||||
val barWidth = (rect.width() - view.padding * 2) / view.getDisplayedEntries() - spacing
|
||||
|
||||
if (percentList.isNotEmpty()) {
|
||||
// draw each rectangles
|
||||
for (i in 1..percentList.size) {
|
||||
// Log.d(TAG, percentList[i - 1].toString())
|
||||
val left = rect.left + spacing * i + barWidth * (i - 1).toFloat() + view.padding
|
||||
// Log.d(TAG, "$spacing, $i, $barWidth = $left")
|
||||
val right = rect.left + (spacing + barWidth) * i.toFloat()
|
||||
val bottom = rect.top + rect.height() - view.padding
|
||||
val top = (bottom - rect.top) * percentList[i - 1] + view.padding
|
||||
|
||||
// create rounded rect
|
||||
canvas.drawRoundRect(left, top, right, bottom, 8f, 8f, fgPaint)
|
||||
// remove the bottom corners DUH
|
||||
canvas.drawRect(left, max(top, bottom - 8f), right, bottom, fgPaint)
|
||||
val targetTop = (bottom - rect.top) * targetPercentList[i - 1]
|
||||
|
||||
val text = view.yAxis.onValueFormat(getYMax(true) - getYMax(true) * targetPercentList[i - 1], true)
|
||||
view.xAxis.labels.build().getTextBounds(text, 0, text.length, r)
|
||||
val doDisplayIn =
|
||||
r.width() + 10f < barWidth && bottom - targetTop > r.height() + 40f
|
||||
if (view.debug || !doDisplayIn || (doDisplayIn && bottom - top > r.height() + 40f)) {
|
||||
val y = if (doDisplayIn) top + r.height() + 20f else top - r.height()
|
||||
canvas.drawText(
|
||||
text,
|
||||
left + (right - left) / 2,
|
||||
y,
|
||||
view.xAxis.labels.build()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
50
library/src/main/java/com/dzeio/charts/series/BaseSerie.kt
Normal file
50
library/src/main/java/com/dzeio/charts/series/BaseSerie.kt
Normal file
@ -0,0 +1,50 @@
|
||||
package com.dzeio.charts.series
|
||||
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.RectF
|
||||
import com.dzeio.charts.ChartViewInterface
|
||||
import com.dzeio.charts.Entry
|
||||
import com.dzeio.charts.axis.YAxisPosition
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
sealed class BaseSerie(
|
||||
private val view: ChartViewInterface
|
||||
) : SerieInterface {
|
||||
|
||||
private companion object {
|
||||
const val TAG = "Charts/BaseSerie"
|
||||
}
|
||||
|
||||
override var formatValue: (entry: Entry) -> String = { entry -> entry.y.roundToInt().toString()}
|
||||
|
||||
override var yAxisPosition: YAxisPosition = YAxisPosition.RIGHT
|
||||
|
||||
override var entries: ArrayList<Entry> = arrayListOf()
|
||||
|
||||
override fun getDisplayedEntries(): ArrayList<Entry> {
|
||||
val minX = view.xAxis.x
|
||||
val maxX = minX + view.xAxis.getDataWidth()
|
||||
|
||||
val result: ArrayList<Entry> = arrayListOf()
|
||||
|
||||
var lastIndex = -1
|
||||
for (i in 0 until entries.size) {
|
||||
val it = entries[i]
|
||||
if (it.x in minX..maxX) {
|
||||
if (result.size === 0 && i > 0) {
|
||||
result.add((entries[i - 1]))
|
||||
}
|
||||
lastIndex = i
|
||||
result.add(it)
|
||||
}
|
||||
}
|
||||
|
||||
if (lastIndex < entries.size - 1) {
|
||||
result.add(entries [lastIndex + 1])
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
abstract override fun onDraw(canvas: Canvas, drawableSpace: RectF)
|
||||
}
|
74
library/src/main/java/com/dzeio/charts/series/LineSerie.kt
Normal file
74
library/src/main/java/com/dzeio/charts/series/LineSerie.kt
Normal file
@ -0,0 +1,74 @@
|
||||
package com.dzeio.charts.series
|
||||
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
import android.graphics.RectF
|
||||
import com.dzeio.charts.ChartView
|
||||
|
||||
class LineSerie(
|
||||
private val view: ChartView
|
||||
) : BaseSerie(view) {
|
||||
|
||||
private companion object {
|
||||
const val TAG = "Charts/LineSerie"
|
||||
}
|
||||
|
||||
init {
|
||||
view.series.add(this)
|
||||
}
|
||||
|
||||
val linePaint = Paint().apply {
|
||||
isAntiAlias = true
|
||||
color = Color.parseColor("#123456")
|
||||
strokeWidth = 5f
|
||||
}
|
||||
|
||||
val textPaint = Paint().apply {
|
||||
isAntiAlias = true
|
||||
color = Color.parseColor("#FC496D")
|
||||
textSize = 30f
|
||||
textAlign = Paint.Align.CENTER
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas, drawableSpace: RectF) {
|
||||
val displayedEntries = getDisplayedEntries()
|
||||
displayedEntries.sortBy { it.x }
|
||||
val max = view.yAxis.getYMax()
|
||||
|
||||
var previousPosX: Float? = null
|
||||
var previousPosY: Float? = null
|
||||
|
||||
for (entry in displayedEntries) {
|
||||
// calculated height in percent from 0 to 100
|
||||
val top = (1 - entry.y / max) * drawableSpace.height() + drawableSpace.top
|
||||
val posX = (drawableSpace.left +
|
||||
view.xAxis.getPositionOnRect(entry, drawableSpace) +
|
||||
view.xAxis.getEntryWidth(drawableSpace) / 2f).toFloat()
|
||||
|
||||
// handle color recoloration
|
||||
val paint = Paint(linePaint)
|
||||
|
||||
if (entry.color != null) {
|
||||
paint.color = entry.color!!
|
||||
}
|
||||
|
||||
// draw smol point
|
||||
if (posX < drawableSpace.right) {
|
||||
canvas.drawCircle(posX, top, paint.strokeWidth, paint)
|
||||
}
|
||||
|
||||
// draw line
|
||||
if (previousPosX != null && previousPosY != null) {
|
||||
canvas.drawLine(previousPosX, previousPosY, posX, top, paint)
|
||||
|
||||
}
|
||||
previousPosX = posX
|
||||
previousPosY = top
|
||||
}
|
||||
}
|
||||
|
||||
override fun refresh() {
|
||||
// TODO("Not yet implemented")
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package com.dzeio.charts.series
|
||||
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.RectF
|
||||
import com.dzeio.charts.Entry
|
||||
import com.dzeio.charts.axis.YAxisPosition
|
||||
|
||||
sealed interface SerieInterface {
|
||||
|
||||
/**
|
||||
* location of the Y axis
|
||||
*/
|
||||
var yAxisPosition: YAxisPosition
|
||||
|
||||
/**
|
||||
* filter out out of display entries
|
||||
*
|
||||
* @return the list of entries displayed
|
||||
*/
|
||||
fun getDisplayedEntries(): ArrayList<Entry>
|
||||
|
||||
/**
|
||||
* set the entries for the list
|
||||
*/
|
||||
var entries: ArrayList<Entry>
|
||||
|
||||
/**
|
||||
* Change how the value is displayed for each elements
|
||||
*/
|
||||
var formatValue: (entry: Entry) -> String
|
||||
|
||||
|
||||
/**
|
||||
* function that display the graph
|
||||
*
|
||||
* @param canvas the canvas to draw on
|
||||
* @param drawableSpace the space you are allowed to draw on
|
||||
*/
|
||||
fun onDraw(canvas: Canvas, drawableSpace: RectF)
|
||||
|
||||
/**
|
||||
* run when manually refreshing the system
|
||||
*
|
||||
* this is where the pre-logic is handled to make [onDraw] quicker
|
||||
*/
|
||||
fun refresh()
|
||||
}
|
144
library/src/main/java/com/dzeio/charts/utils/CanvasUtils.kt
Normal file
144
library/src/main/java/com/dzeio/charts/utils/CanvasUtils.kt
Normal file
@ -0,0 +1,144 @@
|
||||
package com.dzeio.charts.utils
|
||||
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Paint
|
||||
import android.graphics.RectF
|
||||
import kotlin.math.sqrt
|
||||
|
||||
/**
|
||||
* draw a dotted line
|
||||
*/
|
||||
fun Canvas.drawDottedLine(
|
||||
startX: Float,
|
||||
startY: Float,
|
||||
endX: Float,
|
||||
endY: Float,
|
||||
spacing: Float,
|
||||
paint: Paint
|
||||
) {
|
||||
//calculate line length
|
||||
val length = if (endX - startX == 0f) {
|
||||
// just length of Y
|
||||
endY - startY
|
||||
} else if (endY - startY == 0f) {
|
||||
// just length of X
|
||||
endX - startX
|
||||
} else {
|
||||
// calculate using the Pythagorean theorem
|
||||
sqrt((startX + endX) * (startX + endX) + (startY + endY) * (startY + endY))
|
||||
}
|
||||
|
||||
val lineCount = (length / spacing).toInt()
|
||||
|
||||
val lenX = endX - startX
|
||||
val lenY = endY - startY
|
||||
|
||||
// Log.d("DrawDottedLine", "----------- Start -----------")
|
||||
// Log.d("DrawDottedLine", "lenX: $lenX, lenY: $lenY")
|
||||
for (line in 0 until lineCount) {
|
||||
if (line % 2 == 0) {
|
||||
continue
|
||||
}
|
||||
|
||||
val sx = lenX / lineCount * line + startX
|
||||
val sy = lenY / lineCount * line + startY
|
||||
val ex = lenX / lineCount * (line + 1) + startX
|
||||
val ey = lenY / lineCount * (line + 1) + startY
|
||||
// Log.d("DrawDottedLine", "$sx, $sy, $ex, $ey")
|
||||
this.drawLine(sx, sy, ex, ey, paint)
|
||||
// line
|
||||
// total line startX, endX, startY, endY
|
||||
// total line length
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A more customizable drawRoundRect function
|
||||
*/
|
||||
fun Canvas.drawRoundRect(
|
||||
left: Float,
|
||||
top: Float,
|
||||
right: Float,
|
||||
bottom: Float,
|
||||
topLeft: Float,
|
||||
topRight: Float,
|
||||
bottomLeft: Float,
|
||||
bottomRight: Float,
|
||||
paint: Paint
|
||||
) {
|
||||
val maxRound = arrayOf(topLeft, topRight, bottomLeft, bottomRight).maxOf { it }
|
||||
val width = right - left
|
||||
val height = bottom - top
|
||||
|
||||
// draw first/global rect
|
||||
drawRoundRect(left, top, right, bottom, maxRound, maxRound, paint)
|
||||
|
||||
// top left border
|
||||
if (topLeft == 0f) {
|
||||
drawRect(left, top, left + width / 2, top + height / 2, paint)
|
||||
} else {
|
||||
drawRoundRect(left, top, left + width / 2, top + height / 2, topLeft, topLeft, paint)
|
||||
}
|
||||
|
||||
// top right border
|
||||
if (topRight == 0f) {
|
||||
drawRect(right - width / 2, top, right, top + height / 2, paint)
|
||||
} else {
|
||||
drawRoundRect(right - width / 2, top, right, top + height / 2, topRight, topRight, paint)
|
||||
}
|
||||
|
||||
// bottom left border
|
||||
if (bottomLeft == 0f) {
|
||||
drawRect(left, bottom - height / 2, left + width / 2, bottom, paint)
|
||||
} else {
|
||||
drawRoundRect(
|
||||
left,
|
||||
bottom - height / 2,
|
||||
left + width / 2,
|
||||
bottom,
|
||||
bottomLeft,
|
||||
bottomLeft,
|
||||
paint
|
||||
)
|
||||
}
|
||||
|
||||
// bottom right border
|
||||
if (bottomRight == 0f) {
|
||||
drawRect(right - width / 2, bottom - height / 2, right, bottom, paint)
|
||||
} else {
|
||||
drawRoundRect(
|
||||
right - width / 2,
|
||||
bottom - height / 2,
|
||||
right,
|
||||
bottom,
|
||||
bottomRight,
|
||||
bottomRight,
|
||||
paint
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A more customizable drawRoundRect function
|
||||
*/
|
||||
fun Canvas.drawRoundRect(
|
||||
rect: RectF,
|
||||
topLeft: Float,
|
||||
topRight: Float,
|
||||
bottomLeft: Float,
|
||||
bottomRight: Float,
|
||||
paint: Paint
|
||||
) {
|
||||
drawRoundRect(
|
||||
rect.left,
|
||||
rect.top,
|
||||
rect.right,
|
||||
rect.bottom,
|
||||
topLeft,
|
||||
topRight,
|
||||
bottomLeft,
|
||||
bottomRight,
|
||||
paint
|
||||
)
|
||||
}
|
17
library/src/test/java/com/dzeio/charts/ExampleUnitTest.kt
Normal file
17
library/src/test/java/com/dzeio/charts/ExampleUnitTest.kt
Normal file
@ -0,0 +1,17 @@
|
||||
package com.dzeio.charts
|
||||
|
||||
import org.junit.Test
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
class ExampleUnitTest {
|
||||
@Test
|
||||
fun addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2)
|
||||
}
|
||||
}
|
8
local.properties
Normal file
8
local.properties
Normal file
@ -0,0 +1,8 @@
|
||||
## This file must *NOT* be checked into Version Control Systems,
|
||||
# as it contains information specific to your local configuration.
|
||||
#
|
||||
# Location of the SDK. This is only used by Gradle.
|
||||
# For customization when using a Version Control System, please read the
|
||||
# header note.
|
||||
#Tue Jan 10 11:53:10 CET 2023
|
||||
sdk.dir=/run/media/avior/DATA/Android/SDK
|
1
sample/.gitignore
vendored
Normal file
1
sample/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/build
|
49
sample/build.gradle.kts
Normal file
49
sample/build.gradle.kts
Normal file
@ -0,0 +1,49 @@
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
kotlin("android")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.dzeio.chartstest"
|
||||
compileSdk = 33
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "com.dzeio.chartstest"
|
||||
minSdk = 21
|
||||
targetSdk = 33
|
||||
versionCode = 1
|
||||
versionName = "1.0"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = true
|
||||
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "11"
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
viewBinding = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation(project(":library"))
|
||||
|
||||
// Material Design
|
||||
implementation("com.google.android.material:material:1.7.0")
|
||||
|
||||
// Navigation because I don't want to maintain basic transactions and shit
|
||||
implementation("androidx.navigation:navigation-fragment-ktx:2.5.3")
|
||||
}
|
21
sample/proguard-rules.pro
vendored
Normal file
21
sample/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
19
sample/src/main/AndroidManifest.xml
Normal file
19
sample/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/Theme.Charts">
|
||||
<activity
|
||||
android:name=".ui.MainActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
19
sample/src/main/java/com/dzeio/chartstest/ui/MainActivity.kt
Normal file
19
sample/src/main/java/com/dzeio/chartstest/ui/MainActivity.kt
Normal file
@ -0,0 +1,19 @@
|
||||
package com.dzeio.chartstest.ui
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.WindowCompat
|
||||
import com.dzeio.chartstest.databinding.ActivityMainBinding
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var binding: ActivityMainBinding
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
}
|
||||
}
|
166
sample/src/main/java/com/dzeio/chartstest/ui/MainFragment.kt
Normal file
166
sample/src/main/java/com/dzeio/chartstest/ui/MainFragment.kt
Normal file
@ -0,0 +1,166 @@
|
||||
package com.dzeio.chartstest.ui
|
||||
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.dzeio.charts.ChartView
|
||||
import com.dzeio.charts.Entry
|
||||
import com.dzeio.charts.series.BarSerie
|
||||
import com.dzeio.charts.series.LineSerie
|
||||
import com.dzeio.chartstest.databinding.FragmentMainBinding
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import kotlin.random.Random
|
||||
|
||||
class MainFragment : Fragment() {
|
||||
private var _binding: FragmentMainBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
_binding = FragmentMainBinding.inflate(inflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
binding.chart1.apply {
|
||||
// setup the Serie
|
||||
val serie = BarSerie(this)
|
||||
|
||||
// utils function to use Material3 auto colors
|
||||
materielTheme(this, requireView())
|
||||
|
||||
// give the serie it's entries
|
||||
serie.entries = generateRandomDataset(10)
|
||||
|
||||
// refresh the Chart
|
||||
refresh()
|
||||
}
|
||||
|
||||
binding.chart2.apply {
|
||||
// setup the Serie
|
||||
val serie = LineSerie(this)
|
||||
|
||||
// utils function to use Material3 auto colors
|
||||
materielTheme(this, requireView())
|
||||
|
||||
// give the serie its entries
|
||||
serie.entries = generateRandomDataset(10)
|
||||
|
||||
// refresh the Chart
|
||||
refresh()
|
||||
}
|
||||
|
||||
binding.chart3.apply {
|
||||
// setup the Series
|
||||
val serie1 = BarSerie(this)
|
||||
val serie2 = LineSerie(this)
|
||||
|
||||
// utils function to use Material3 auto colors
|
||||
materielTheme(this, requireView())
|
||||
|
||||
// give the series their entries
|
||||
serie2.entries = generateRandomDataset(20)
|
||||
serie1.entries = generateRandomDataset(20).apply {
|
||||
for (idx in 0 until size) {
|
||||
val compared = serie2.entries[idx]
|
||||
val toCompare = this[idx]
|
||||
if (compared.y > toCompare.y) {
|
||||
toCompare.color = Color.RED
|
||||
} else {
|
||||
toCompare.color = Color.GREEN
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// make the lineSerie red
|
||||
serie2.linePaint.color = Color.WHITE
|
||||
|
||||
// refresh the Chart
|
||||
refresh()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a random dataset
|
||||
*/
|
||||
private fun generateRandomDataset(size: Int = 100): ArrayList<Entry> {
|
||||
val dataset: ArrayList<Entry> = arrayListOf()
|
||||
|
||||
for (i in 0 until size) {
|
||||
dataset.add(Entry(
|
||||
i.toDouble(),
|
||||
Random.nextInt(0, 100).toFloat()
|
||||
))
|
||||
}
|
||||
|
||||
return dataset
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply Material3 theme to a [ChartView]
|
||||
*/
|
||||
private fun materielTheme(chart: ChartView, view: View) {
|
||||
|
||||
chart.apply {
|
||||
yAxis.apply {
|
||||
textLabel.color = MaterialColors.getColor(
|
||||
view,
|
||||
com.google.android.material.R.attr.colorOnPrimaryContainer
|
||||
)
|
||||
linePaint.color = MaterialColors.getColor(
|
||||
view,
|
||||
com.google.android.material.R.attr.colorOnPrimaryContainer
|
||||
)
|
||||
goalLinePaint.color = MaterialColors.getColor(
|
||||
view,
|
||||
com.google.android.material.R.attr.colorError
|
||||
)
|
||||
}
|
||||
|
||||
xAxis.apply {
|
||||
textPaint.color = MaterialColors.getColor(
|
||||
view,
|
||||
com.google.android.material.R.attr.colorOnPrimaryContainer
|
||||
)
|
||||
}
|
||||
|
||||
for (serie in series) {
|
||||
if (serie is BarSerie) {
|
||||
serie.apply {
|
||||
barPaint.color = MaterialColors.getColor(
|
||||
view,
|
||||
com.google.android.material.R.attr.colorPrimary
|
||||
)
|
||||
textPaint.color = MaterialColors.getColor(
|
||||
view,
|
||||
com.google.android.material.R.attr.colorOnPrimary
|
||||
)
|
||||
}
|
||||
} else if (serie is LineSerie) {
|
||||
serie.apply {
|
||||
linePaint.color = MaterialColors.getColor(
|
||||
view,
|
||||
com.google.android.material.R.attr.colorPrimary
|
||||
)
|
||||
textPaint.color = MaterialColors.getColor(
|
||||
view,
|
||||
com.google.android.material.R.attr.colorOnPrimary
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
30
sample/src/main/res/drawable-v24/ic_launcher_foreground.xml
Normal file
30
sample/src/main/res/drawable-v24/ic_launcher_foreground.xml
Normal file
@ -0,0 +1,30 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="85.84757"
|
||||
android:endY="92.4963"
|
||||
android:startX="42.9492"
|
||||
android:startY="49.59793"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#44000000"
|
||||
android:offset="0.0" />
|
||||
<item
|
||||
android:color="#00000000"
|
||||
android:offset="1.0" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="#00000000" />
|
||||
</vector>
|
170
sample/src/main/res/drawable/ic_launcher_background.xml
Normal file
170
sample/src/main/res/drawable/ic_launcher_background.xml
Normal file
@ -0,0 +1,170 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#3DDC84"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M9,0L9,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,0L19,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,0L29,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,0L39,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,0L49,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,0L59,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,0L69,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,0L79,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M89,0L89,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M99,0L99,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,9L108,9"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,19L108,19"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,29L108,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,39L108,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,49L108,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,59L108,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,69L108,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,79L108,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,89L108,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,99L108,99"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,29L89,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,39L89,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,49L89,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,59L89,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,69L89,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,79L89,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,19L29,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,19L39,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,19L49,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,19L59,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,19L69,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,19L79,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
</vector>
|
23
sample/src/main/res/layout/activity_main.xml
Normal file
23
sample/src/main/res/layout/activity_main.xml
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
tools:context=".ui.MainActivity">
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/fragment"
|
||||
android:name="androidx.navigation.fragment.NavHostFragment"
|
||||
android:layout_marginTop="?attr/actionBarSize"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:defaultNavHost="true"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:navGraph="@navigation/nav_graph" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
40
sample/src/main/res/layout/fragment_main.xml
Normal file
40
sample/src/main/res/layout/fragment_main.xml
Normal file
@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".ui.MainFragment">
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp">
|
||||
|
||||
<com.dzeio.charts.ChartView
|
||||
android:id="@+id/chart1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="200dp" />
|
||||
|
||||
<com.dzeio.charts.ChartView
|
||||
android:id="@+id/chart2"
|
||||
android:layout_marginVertical="16dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="200dp" />
|
||||
|
||||
<com.dzeio.charts.ChartView
|
||||
android:id="@+id/chart3"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="200dp" />
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
5
sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
Normal file
5
sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
BIN
sample/src/main/res/mipmap-hdpi/ic_launcher.webp
Normal file
BIN
sample/src/main/res/mipmap-hdpi/ic_launcher.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
BIN
sample/src/main/res/mipmap-mdpi/ic_launcher.webp
Normal file
BIN
sample/src/main/res/mipmap-mdpi/ic_launcher.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 982 B |
BIN
sample/src/main/res/mipmap-xhdpi/ic_launcher.webp
Normal file
BIN
sample/src/main/res/mipmap-xhdpi/ic_launcher.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.9 KiB |
BIN
sample/src/main/res/mipmap-xxhdpi/ic_launcher.webp
Normal file
BIN
sample/src/main/res/mipmap-xxhdpi/ic_launcher.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.8 KiB |
BIN
sample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
Normal file
BIN
sample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.8 KiB |
12
sample/src/main/res/navigation/nav_graph.xml
Normal file
12
sample/src/main/res/navigation/nav_graph.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/nav_graph"
|
||||
app:startDestination="@id/MainFragment">
|
||||
|
||||
<fragment
|
||||
android:id="@+id/MainFragment"
|
||||
android:name="com.dzeio.chartstest.ui.MainFragment"
|
||||
tools:layout="@layout/fragment_main"/>
|
||||
</navigation>
|
3
sample/src/main/res/values/strings.xml
Normal file
3
sample/src/main/res/values/strings.xml
Normal file
@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<string name="app_name" translatable="false">Dzeio Charts</string>
|
||||
</resources>
|
4
sample/src/main/res/values/themes.xml
Normal file
4
sample/src/main/res/values/themes.xml
Normal file
@ -0,0 +1,4 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<style name="Theme.Charts" parent="Theme.Material3.DynamicColors.DayNight" />
|
||||
</resources>
|
19
settings.gradle
Normal file
19
settings.gradle
Normal file
@ -0,0 +1,19 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
dependencyResolutionManagement {
|
||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "Dzeio Charts"
|
||||
|
||||
include ':sample'
|
||||
include ':library'
|
Reference in New Issue
Block a user