Showing preview only (241K chars total). Download the full file or copy to clipboard to get everything.
Repository: vlad1m1r990/Lemniscate
Branch: master
Commit: 1e28235f6f4c
Files: 106
Total size: 209.9 KB
Directory structure:
gitextract_ucgiast4/
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── build.gradle
├── buildSrc/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ └── main/
│ └── java/
│ └── Dependencies.kt
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── lemniscate/
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── vlad1m1r/
│ │ │ └── lemniscate/
│ │ │ ├── BernoullisBowProgressView.kt
│ │ │ ├── BernoullisProgressView.kt
│ │ │ ├── BernoullisSharpProgressView.kt
│ │ │ ├── GeronosProgressView.kt
│ │ │ ├── base/
│ │ │ │ ├── BaseCurveContract.kt
│ │ │ │ ├── BaseCurvePresenter.kt
│ │ │ │ ├── BaseCurveProgressView.kt
│ │ │ │ ├── models/
│ │ │ │ │ ├── DrawState.kt
│ │ │ │ │ ├── LineLength.kt
│ │ │ │ │ ├── Point.kt
│ │ │ │ │ ├── Points.kt
│ │ │ │ │ └── ViewSize.kt
│ │ │ │ └── settings/
│ │ │ │ ├── AnimationSettings.kt
│ │ │ │ └── CurveSettings.kt
│ │ │ ├── funny/
│ │ │ │ ├── CannabisProgressView.kt
│ │ │ │ └── HeartProgressView.kt
│ │ │ ├── other/
│ │ │ │ └── XProgressView.kt
│ │ │ ├── roulette/
│ │ │ │ ├── BaseRouletteProgressView.kt
│ │ │ │ ├── EpitrochoidProgressView.kt
│ │ │ │ ├── HypotrochoidProgressView.kt
│ │ │ │ ├── scribble/
│ │ │ │ │ ├── RoundScribbleProgressView.kt
│ │ │ │ │ └── ScribbleProgressView.kt
│ │ │ │ └── settings/
│ │ │ │ └── RouletteCurveSettings.kt
│ │ │ └── utils/
│ │ │ └── CurveUtils.kt
│ │ └── res/
│ │ └── values/
│ │ ├── attrs.xml
│ │ └── dimens.xml
│ └── test/
│ ├── java/
│ │ └── com/
│ │ └── vlad1m1r/
│ │ └── lemniscate/
│ │ ├── BernoullisBowProgressViewTest.kt
│ │ ├── BernoullisProgressViewTest.kt
│ │ ├── BernoullisSharpProgressViewTest.kt
│ │ ├── GeronosProgressViewTest.kt
│ │ ├── base/
│ │ │ ├── BaseCurvePresenterTest.kt
│ │ │ ├── BaseCurveProgressViewTest.kt
│ │ │ ├── BaseProgressViewAttributesTest.kt
│ │ │ ├── models/
│ │ │ │ ├── DrawStateTest.kt
│ │ │ │ ├── LineLengthParcelableTest.kt
│ │ │ │ ├── LineLengthTest.kt
│ │ │ │ ├── PointTest.kt
│ │ │ │ └── PointsTest.kt
│ │ │ └── settings/
│ │ │ ├── AnimationSettingsParcelableTest.kt
│ │ │ ├── CurveSettingsParcelableTest.kt
│ │ │ └── CurveSettingsTest.kt
│ │ ├── funny/
│ │ │ ├── CannabisProgressViewTest.kt
│ │ │ └── HeartProgressViewTest.kt
│ │ ├── other/
│ │ │ └── XProgressViewTest.kt
│ │ ├── roulette/
│ │ │ ├── BaseRouletteProgressViewAttributesTest.kt
│ │ │ ├── EpitrochoidProgressViewTest.kt
│ │ │ ├── HypotrochoidProgressViewTest.kt
│ │ │ ├── scribble/
│ │ │ │ ├── RoundScribbleProgressViewTest.kt
│ │ │ │ └── ScribbleProgressViewTest.kt
│ │ │ └── settings/
│ │ │ └── RouletteCurveSettingsParcelableTest.kt
│ │ ├── testutils/
│ │ │ ├── CurveTestUtils.kt
│ │ │ ├── EqualUtils.kt
│ │ │ ├── TestConstants.kt
│ │ │ └── TestLayoutInflater.kt
│ │ └── utils/
│ │ └── CurveUtilsTest.kt
│ └── resources/
│ └── mockito-extensions/
│ └── org.mockito.plugins.MockMaker
├── remote_data/
│ └── legal/
│ ├── privacy_policy.html
│ └── terms_and_conditions.html
├── sample/
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ ├── java/
│ │ └── com/
│ │ └── vlad1m1r/
│ │ └── lemniscate/
│ │ └── sample/
│ │ ├── CurveData.kt
│ │ ├── FragmentCurve.kt
│ │ ├── FragmentSettings.kt
│ │ ├── MainActivity.kt
│ │ └── PresentationActivity.kt
│ └── res/
│ ├── drawable/
│ │ ├── indicator.xml
│ │ ├── indicator_selected.xml
│ │ └── shadow.xml
│ ├── drawable-v26/
│ │ ├── ic_launcher_background.xml
│ │ └── ic_launcher_foreground.xml
│ ├── layout/
│ │ ├── activity_main.xml
│ │ ├── activity_presentation.xml
│ │ ├── fragment_curve.xml
│ │ ├── fragment_settings.xml
│ │ └── toolbar.xml
│ ├── layout-land/
│ │ ├── activity_main.xml
│ │ └── activity_presentation.xml
│ ├── menu/
│ │ └── menu_main_activity.xml
│ ├── mipmap-anydpi-v26/
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ ├── values/
│ │ ├── colors.xml
│ │ ├── constants.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── values-w820dp/
│ └── dimens.xml
└── settings.gradle
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# Built application files
*.apk
*.ap_
# Files for the ART/Dalvik VM
*.dex
# Java class files
*.class
# Generated files
bin/
gen/
out/
# Gradle files
.gradle/
build/
# Local configuration file (sdk path, etc)
local.properties
# Proguard folder generated by Eclipse
proguard/
# Log Files
*.log
# Android Studio Navigation editor temp files
.navigation/
# Android Studio captures folder
captures/
# Intellij
*.iml
.idea/workspace.xml
.idea/
# Keystore files
*.jks
================================================
FILE: CHANGELOG.md
================================================
Change Log
==========
Version 2.0.2 *(2019-07-27)*
----------------------------
* Fix `lineMinLength` setter
Version 2.0.1 *(2019-07-21)*
----------------------------
* Remove `android:allowBackup` and `android:supportsRtl` from manifest
* Update dependencies
* Change package from `com.vlad1m1r.lemniscate.sample.lemniscate` to `com.vlad1m1r.lemniscate`
Version 2.0.0 *(2019-03-23)*
----------------------------
* Migration to AndroidX
Version 1.4.5 *(2019-03-23)*
----------------------------
* Update dependencies
Version 1.4.4 *(2018-04-05)*
----------------------------
* Replace `View.BaseSavedState` with `android.support.v4.view.AbsSavedState` in `BaseCurveProgressView`
Version 1.4.3 *(2018-04-04)*
----------------------------
* Fixed problem with restoring view's state
* Small code optimization
Version 1.4.2 *(2018-01-16)*
----------------------------
* Fixed bug where View would not show inside ScrollView. [#5](https://github.com/VladimirWrites/Lemniscate/issues/5)
Version 1.4.1 *(2018-01-06)*
----------------------------
* Fixed bug where SizeMultiplier property was not working when set from `xml`. [#4](https://github.com/VladimirWrites/Lemniscate/issues/4)
Version 1.4.0 *(2017-11-09)*
----------------------------
* Project rewritten in Kotlin.
* Organization of base classes improved
* Fixed bugs in Sample app
Version 1.3.0 *(2017-11-03)*
----------------------------
* `lineLength` and `lineLengthChangeable` do not exist anymore. If `maxLineLength` and `minLineLength` are the same then `lineLengthChangeable==false`, otherwise line length will be changeable
`getGraphX` and `getGraphY` now return `float` and not `double`
* `mLemniscateParamX` and `mLemniscateParamY` are not used anymore and are replaced by `viewSize.getSize()`, where `mLemniscateParamX == mLemniscateParamY == viewSize.getSize()/2`
* `minSdkVersion` moved from 11 to 14
Version 1.2.0 *(2017-02-16)*
----------------------------
* New curves added: `BernoullisBowProgressView`, `BernoullisSharpProgressView`, `XProgressView`, `RoundScribbleProgressView`, `ScribbleProgressView`
* `colorAccent` is now being used as default line color
Version 1.1.1 *(2017-01-26)*
----------------------------
* Optimization of function that is doing sampling of curve
Version 1.1.0 *(2017-01-26)*
----------------------------
* Abstract functions `getGraphX()` and `getGraphY()` now receive value of `getT()`
Version 1.0.2 *(2017-01-24)*
----------------------------
* Fix: Added `onSaveState` for Roulette curves
* Fix: Precision is being saved `onSaveState` for all curves
Version 1.0.1 *(2017-01-23)*
----------------------------
* Fix: Crash on `setColor(int color)` in `BaseCurveProgressBar`, when called from constructor.
Version 1.0.0 *(2017-01-23)*
----------------------------
Initial release.
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
================================================
FILE: README.md
================================================

[](https://github.com/VladimirWrites/Lemniscate/blob/master/LICENSE)
[](https://jitpack.io/#VladimirWrites/Lemniscate)
[](https://android-arsenal.com/api?level-11)
[](https://android-arsenal.com/details/1/5142)
[](https://app.bitrise.io/app/a22f82dd1a84f058#/builds)
[](https://codecov.io/gh/VladimirWrites/Lemniscate)
-----
Lemniscate is a library that will help you to make your progress view nice and sleek.

Demo
-----
Demo application is available on Google Play.
<a href='https://play.google.com/store/apps/details?id=com.vlad1m1r.lemniscate.sample'>
<img alt='Get it on Google Play' src='http://i.imgur.com/tka3Exw.png'/>
</a>
The application is intentionally simple, without any libraries, to be understandable to more developers.
Setup
-----
Add to your module's `build.gradle`:
```groovy
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
```
and to your app `build.gradle`:
###### AndroidX
```groovy
dependencies {
implementation 'com.github.VladimirWrites:Lemniscate:2.0.4'
}
```
###### Android Support Library (Depricated)
```groovy
dependencies {
implementation 'com.github.VladimirWrites:Lemniscate:1.4.5'
}
```
Usage
-----
Example of usage:
```xml
<com.vlad1m1r.lemniscate.BernoullisProgressView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:duration="1000"
app:hasHole="false"
app:lineColor="@color/colorPrimary"
app:maxLineLength="0.8"
app:minLineLength="0.4"
app:sizeMultiplier="1"
app:strokeWidth="5dp"/>
```
###### Params available in all views:
* **duration** (int) - duration of one animation cycle in millisecondes
* **lineColor** (color) - color of the line
* **maxLineLength** (float) - max length of line (in percentage; 1.0 is full length, 0.5 is half of length)
* **minLineLength** (float) - min length of line (in percentage; 1.0 is full length, 0.5 is half of length)
* **sizeMultiplier** (float) - default size of view will be multiplied with that number
* **strokeWidth** (dimension) - width of line
* **precision** (int) - number of points in curve calculated in one cycle
#### Lemniscates
* `BernoullisProgressView` - [Lemniscate of Bernoulli](https://en.wikipedia.org/wiki/Lemniscate_of_Bernoulli),
* `GeronosProgressView` - [Lemniscate of Gerono](https://en.wikipedia.org/wiki/Lemniscate_of_Gerono)
* `BernoullisBowProgressView`
* `BernoullisSharpProgressView`
###### Additional params:
* **hasHole** (boolean) - hole in a middle of Lemniscates
#### Roulettes
* `EpitrochoidProgressView` - [Epitrochoid](https://en.wikipedia.org/wiki/Epitrochoid),
* `HypotrochoidProgressView` - [Hypotrochoid](https://en.wikipedia.org/wiki/Hypotrochoid)
###### Additional params:
* **radiusFixed** (float) - radius of fixed circle
* **radiusMoving** (float) - radius of moving circle
* **distanceFromCenter** (float) - distance from the center of the moving circle
* **numberOfCycles** (float) - for one **duration** curve will be drawn on interval [0, 2 \* mNumberOfCycles \* π]
#### Scribble
* `RoundScribbleProgressView`
* `ScribbleProgressView`
#### Funny
* `HeartProgressView` - [Heart Curve](http://mathworld.wolfram.com/HeartCurve.html),
* `CannabisProgressView` - [Cannabis Curve](http://mathworld.wolfram.com/CannabisCurve.html)
#### Other
* `XProgressView`
Contributing
-------
Want to contribute? You are welcome!
Note that all pull request should go to [`development`](https://github.com/VladimirWrites/Lemniscate/tree/development) branch.
Credits
-------
+ [Vladimir Jovanovic](https://github.com/VladimirWrites)
License
-------
Copyright 2016 Vladimir Jovanovic
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: build.gradle
================================================
buildscript {
repositories {
jcenter()
google()
}
dependencies {
classpath Deps.android_gradle_plugin
classpath Deps.kotlin_gradle_plugin
}
}
allprojects {
repositories {
jcenter()
google()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
================================================
FILE: buildSrc/.gitignore
================================================
/build
================================================
FILE: buildSrc/build.gradle.kts
================================================
/*
* Copyright 2017 Vladimir Jovanovic
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
plugins {
`kotlin-dsl`
}
repositories {
mavenCentral()
}
================================================
FILE: buildSrc/src/main/java/Dependencies.kt
================================================
object Versions {
const val kotlin = "2.0.20"
const val android_x = "1.7.0"
const val circleindicator = "2.1.6"
const val junit = "4.13.2"
const val mockito_core = "4.5.1"
const val mockito_kotlin = "4.0.0"
const val truth = "1.1.3"
const val robolectric = "4.7.3"
const val gradle_android = "8.6.0"
const val jacoco = "0.8.8"
const val min_sdk = 14
const val sample_min_sdk = 21
const val target_sdk = 35
const val compile_sdk = 35
const val lemniscate_version_code = 204
const val lemniscate_version_name = "2.0.4"
const val sample_version_code = 144
const val sample_version_name = "1.4.4"
}
object Deps {
const val kotlin_stdlib = "org.jetbrains.kotlin:kotlin-stdlib:${Versions.kotlin}"
const val appcompat = "androidx.appcompat:appcompat:${Versions.android_x}"
const val circleindicator = "me.relex:circleindicator:${Versions.circleindicator}"
const val junit = "junit:junit:${Versions.junit}"
const val mockito_core = "org.mockito:mockito-core:${Versions.mockito_core}"
const val mockito_kotlin = "org.mockito.kotlin:mockito-kotlin:${Versions.mockito_kotlin}"
const val truth = "com.google.truth:truth:${Versions.truth}"
const val robolectric = "org.robolectric:robolectric:${Versions.robolectric}"
const val android_gradle_plugin = "com.android.tools.build:gradle:${Versions.gradle_android}"
const val kotlin_gradle_plugin = "org.jetbrains.kotlin:kotlin-gradle-plugin:${Versions.kotlin}"
}
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
#Sat Sep 07 13:09:29 CEST 2024
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
================================================
FILE: gradle.properties
================================================
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# 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.
org.gradle.jvmargs=-Xmx1536m
android.useAndroidX=true
# 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
================================================
FILE: gradlew
================================================
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# 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
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# Attempt to set APP_HOME
# Resolve links: $0 may be radiusFixed 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
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" -radiusFixed "$darwin" = "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, switch paths to Windows format before running java
if $cygwin ; 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 distanceFromCenter 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add radiusFixed 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 radiusFixed condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((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
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
================================================
FILE: gradlew.bat
================================================
@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
@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=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@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 init
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 init
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
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
: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 %CMD_LINE_ARGS%
: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 /radiusMoving 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: lemniscate/.gitignore
================================================
/build
================================================
FILE: lemniscate/build.gradle
================================================
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'jacoco'
jacoco {
toolVersion = Versions.jacoco
}
android {
namespace "com.vlad1m1r.lemniscate"
defaultConfig {
minSdkVersion Versions.min_sdk
targetSdkVersion Versions.target_sdk
compileSdk Versions.compile_sdk
versionCode Versions.lemniscate_version_code
versionName Versions.lemniscate_version_name
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
debug {
testCoverageEnabled true
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
testOptions {
unitTests.all {
jacoco {
includeNoLocationClasses = true
}
}
unitTests {
includeAndroidResources = true
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_17.toString()
}
}
task jacocoDebugReport(type: JacocoReport, dependsOn: ['testDebugUnitTest']) {
reports {
csv {
enabled true
}
xml {
enabled true
}
html {
enabled true
}
}
def fileFilter = ['**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*', '**/*Test*.*', 'android/**/*.*']
def debugTree = fileTree(dir: "${buildDir}/intermediates/classes/debug", excludes: fileFilter)
def kotlinDebugTree = fileTree(dir: "${buildDir}/tmp/kotlin-classes/debug", excludes: fileFilter)
def mainSrc = "${project.projectDir}/src/androidTest/java"
getSourceDirectories().setFrom(files([mainSrc]))
getClassDirectories().setFrom(files([debugTree], [kotlinDebugTree]))
getExecutionData().setFrom(fileTree(dir: "$buildDir", includes: [
"jacoco/testDebugUnitTest.exec",
"outputs/code-coverage/connected/*coverage.ec"
]))
}
dependencies {
implementation Deps.appcompat
implementation Deps.kotlin_stdlib
testImplementation Deps.junit
testImplementation Deps.mockito_core
testImplementation Deps.mockito_kotlin
testImplementation Deps.truth
testImplementation Deps.robolectric
}
repositories {
mavenCentral()
}
================================================
FILE: lemniscate/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/vladimirjovanovic/Library/Android/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
================================================
FILE: lemniscate/src/main/AndroidManifest.xml
================================================
<manifest package="com.vlad1m1r.lemniscate"/>
================================================
FILE: lemniscate/src/main/java/com/vlad1m1r/lemniscate/BernoullisBowProgressView.kt
================================================
/*
* Copyright 2016 Vladimir Jovanovic
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.vlad1m1r.lemniscate
import android.content.Context
import android.util.AttributeSet
import com.vlad1m1r.lemniscate.base.BaseCurveProgressView
import kotlin.math.cos
import kotlin.math.pow
import kotlin.math.sin
class BernoullisBowProgressView : BaseCurveProgressView {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
override fun getGraphX(t: Float): Float =
size * 0.75f * cos(t) / (1 + cos(t).pow(6))
override fun getGraphY(t: Float): Float =
size * 0.75f * sin(t) * cos(t) / (1 + cos(t).pow(6))
}
================================================
FILE: lemniscate/src/main/java/com/vlad1m1r/lemniscate/BernoullisProgressView.kt
================================================
/*
* Copyright 2016 Vladimir Jovanovic
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.vlad1m1r.lemniscate
import android.content.Context
import android.util.AttributeSet
import com.vlad1m1r.lemniscate.base.BaseCurveProgressView
import kotlin.math.cos
import kotlin.math.pow
import kotlin.math.sin
class BernoullisProgressView : BaseCurveProgressView {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
override fun getGraphX(t: Float): Float =
size / 2 * cos(t) / (1 + sin(t).pow(2))
override fun getGraphY(t: Float): Float =
(size / 2) * sin(t) * cos(t) / (1 + sin(t).pow(2))
}
================================================
FILE: lemniscate/src/main/java/com/vlad1m1r/lemniscate/BernoullisSharpProgressView.kt
================================================
/*
* Copyright 2016 Vladimir Jovanovic
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.vlad1m1r.lemniscate
import android.content.Context
import android.util.AttributeSet
import com.vlad1m1r.lemniscate.base.BaseCurveProgressView
import kotlin.math.cos
import kotlin.math.pow
import kotlin.math.sin
class BernoullisSharpProgressView : BaseCurveProgressView {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
override fun getGraphX(t: Float): Float =
size * cos(t) / (1 + cos(t).pow(2))
override fun getGraphY(t: Float): Float =
size * sin(t) * cos(t) / (1 + cos(t).pow(2))
}
================================================
FILE: lemniscate/src/main/java/com/vlad1m1r/lemniscate/GeronosProgressView.kt
================================================
/*
* Copyright 2016 Vladimir Jovanovic
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.vlad1m1r.lemniscate
import android.content.Context
import android.util.AttributeSet
import com.vlad1m1r.lemniscate.base.BaseCurveProgressView
import kotlin.math.cos
import kotlin.math.sin
class GeronosProgressView : BaseCurveProgressView {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
override fun getGraphX(t: Float): Float =
size / 2 * sin(t)
override fun getGraphY(t: Float): Float =
(size / 2)* sin(t) * cos(t)
}
================================================
FILE: lemniscate/src/main/java/com/vlad1m1r/lemniscate/base/BaseCurveContract.kt
================================================
package com.vlad1m1r.lemniscate.base
import com.vlad1m1r.lemniscate.base.models.DrawState
import com.vlad1m1r.lemniscate.base.models.Points
import com.vlad1m1r.lemniscate.base.models.ViewSize
import com.vlad1m1r.lemniscate.base.settings.AnimationSettings
import com.vlad1m1r.lemniscate.base.settings.CurveSettings
import kotlin.math.PI
interface IBaseCurvePresenter {
val view:IBaseCurveView
var curveSettings: CurveSettings
val viewSize: ViewSize
var animationSettings: AnimationSettings
val drawState: DrawState
val points: Points
fun recreatePoints()
fun updateStartingPointOnCurve(point: Int)
}
interface IBaseCurveView {
/**
* This method should return values of x for t∈[0, upper limit of getT() function].
* We should use parametric representation of curve for x.
* Curve should be closed and periodic on interval that returns getT().
* Resulting value should satisfy x∈[-viewSize.getWidth()/2, viewSize.getWidth()/2].
*/
fun getGraphX(t: Float): Float
/**
* This method should return values of y for t∈[0, upper limit of getT() function].
* We should use parametric representation of curve for y.
* Curve should be closed and periodic on interval that returns getT().
* Resulting value should satisfy y∈[-viewSize.getHeight()/2, viewSize.getHeight()/2].
*/
fun getGraphY(t: Float): Float
/**
* @param i ∈ [0, mPrecision)
* @return function is putting i∈[0, curveSettings.getPrecision()) points between [0, 2π]
*/
fun getT(i: Int, precision: Int): Float {
return i * 2f * PI.toFloat() / precision
}
fun invalidateProgressView()
fun requestProgressViewLayout()
}
================================================
FILE: lemniscate/src/main/java/com/vlad1m1r/lemniscate/base/BaseCurvePresenter.kt
================================================
package com.vlad1m1r.lemniscate.base
import com.vlad1m1r.lemniscate.base.models.DrawState
import com.vlad1m1r.lemniscate.base.models.Point
import com.vlad1m1r.lemniscate.base.models.Points
import com.vlad1m1r.lemniscate.base.models.ViewSize
import com.vlad1m1r.lemniscate.base.settings.AnimationSettings
import com.vlad1m1r.lemniscate.base.settings.CurveSettings
import kotlin.math.round
class BaseCurvePresenter(override val view: IBaseCurveView,
override var curveSettings: CurveSettings,
override val viewSize: ViewSize,
override var animationSettings: AnimationSettings,
override val drawState: DrawState,
override val points: Points) : IBaseCurvePresenter {
override fun updateStartingPointOnCurve(point: Int) {
animationSettings.startingPointOnCurve = point
drawState.recalculateLineLength(curveSettings.lineLength)
view.invalidateProgressView()
}
internal val lineLengthToDraw: Int
get() = round(curveSettings.precision * drawState.currentLineLength).toInt()
override fun recreatePoints() {
points.clear()
createNewPoints()
addPointsToPath()
}
internal fun createNewPoints() {
var lineLengthToDraw = lineLengthToDraw
while (lineLengthToDraw > 0) {
lineLengthToDraw = addPointsToCurve(
getStartingPoint(),
lineLengthToDraw
)
}
}
internal fun getStartingPoint() = if (points.isEmpty) animationSettings.startingPointOnCurve else 0
internal fun addPointsToPath() {
drawState.addPointsToPath(points.getPoints(), curveSettings, viewSize)
}
internal fun addPointsToCurve(start: Int, remainingPoints: Int): Int {
var remainingPointsTemp = remainingPoints
for (i in start until curveSettings.precision) {
points.addPoint(getPoint(i))
if (--remainingPointsTemp == 0) {
return remainingPointsTemp
}
}
return remainingPointsTemp
}
internal fun getPoint(i: Int): Point {
return Point(
view.getGraphX(getT(i)),
view.getGraphY(getT(i)),
curveSettings.strokeWidth,
viewSize.size
)
}
internal fun getT(i: Int) = view.getT(i, curveSettings.precision)
}
================================================
FILE: lemniscate/src/main/java/com/vlad1m1r/lemniscate/base/BaseCurveProgressView.kt
================================================
/*
* Copyright 2016 Vladimir Jovanovic
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.vlad1m1r.lemniscate.base
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.Canvas
import android.graphics.Path
import android.os.Parcel
import android.os.Parcelable
import android.util.AttributeSet
import android.view.View
import android.view.animation.LinearInterpolator
import androidx.customview.view.AbsSavedState
import com.vlad1m1r.lemniscate.R
import com.vlad1m1r.lemniscate.base.models.DrawState
import com.vlad1m1r.lemniscate.base.models.Points
import com.vlad1m1r.lemniscate.base.models.ViewSize
import com.vlad1m1r.lemniscate.base.settings.AnimationSettings
import com.vlad1m1r.lemniscate.base.settings.CurveSettings
import kotlin.math.min
import kotlin.math.round
abstract class BaseCurveProgressView : View, IBaseCurveView {
protected var presenter: IBaseCurvePresenter = BaseCurvePresenter(
this,
CurveSettings(),
ViewSize(), AnimationSettings(),
DrawState(Path()),
Points())
private var valueAnimator: ValueAnimator? = null
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
val curveAttributes = context.obtainStyledAttributes(
attrs,
R.styleable.BaseCurveProgressView,
0, 0)
val colorAccentAttributes = context.obtainStyledAttributes(attrs, intArrayOf(android.R.attr.colorAccent))
try {
val colorAccent = colorAccentAttributes.getColor(0, 0)
presenter.curveSettings.lineLength.lineMaxLength = curveAttributes.getFloat(R.styleable.BaseCurveProgressView_maxLineLength, 0.8f)
presenter.curveSettings.lineLength.lineMinLength = curveAttributes.getFloat(R.styleable.BaseCurveProgressView_minLineLength, 0.4f)
presenter.curveSettings.color = curveAttributes.getColor(R.styleable.BaseCurveProgressView_lineColor, colorAccent)
presenter.curveSettings.hasHole = curveAttributes.getBoolean(R.styleable.BaseCurveProgressView_hasHole, false)
presenter.curveSettings.strokeWidth = curveAttributes.getDimension(R.styleable.BaseCurveProgressView_strokeWidth, resources.getDimension(R.dimen.lemniscate_stroke_width))
presenter.curveSettings.precision = curveAttributes.getInteger(R.styleable.BaseCurveProgressView_precision, 200)
presenter.animationSettings.duration = curveAttributes.getInteger(R.styleable.BaseCurveProgressView_duration, 1000)
} finally {
curveAttributes.recycle()
colorAccentAttributes.recycle()
}
}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
presenter.recreatePoints()
canvas.drawPath(presenter.drawState.path, presenter.curveSettings.paint)
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val defaultSize = resources.getDimension(R.dimen.lemniscate_preferred_height) * presenter.viewSize.sizeMultiplier
val xPadding = paddingLeft + paddingRight
val yPadding = paddingTop + paddingBottom
val viewSize = getMaxViewSquareSize(
measuredHeight,
measuredWidth,
xPadding,
yPadding
)
presenter.viewSize.size = getViewDimension(
MeasureSpec.getMode(widthMeasureSpec),
viewSize.toFloat(),
defaultSize
)
setMeasuredDimension(round(presenter.viewSize.size + xPadding).toInt(), round(presenter.viewSize.size + yPadding).toInt())
}
internal fun getMaxViewSquareSize(height: Int, width: Int, xPadding: Int, yPadding: Int): Int {
return min(height - yPadding, width - xPadding)
}
internal fun getViewDimension(mode: Int, viewSize: Float, defaultSize: Float): Float {
return when {
viewSize == 0.0f -> defaultSize
mode == MeasureSpec.EXACTLY -> viewSize
mode == MeasureSpec.AT_MOST -> min(defaultSize, viewSize)
else -> defaultSize
}
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
animateLemniscate()
}
private fun animateLemniscate() {
valueAnimator?.end()
valueAnimator = ValueAnimator.ofInt(presenter.curveSettings.precision - 1, 0).apply {
duration = presenter.animationSettings.duration.toLong()
repeatCount = -1
repeatMode = ValueAnimator.RESTART
interpolator = LinearInterpolator()
addUpdateListener { animation ->
presenter.updateStartingPointOnCurve(animation.animatedValue as Int)
}
start()
}
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
valueAnimator?.end()
}
var strokeWidth
get() = presenter.curveSettings.strokeWidth
set(strokeWidth) {
presenter.curveSettings.strokeWidth = strokeWidth
}
var lineMaxLength
get() = presenter.curveSettings.lineLength.lineMaxLength
set(lineMaxLength) {
presenter.curveSettings.lineLength.lineMaxLength = lineMaxLength
}
var lineMinLength
get() = presenter.curveSettings.lineLength.lineMinLength
set(lineMinLength) {
presenter.curveSettings.lineLength.lineMinLength = lineMinLength
}
var color
get() = presenter.curveSettings.color
set(color) {
presenter.curveSettings.color = color
}
var duration
get() = presenter.animationSettings.duration
set(duration) {
presenter.animationSettings.duration = duration
if (valueAnimator != null) valueAnimator!!.duration = duration.toLong()
}
var precision
get() = presenter.curveSettings.precision
set(precision) {
presenter.curveSettings.precision = precision
animateLemniscate()
invalidate()
}
var sizeMultiplier
get() = presenter.viewSize.sizeMultiplier
set(sizeMultiplier) {
presenter.viewSize.sizeMultiplier = sizeMultiplier
requestLayout()
invalidate()
}
var size = presenter.viewSize.size
get() = presenter.viewSize.size
private set
open var hasHole
get() = presenter.curveSettings.hasHole
set(hasHole) {
presenter.curveSettings.hasHole = hasHole
}
public override fun onSaveInstanceState(): Parcelable {
val ss = BaseCurveSavedState(super.onSaveInstanceState()!!)
ss.curveSettings = this.presenter.curveSettings
ss.animationSettings = this.presenter.animationSettings
return ss
}
public override fun onRestoreInstanceState(state: Parcelable) {
if (state is BaseCurveSavedState) {
super.onRestoreInstanceState(state.superState)
state.curveSettings?.let {
this.presenter.curveSettings = it
}
state.animationSettings?.let {
this.presenter.animationSettings = it
}
} else {
super.onRestoreInstanceState(state)
}
}
protected open class BaseCurveSavedState : AbsSavedState {
internal var curveSettings: CurveSettings? = null
internal var animationSettings: AnimationSettings? = null
constructor(superState: Parcelable) : super(superState)
constructor(state: Parcel) : super(state, BaseCurveSavedState::class.java.classLoader) {
this.curveSettings = state.readParcelable(CurveSettings::class.java.classLoader)
this.animationSettings = state.readParcelable(AnimationSettings::class.java.classLoader)
}
override fun writeToParcel(out: Parcel, flags: Int) {
super.writeToParcel(out, flags)
out.writeParcelable(curveSettings, flags)
out.writeParcelable(animationSettings, flags)
}
companion object {
@JvmField
val CREATOR = object : Parcelable.Creator<BaseCurveSavedState> {
override fun createFromParcel(source: Parcel): BaseCurveSavedState {
return BaseCurveSavedState(source)
}
override fun newArray(size: Int): Array<BaseCurveSavedState?> {
return arrayOfNulls(size)
}
}
}
}
override fun invalidateProgressView() {
invalidate()
}
override fun requestProgressViewLayout() {
requestLayout()
}
}
================================================
FILE: lemniscate/src/main/java/com/vlad1m1r/lemniscate/base/models/DrawState.kt
================================================
/*
* Copyright 2016 Vladimir Jovanovic
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.vlad1m1r.lemniscate.base.models
import android.graphics.Path
import com.vlad1m1r.lemniscate.base.settings.CurveSettings
import com.vlad1m1r.lemniscate.utils.CurveUtils
private const val STEP_SIZE = 0.001f
class DrawState(val path:Path) {
internal var isExpanding = true
var currentLineLength = 0.0f
internal set
internal fun addPairOfPointsToPath(start: Point?, end: Point?) {
if (start != null && end != null) {
path.moveTo(start.x, start.y)
path.quadTo(start.x, start.y, end.x, end.y)
} else if (start != null) {
path.moveTo(start.x, start.y)
path.lineTo(start.x, start.y)
} else if (end != null) {
path.moveTo(end.x, end.y)
}
}
internal fun isInRightDirectionToBeInHole(start: Point?, end: Point?)
= start != null && end != null && start.x > end.x
fun addPointsToPath(listOfPoints: List<Point>, curveSettings: CurveSettings, viewSize: ViewSize) {
resetPath()
val holeSize = curveSettings.strokeWidth
//adds points to path and creates hole if curveSettings.hasHole()
for (i in listOfPoints.indices) {
var start: Point? = listOfPoints[i]
var end: Point? = null
if (listOfPoints.size > i + 1)
end = listOfPoints[i + 1]
if (curveSettings.hasHole) {
if (isInRightDirectionToBeInHole(start, end)) {
start = CurveUtils.checkPointForHole(start, holeSize, viewSize.size)
end = CurveUtils.checkPointForHole(end, holeSize, viewSize.size)
}
}
addPairOfPointsToPath(start, end)
}
}
internal fun resetPath() {
path.reset()
}
internal fun keepLineLengthInsideLimits(lineLength: LineLength){
if (currentLineLength < lineLength.lineMinLength) {
currentLineLength = lineLength.lineMinLength
}
if (currentLineLength > lineLength.lineMaxLength) {
currentLineLength = lineLength.lineMaxLength
}
}
internal fun calculateNewCurrentLineLength(lineLength: LineLength) {
if (currentLineLength < lineLength.lineMaxLength && isExpanding) {
currentLineLength += STEP_SIZE
} else if (currentLineLength > lineLength.lineMinLength && !isExpanding) {
currentLineLength -= STEP_SIZE
} else if (currentLineLength == lineLength.lineMaxLength) {
isExpanding = false
} else if (currentLineLength == lineLength.lineMinLength) {
isExpanding = true
} else {
throw IllegalArgumentException("currentLineLength is not inside limits")
}
}
fun recalculateLineLength(lineLength: LineLength) {
if (lineLength.lineMinLength < lineLength.lineMaxLength) {
keepLineLengthInsideLimits(lineLength)
calculateNewCurrentLineLength(lineLength)
} else {
currentLineLength = lineLength.lineMaxLength
}
}
}
================================================
FILE: lemniscate/src/main/java/com/vlad1m1r/lemniscate/base/models/LineLength.kt
================================================
/*
* Copyright 2016 Vladimir Jovanovic
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.vlad1m1r.lemniscate.base.models
import android.os.Parcel
import android.os.Parcelable
class LineLength() : Parcelable {
var lineMinLength = 0.4f
set(value) {
if (value > 0 && value <= 1) {
field = value
} else {
throw IllegalArgumentException()
}
}
var lineMaxLength = 0.8f
set(value) {
if (value > 0 && value <= 1) {
field = value
} else {
throw IllegalArgumentException()
}
}
internal constructor(state: Parcel) : this() {
this.lineMinLength = state.readFloat()
this.lineMaxLength = state.readFloat()
}
override fun describeContents(): Int {
return 0
}
override fun writeToParcel(dest: Parcel, flags: Int) {
dest.writeFloat(this.lineMinLength)
dest.writeFloat(this.lineMaxLength)
}
companion object CREATOR : Parcelable.Creator<LineLength> {
override fun createFromParcel(parcel: Parcel): LineLength {
return LineLength(parcel)
}
override fun newArray(size: Int): Array<LineLength?> {
return arrayOfNulls(size)
}
}
}
================================================
FILE: lemniscate/src/main/java/com/vlad1m1r/lemniscate/base/models/Point.kt
================================================
/*
* Copyright 2016 Vladimir Jovanovic
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.vlad1m1r.lemniscate.base.models
class Point(x: Float, y: Float, strokeWidth: Float, viewSize: Float) {
val x: Float = translateToPositiveCoordinates(x, strokeWidth, viewSize)
val y: Float = translateToPositiveCoordinates(y, strokeWidth, viewSize)
private fun compensateForStrokeWidth(coordinate: Float, strokeWidth: Float, viewSize: Float): Float {
val ratio = viewSize / (viewSize + 2 * strokeWidth)
return coordinate * ratio + strokeWidth * ratio
}
private fun translateToPositiveCoordinates(coordinate: Float, strokeWidth: Float, viewSize: Float): Float {
return compensateForStrokeWidth(coordinate + viewSize / 2, strokeWidth, viewSize)
}
}
================================================
FILE: lemniscate/src/main/java/com/vlad1m1r/lemniscate/base/models/Points.kt
================================================
/*
* Copyright 2016 Vladimir Jovanovic
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.vlad1m1r.lemniscate.base.models
import java.util.*
class Points {
private val points = ArrayList<Point>()
val isEmpty: Boolean
get() = points.isEmpty()
fun getPoints(): List<Point> {
return points.toList()
}
fun addPoint(point: Point) {
points.add(point)
}
fun clear() {
points.clear()
}
}
================================================
FILE: lemniscate/src/main/java/com/vlad1m1r/lemniscate/base/models/ViewSize.kt
================================================
/*
* Copyright 2016 Vladimir Jovanovic
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.vlad1m1r.lemniscate.base.models
class ViewSize {
var size: Float = 0.0f
var sizeMultiplier: Float = 1.0f
}
================================================
FILE: lemniscate/src/main/java/com/vlad1m1r/lemniscate/base/settings/AnimationSettings.kt
================================================
/*
* Copyright 2016 Vladimir Jovanovic
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.vlad1m1r.lemniscate.base.settings
import android.os.Parcel
import android.os.Parcelable
class AnimationSettings(var startingPointOnCurve:Int = 0, var duration: Int = 1000) : Parcelable {
constructor(state: Parcel) : this(
state.readInt(),
state.readInt())
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeInt(startingPointOnCurve)
parcel.writeInt(duration)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<AnimationSettings> {
override fun createFromParcel(parcel: Parcel): AnimationSettings {
return AnimationSettings(parcel)
}
override fun newArray(size: Int): Array<AnimationSettings?> {
return arrayOfNulls(size)
}
}
}
================================================
FILE: lemniscate/src/main/java/com/vlad1m1r/lemniscate/base/settings/CurveSettings.kt
================================================
/*
* Copyright 2016 Vladimir Jovanovic
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.vlad1m1r.lemniscate.base.settings
import android.graphics.Paint
import android.os.Parcel
import android.os.Parcelable
import com.vlad1m1r.lemniscate.base.models.LineLength
open class CurveSettings (val paint: Paint = Paint(Paint.ANTI_ALIAS_FLAG), var lineLength: LineLength = LineLength()) : Parcelable {
init {
paint.style = Paint.Style.STROKE
paint.strokeCap = Paint.Cap.ROUND
}
var precision = 200
var strokeWidth: Float = 0f
@Throws(IllegalArgumentException::class)
set(value) {
if (value >= 0) {
field = value
this.paint.strokeWidth = value
} else {
throw IllegalArgumentException("\'strokeWidth\' must be positive!")
}
}
var color: Int = 0
set(value) {
field = value
paint.color = value
}
var hasHole = false
internal constructor(state: Parcel) : this() {
this.precision = state.readInt()
this.strokeWidth = state.readFloat()
this.color = state.readInt()
this.lineLength = state.readParcelable(LineLength::class.java.classLoader)!!
this.hasHole = state.readByte().toInt() != 0
}
override fun describeContents(): Int {
return 0
}
override fun writeToParcel(dest: Parcel, flags: Int) {
dest.writeInt(this.precision)
dest.writeFloat(this.strokeWidth)
dest.writeInt(this.color)
dest.writeParcelable(this.lineLength, flags)
dest.writeByte(if (this.hasHole) 1.toByte() else 0.toByte())
}
companion object CREATOR : Parcelable.Creator<CurveSettings> {
override fun createFromParcel(source: Parcel): CurveSettings {
return CurveSettings(source)
}
override fun newArray(size: Int): Array<CurveSettings?> {
return arrayOfNulls(size)
}
}
}
================================================
FILE: lemniscate/src/main/java/com/vlad1m1r/lemniscate/funny/CannabisProgressView.kt
================================================
/*
* Copyright 2016 Vladimir Jovanovic
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.vlad1m1r.lemniscate.funny
import android.content.Context
import android.util.AttributeSet
import com.vlad1m1r.lemniscate.base.BaseCurveProgressView
import kotlin.math.cos
import kotlin.math.sin
class CannabisProgressView : BaseCurveProgressView {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
override fun getGraphX(t: Float): Float =
((size / 6)
* (sin(t) + 1)
* cos(t)
* (9 / 10f * cos(8 * t) + 1)
* (1 / 10f * cos(24 * t) + 1)
* (1 / 10f * cos(200 * t) + 9 / 10f))
override fun getGraphY(t: Float): Float =
((-size / 6)
* sin(t)
* (sin(t) + 1)
* (9 / 10f * cos(8 * t) + 1)
* (1 / 10f * cos(24 * t) + 1)
* (1 / 10f * cos(200 * t) + 9 / 10f)) + size / 4
// Disable hasHole setter. Should stay false
override var hasHole: Boolean = false
set(hasHole) {
super.hasHole = hasHole && false
field = hasHole && false
}
}
================================================
FILE: lemniscate/src/main/java/com/vlad1m1r/lemniscate/funny/HeartProgressView.kt
================================================
/*
* Copyright 2016 Vladimir Jovanovic
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.vlad1m1r.lemniscate.funny
import android.content.Context
import android.util.AttributeSet
import com.vlad1m1r.lemniscate.base.BaseCurveProgressView
import kotlin.math.cos
import kotlin.math.pow
import kotlin.math.sin
class HeartProgressView : BaseCurveProgressView {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
override fun getGraphX(t: Float): Float =
(size / 34) * 16 * sin(t).pow(3)
override fun getGraphY(t: Float): Float =
-size / 34 * (13 * cos(t)
- 5 * cos(2 * t)
- 2 * cos(3 * t)
- cos(4 * t))
// Disable hasHole setter. Should stay false
override var hasHole: Boolean = false
set(hasHole) {
super.hasHole = hasHole && false
field = hasHole && false
}
}
================================================
FILE: lemniscate/src/main/java/com/vlad1m1r/lemniscate/other/XProgressView.kt
================================================
/*
* Copyright 2016 Vladimir Jovanovic
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.vlad1m1r.lemniscate.other
import android.content.Context
import android.util.AttributeSet
import com.vlad1m1r.lemniscate.base.BaseCurveProgressView
import kotlin.math.abs
import kotlin.math.cos
import kotlin.math.sin
class XProgressView : BaseCurveProgressView {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
override fun getGraphX(t: Float): Float =
size * abs(sin(t)) * cos(t)
override fun getGraphY(t: Float): Float =
size * sin(t) * cos(t)
// Disable hasHole setter. Should stay false
override var hasHole: Boolean = false
set(hasHole) {
super.hasHole = hasHole && false
field = hasHole && false
}
}
================================================
FILE: lemniscate/src/main/java/com/vlad1m1r/lemniscate/roulette/BaseRouletteProgressView.kt
================================================
/*
* Copyright 2016 Vladimir Jovanovic
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.vlad1m1r.lemniscate.roulette
import android.content.Context
import android.os.Parcel
import android.os.Parcelable
import android.util.AttributeSet
import com.vlad1m1r.lemniscate.R
import com.vlad1m1r.lemniscate.base.BaseCurveProgressView
import com.vlad1m1r.lemniscate.roulette.settings.RouletteCurveSettings
import kotlin.math.PI
abstract class BaseRouletteProgressView : BaseCurveProgressView {
protected var rouletteCurveSettings: RouletteCurveSettings = RouletteCurveSettings()
var radiusFixed: Float
get() = rouletteCurveSettings.radiusFixed
set(radiusFixed) {
rouletteCurveSettings.radiusFixed = radiusFixed
recalculateConstants()
}
var radiusMoving: Float
get() = rouletteCurveSettings.radiusMoving
set(radiusMoving) {
rouletteCurveSettings.radiusMoving = radiusMoving
recalculateConstants()
}
var distanceFromCenter: Float
get() = rouletteCurveSettings.distanceFromCenter
set(distanceFromCenter) {
rouletteCurveSettings.distanceFromCenter = distanceFromCenter
recalculateConstants()
}
var numberOfCycles: Float
get() = rouletteCurveSettings.numberOfCycles
set(numberOfCycles) {
rouletteCurveSettings.numberOfCycles = numberOfCycles
}
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
val rouletteCurveAttributes = context.obtainStyledAttributes(
attrs,
R.styleable.RouletteCurveProgressView,
0, 0)
try {
radiusFixed = rouletteCurveAttributes.getFloat(R.styleable.RouletteCurveProgressView_radiusFixed, rouletteCurveSettings.radiusFixed)
radiusMoving = rouletteCurveAttributes.getFloat(R.styleable.RouletteCurveProgressView_radiusMoving, rouletteCurveSettings.radiusMoving)
distanceFromCenter = rouletteCurveAttributes.getFloat(R.styleable.RouletteCurveProgressView_distanceFromCenter, rouletteCurveSettings.distanceFromCenter)
numberOfCycles = rouletteCurveAttributes.getFloat(R.styleable.RouletteCurveProgressView_numberOfCycles, rouletteCurveSettings.numberOfCycles)
} finally {
rouletteCurveAttributes.recycle()
}
}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
internal open fun recalculateConstants() {}
// Disable hasHole setter. Should stay false
override var hasHole: Boolean = false
set(hasHole) {
super.hasHole = hasHole && false
field = hasHole && false
}
override fun getT(i: Int, precision: Int): Float {
return i * rouletteCurveSettings.numberOfCycles * 2 * PI.toFloat() / precision
}
override fun onSaveInstanceState(): Parcelable {
val ss = RouletteCurveSavedState(super.onSaveInstanceState())
ss.rouletteCurveSettings = rouletteCurveSettings
return ss
}
override fun onRestoreInstanceState(state: Parcelable) {
if (state is RouletteCurveSavedState) {
super.onRestoreInstanceState(state.superState!!)
state.rouletteCurveSettings?.let {
this.rouletteCurveSettings = it
}
} else {
super.onRestoreInstanceState(state)
}
}
protected open class RouletteCurveSavedState : BaseCurveSavedState {
internal var rouletteCurveSettings: RouletteCurveSettings? = null
constructor(superState: Parcelable) : super(superState)
constructor(source: Parcel) : super(source) {
this.rouletteCurveSettings = source.readParcelable(RouletteCurveSettings::class.java.classLoader)
}
override fun writeToParcel(out: Parcel, flags: Int) {
super.writeToParcel(out, flags)
out.writeParcelable(rouletteCurveSettings, flags)
}
companion object {
@JvmField
val CREATOR = object : Parcelable.Creator<RouletteCurveSavedState> {
override fun createFromParcel(source: Parcel): RouletteCurveSavedState {
return RouletteCurveSavedState(source)
}
override fun newArray(size: Int): Array<RouletteCurveSavedState?> {
return arrayOfNulls(size)
}
}
}
}
}
================================================
FILE: lemniscate/src/main/java/com/vlad1m1r/lemniscate/roulette/EpitrochoidProgressView.kt
================================================
/*
* Copyright 2016 Vladimir Jovanovic
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.vlad1m1r.lemniscate.roulette
import android.content.Context
import android.util.AttributeSet
import kotlin.math.cos
import kotlin.math.sin
class EpitrochoidProgressView : BaseRouletteProgressView {
internal var radiusSum = 0f
get() = radiusFixed + radiusMoving
private set
internal var sizeFactor = 0f
get() = 2 * (radiusSum + distanceFromCenter)
private set
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
override fun getGraphX(t: Float): Float =
size / sizeFactor * (radiusSum * cos(t) - distanceFromCenter * cos(radiusSum / radiusMoving * t))
override fun getGraphY(t: Float): Float =
size / sizeFactor * (radiusSum * sin(t) - distanceFromCenter * sin(radiusSum / radiusMoving * t))
override fun recalculateConstants() {
radiusSum = radiusFixed + radiusMoving
sizeFactor = 2 * (radiusSum + distanceFromCenter)
}
}
================================================
FILE: lemniscate/src/main/java/com/vlad1m1r/lemniscate/roulette/HypotrochoidProgressView.kt
================================================
/*
* Copyright 2016 Vladimir Jovanovic
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.vlad1m1r.lemniscate.roulette
import android.content.Context
import android.util.AttributeSet
import kotlin.math.cos
import kotlin.math.sin
class HypotrochoidProgressView : BaseRouletteProgressView {
internal var radiusDiff = 0f
get() = radiusFixed - radiusMoving
private set
internal var sizeFactor = 0f
get() = 2 * (radiusDiff + distanceFromCenter)
private set
// radiusFixed = 5, radiusMoving=3, distanceFromCenter=5, numberOfCycles = 3 to get pentagram
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
override fun getGraphX(t: Float): Float =
size / sizeFactor * (radiusDiff * cos(t) - distanceFromCenter * cos(radiusDiff / radiusMoving * t))
override fun getGraphY(t: Float): Float =
size / sizeFactor * (radiusDiff * sin(t) + distanceFromCenter * sin(radiusDiff / radiusMoving * t))
}
================================================
FILE: lemniscate/src/main/java/com/vlad1m1r/lemniscate/roulette/scribble/RoundScribbleProgressView.kt
================================================
/*
* Copyright 2016 Vladimir Jovanovic
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.vlad1m1r.lemniscate.roulette.scribble
import android.content.Context
import android.util.AttributeSet
import com.vlad1m1r.lemniscate.roulette.BaseRouletteProgressView
import kotlin.math.cos
import kotlin.math.sin
class RoundScribbleProgressView : BaseRouletteProgressView {
internal var radiusSum = 0.0f
get() = radiusFixed + radiusMoving
private set
internal var sizeFactor = 0.0f
get() = 2 * (radiusSum + distanceFromCenter)
private set
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
override fun getGraphX(t: Float): Float =
size / sizeFactor * (radiusSum * cos(t) - distanceFromCenter * cos(radiusSum / radiusMoving * t))
override fun getGraphY(t: Float): Float =
size / sizeFactor * (radiusSum * cos(t) - distanceFromCenter * sin(radiusSum / radiusMoving * t))
}
================================================
FILE: lemniscate/src/main/java/com/vlad1m1r/lemniscate/roulette/scribble/ScribbleProgressView.kt
================================================
/*
* Copyright 2016 Vladimir Jovanovic
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.vlad1m1r.lemniscate.roulette.scribble
import android.content.Context
import android.util.AttributeSet
import com.vlad1m1r.lemniscate.roulette.BaseRouletteProgressView
import kotlin.math.cos
import kotlin.math.sin
class ScribbleProgressView : BaseRouletteProgressView {
internal var radiusSum: Float = 0f
get() = radiusFixed + radiusMoving
private set
internal var sizeFactor: Float = 0f
get() = 2 * (radiusSum + distanceFromCenter)
private set
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
override fun getGraphX(t: Float): Float =
(size / sizeFactor * (radiusSum * cos(t) - distanceFromCenter * cos(radiusSum / radiusMoving * t)))
override fun getGraphY(t: Float): Float =
(size / sizeFactor * (radiusSum * sin(t) - distanceFromCenter * cos(radiusSum / radiusMoving * t)))
}
================================================
FILE: lemniscate/src/main/java/com/vlad1m1r/lemniscate/roulette/settings/RouletteCurveSettings.kt
================================================
package com.vlad1m1r.lemniscate.roulette.settings
import android.os.Parcel
import android.os.Parcelable
class RouletteCurveSettings() : Parcelable {
/**
* Radius of the non-moving circle
*/
var radiusFixed = 3.0f
/**
* Radius of the moving circle
*/
var radiusMoving = 1.0f
/**
* Distance from the center of the moving circle
*/
var distanceFromCenter = 1.0f
/**
* Curve will be drawn on interval [0, 2*numberOfCycles*π] before repeating
*/
var numberOfCycles = 1.0f
constructor(parcel: Parcel) : this() {
radiusFixed = parcel.readFloat()
radiusMoving = parcel.readFloat()
distanceFromCenter = parcel.readFloat()
numberOfCycles = parcel.readFloat()
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeFloat(radiusFixed)
parcel.writeFloat(radiusMoving)
parcel.writeFloat(distanceFromCenter)
parcel.writeFloat(numberOfCycles)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<RouletteCurveSettings> {
override fun createFromParcel(parcel: Parcel): RouletteCurveSettings {
return RouletteCurveSettings(parcel)
}
override fun newArray(size: Int): Array<RouletteCurveSettings?> {
return arrayOfNulls(size)
}
}
}
================================================
FILE: lemniscate/src/main/java/com/vlad1m1r/lemniscate/utils/CurveUtils.kt
================================================
/*
* Copyright 2016 Vladimir Jovanovic
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.vlad1m1r.lemniscate.utils
import com.vlad1m1r.lemniscate.base.models.Point
import kotlin.math.abs
object CurveUtils {
fun checkPointForHole(point: Point?, holeSize: Float, viewSize: Float): Point? {
return if (point != null &&
abs(point.x - viewSize / 2) < holeSize &&
abs(point.y - viewSize / 2) < holeSize) {
null
} else point
}
}
================================================
FILE: lemniscate/src/main/res/values/attrs.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="BaseCurveProgressView">
<attr name="minLineLength" format="float"/>
<attr name="maxLineLength" format="float"/>
<attr name="lineColor" format="color"/>
<attr name="duration" format="integer"/>
<attr name="hasHole" format="boolean"/>
<attr name="strokeWidth" format="dimension"/>
<attr name="sizeMultiplier" format="float"/>
<attr name="precision" format="integer"/>
</declare-styleable>
<declare-styleable name="RouletteCurveProgressView">
<attr name="radiusFixed" format="float"/>
<attr name="radiusMoving" format="float"/>
<attr name="distanceFromCenter" format="float"/>
<attr name="numberOfCycles" format="float"/>
</declare-styleable>
</resources>
================================================
FILE: lemniscate/src/main/res/values/dimens.xml
================================================
<resources>
<dimen name="lemniscate_stroke_width">10dp</dimen>
<dimen name="lemniscate_preferred_width">80dp</dimen>
<dimen name="lemniscate_preferred_height">80dp</dimen>
</resources>
================================================
FILE: lemniscate/src/test/java/com/vlad1m1r/lemniscate/BernoullisBowProgressViewTest.kt
================================================
package com.vlad1m1r.lemniscate
import com.google.common.truth.Truth.assertThat
import com.vlad1m1r.lemniscate.testutils.TestConstants
import com.vlad1m1r.lemniscate.testutils.isPeriodic
import com.vlad1m1r.lemniscate.testutils.setupDefaultMock
import org.junit.Before
import org.junit.Test
import org.mockito.kotlin.mock
import kotlin.math.PI
class BernoullisBowProgressViewTest {
private val view = mock<BernoullisBowProgressView>()
@Before
fun setUp() {
view.setupDefaultMock()
}
@Test
fun getGraphX() {
assertThat(view.getGraphX(0.0f)).isWithin(TestConstants.DELTA).of(37.5f)
assertThat(view.getGraphX(0.1f)).isWithin(TestConstants.DELTA).of(37.873238f)
assertThat(view.getGraphX(0.5f)).isWithin(TestConstants.DELTA).of(45.180264f)
assertThat(view.getGraphX(1.0f)).isWithin(TestConstants.DELTA).of(39.53901f)
assertThat(view.getGraphX(PI.toFloat())).isWithin(TestConstants.DELTA).of(-37.5f)
assertThat(view.getGraphX(2.0f)).isWithin(TestConstants.DELTA).of(-31.049751f)
assertThat(view.getGraphX(2 * PI.toFloat())).isWithin(TestConstants.DELTA).of(37.5f)
}
@Test
fun getGraphY() {
assertThat(view.getGraphY(0.0f)).isWithin(TestConstants.DELTA).of(0.0f)
assertThat(view.getGraphY(0.1f)).isWithin(TestConstants.DELTA).of(3.7810147f)
assertThat(view.getGraphY(0.5f)).isWithin(TestConstants.DELTA).of(21.660572f)
assertThat(view.getGraphY(1.0f)).isWithin(TestConstants.DELTA).of(33.270927f)
assertThat(view.getGraphY(PI.toFloat())).isWithin(TestConstants.DELTA).of(0.0f)
assertThat(view.getGraphY(2.0f)).isWithin(TestConstants.DELTA).of(-28.233456f)
assertThat(view.getGraphY(2 * PI.toFloat())).isWithin(TestConstants.DELTA).of(0.0f)
}
@Test
fun isPeriodic() {
view.isPeriodic(2 * PI.toFloat())
}
}
================================================
FILE: lemniscate/src/test/java/com/vlad1m1r/lemniscate/BernoullisProgressViewTest.kt
================================================
package com.vlad1m1r.lemniscate
import com.google.common.truth.Truth.assertThat
import org.mockito.kotlin.mock
import com.vlad1m1r.lemniscate.testutils.TestConstants
import com.vlad1m1r.lemniscate.testutils.isPeriodic
import com.vlad1m1r.lemniscate.testutils.setupDefaultMock
import org.junit.Before
import org.junit.Test
import kotlin.math.PI
class BernoullisProgressViewTest {
private val view = mock<BernoullisProgressView>()
@Before
fun setUp() {
view.setupDefaultMock()
}
@Test
fun getGraphX() {
assertThat(view.getGraphX(0.0f)).isWithin(TestConstants.DELTA).of(50.0f)
assertThat(view.getGraphX(0.1f)).isWithin(TestConstants.DELTA).of(49.259254f)
assertThat(view.getGraphX(0.5f)).isWithin(TestConstants.DELTA).of(35.67847f)
assertThat(view.getGraphX(1.0f)).isWithin(TestConstants.DELTA).of(15.816132f)
assertThat(view.getGraphX(PI.toFloat())).isWithin(TestConstants.DELTA).of(-50.0f)
assertThat(view.getGraphX(2.0f)).isWithin(TestConstants.DELTA).of(-11.389914f)
assertThat(view.getGraphX(2 * PI.toFloat())).isWithin(TestConstants.DELTA).of(50.0f)
}
@Test
fun getGraphY() {
assertThat(view.getGraphY(0.0f)).isWithin(TestConstants.DELTA).of(0.0f)
assertThat(view.getGraphY(0.1f)).isWithin(TestConstants.DELTA).of(4.91772f)
assertThat(view.getGraphY(0.5f)).isWithin(TestConstants.DELTA).of(17.10517f)
assertThat(view.getGraphY(1.0f)).isWithin(TestConstants.DELTA).of(13.308815f)
assertThat(view.getGraphY(PI.toFloat())).isWithin(TestConstants.DELTA).of(0.0f)
assertThat(view.getGraphY(2.0f)).isWithin(TestConstants.DELTA).of(-10.356819f)
assertThat(view.getGraphY(2 * PI.toFloat())).isWithin(TestConstants.DELTA).of(0.0f)
}
@Test
fun isPeriodic() {
view.isPeriodic(2 * PI.toFloat())
}
}
================================================
FILE: lemniscate/src/test/java/com/vlad1m1r/lemniscate/BernoullisSharpProgressViewTest.kt
================================================
package com.vlad1m1r.lemniscate
import com.google.common.truth.Truth.assertThat
import com.vlad1m1r.lemniscate.testutils.TestConstants
import com.vlad1m1r.lemniscate.testutils.isPeriodic
import com.vlad1m1r.lemniscate.testutils.setupDefaultMock
import org.junit.Before
import org.junit.Test
import org.mockito.kotlin.mock
import kotlin.math.PI
class BernoullisSharpProgressViewTest {
private val view = mock<BernoullisSharpProgressView>()
@Before
fun setUp() {
view.setupDefaultMock()
}
@Test
fun getGraphX() {
assertThat(view.getGraphX(0.0f)).isWithin(TestConstants.DELTA).of(50.0f)
assertThat(view.getGraphX(0.1f)).isWithin(TestConstants.DELTA).of(49.99937f)
assertThat(view.getGraphX(0.5f)).isWithin(TestConstants.DELTA).of(49.576702f)
assertThat(view.getGraphX(1.0f)).isWithin(TestConstants.DELTA).of(41.821438f)
assertThat(view.getGraphX(PI.toFloat())).isWithin(TestConstants.DELTA).of(-50.0f)
assertThat(view.getGraphX(2.0f)).isWithin(TestConstants.DELTA).of(-35.471752f)
assertThat(view.getGraphX(2 * PI.toFloat())).isWithin(TestConstants.DELTA).of(50.0f)
}
@Test
fun getGraphY() {
assertThat(view.getGraphY(0.0f)).isWithin(TestConstants.DELTA).of(0.0f)
assertThat(view.getGraphY(0.1f)).isWithin(TestConstants.DELTA).of(4.991608f)
assertThat(view.getGraphY(0.5f)).isWithin(TestConstants.DELTA).of(23.768335f)
assertThat(view.getGraphY(1.0f)).isWithin(TestConstants.DELTA).of(35.191525f)
assertThat(view.getGraphY(PI.toFloat())).isWithin(TestConstants.DELTA).of(0.0f)
assertThat(view.getGraphY(2.0f)).isWithin(TestConstants.DELTA).of(-32.25437f)
assertThat(view.getGraphY(2 * PI.toFloat())).isWithin(TestConstants.DELTA).of(0.0f)
}
@Test
fun isPeriodic() {
view.isPeriodic(2 * PI.toFloat())
}
}
================================================
FILE: lemniscate/src/test/java/com/vlad1m1r/lemniscate/GeronosProgressViewTest.kt
================================================
package com.vlad1m1r.lemniscate
import com.google.common.truth.Truth.assertThat
import org.mockito.kotlin.mock
import com.vlad1m1r.lemniscate.testutils.TestConstants
import com.vlad1m1r.lemniscate.testutils.isPeriodic
import com.vlad1m1r.lemniscate.testutils.setupDefaultMock
import org.junit.Before
import org.junit.Test
import kotlin.math.PI
class GeronosProgressViewTest {
private val view = mock<GeronosProgressView>()
@Before
fun setUp() {
view.setupDefaultMock()
}
@Test
fun getGraphX() {
assertThat(view.getGraphX(0.0f)).isWithin(TestConstants.DELTA).of(0.0f)
assertThat(view.getGraphX(0.1f)).isWithin(TestConstants.DELTA).of(4.991671f)
assertThat(view.getGraphX(0.5f)).isWithin(TestConstants.DELTA).of(23.971277f)
assertThat(view.getGraphX(1.0f)).isWithin(TestConstants.DELTA).of(42.073547f)
assertThat(view.getGraphX(PI.toFloat())).isWithin(TestConstants.DELTA).of(0.0f)
assertThat(view.getGraphX(2.0f)).isWithin(TestConstants.DELTA).of(45.46487f)
assertThat(view.getGraphX(2 * PI.toFloat())).isWithin(TestConstants.DELTA).of(0.0f)
}
@Test
fun getGraphY() {
assertThat(view.getGraphY(0.0f)).isWithin(TestConstants.DELTA).of(0.0f)
assertThat(view.getGraphY(0.1f)).isWithin(TestConstants.DELTA).of(4.9667335f)
assertThat(view.getGraphY(0.5f)).isWithin(TestConstants.DELTA).of(21.036774f)
assertThat(view.getGraphY(1.0f)).isWithin(TestConstants.DELTA).of(22.732433f)
assertThat(view.getGraphY(PI.toFloat())).isWithin(TestConstants.DELTA).of(0.0f)
assertThat(view.getGraphY(2.0f)).isWithin(TestConstants.DELTA).of(-18.920063f)
assertThat(view.getGraphY(2 * PI.toFloat())).isWithin(TestConstants.DELTA).of(0.0f)
}
@Test
fun isPeriodic() {
view.isPeriodic(2 * PI.toFloat())
}
}
================================================
FILE: lemniscate/src/test/java/com/vlad1m1r/lemniscate/base/BaseCurvePresenterTest.kt
================================================
package com.vlad1m1r.lemniscate.base
import com.google.common.truth.Truth.assertThat
import org.mockito.kotlin.*
import com.vlad1m1r.lemniscate.base.models.*
import com.vlad1m1r.lemniscate.base.settings.AnimationSettings
import com.vlad1m1r.lemniscate.base.settings.CurveSettings
import org.junit.Test
class BaseCurvePresenterTest {
private val view = mock<IBaseCurveView>()
private val curveSettings = mock<CurveSettings>()
private val viewSize = mock<ViewSize>()
private val animationSettings = mock<AnimationSettings>()
private val drawState = mock<DrawState>()
private val points = mock<Points>()
val presenter = BaseCurvePresenter(view, curveSettings, viewSize, animationSettings, drawState, points)
@Test
fun updateStartingPointOnCurve() {
whenever(curveSettings.lineLength).thenReturn(LineLength())
presenter.updateStartingPointOnCurve(1)
verify(animationSettings).startingPointOnCurve = 1
verify(drawState).recalculateLineLength(curveSettings.lineLength)
verify(view).invalidateProgressView()
}
@Test
fun recreatePoints() {
val presenterSpy = spy(presenter)
presenterSpy.recreatePoints()
verify(points).clear()
verify(presenterSpy).createNewPoints()
verify(presenterSpy).addPointsToPath()
}
@Test
fun getCorrectLineLengthToDraw() {
whenever(curveSettings.precision).thenReturn(99)
whenever(drawState.currentLineLength).thenReturn(10.32f)
assertThat(presenter.lineLengthToDraw).isEqualTo(1022)
}
@Test
fun getCorrectStartingPoint_whenPointsIsEmpty() {
whenever(animationSettings.startingPointOnCurve).thenReturn(10)
whenever(points.isEmpty).thenReturn(true)
assertThat(presenter.getStartingPoint()).isEqualTo(10)
}
@Test
fun getCorrectStartingPoint_whenPointsIsNotEmpty() {
whenever(points.isEmpty).thenReturn(false)
assertThat(presenter.getStartingPoint()).isEqualTo(0)
}
@Test
fun addPointsToPath() {
whenever(points.getPoints()).thenReturn(ArrayList())
presenter.addPointsToPath()
verify(drawState).addPointsToPath(points.getPoints(), curveSettings, viewSize)
}
@Test
fun addPointsToCurve_whenNotAllAddedStartO() {
val presenterSpy = spy(presenter)
val point = Point(1f,2f, 3f, 200f)
doReturn(point).whenever(presenterSpy).getPoint(any())
whenever(curveSettings.precision).thenReturn(20)
val remainingPointsValue = 100
val remainingPoints = presenterSpy.addPointsToCurve(0, remainingPointsValue)
verify(points, times(curveSettings.precision)).addPoint(point)
assertThat(remainingPoints).isEqualTo(remainingPointsValue - curveSettings.precision)
}
@Test
fun addPointsToCurveWhenNotAllAddedStartNotO() {
val presenterSpy = spy(presenter)
val point = Point(1f,2f, 3f, 200f)
doReturn(point).whenever(presenterSpy).getPoint(any())
whenever(curveSettings.precision).thenReturn(20)
val startValue = 10
val remainingPointsValue = 100
val remainingPoints = presenterSpy.addPointsToCurve(startValue, remainingPointsValue)
verify(points, times(curveSettings.precision - startValue)).addPoint(point)
assertThat(remainingPoints).isEqualTo(remainingPointsValue - curveSettings.precision + startValue)
}
@Test
fun addPointsToCurveWhenAllAdded() {
val presenterSpy = spy(presenter)
val point = Point(1f,2f, 3f, 200f)
doReturn(point).whenever(presenterSpy).getPoint(any())
whenever(curveSettings.precision).thenReturn(200)
val startValue = 10
val remainingPointsValue = 100
val remainingPoints = presenterSpy.addPointsToCurve(startValue, remainingPointsValue)
verify(points, times(remainingPointsValue)).addPoint(point)
assertThat(remainingPoints).isEqualTo(0)
}
@Test
fun getCorrectPoint() {
val presenterSpy = spy(presenter)
whenever(presenterSpy.getT(1)).thenReturn(10.9f)
presenterSpy.getPoint(1)
verify(view).getGraphX(10.9f)
verify(view).getGraphY(10.9f)
}
@Test
fun getT() {
whenever(curveSettings.precision).thenReturn(10)
presenter.getT(1)
verify(view).getT(1, curveSettings.precision)
}
}
================================================
FILE: lemniscate/src/test/java/com/vlad1m1r/lemniscate/base/BaseCurveProgressViewTest.kt
================================================
package com.vlad1m1r.lemniscate.base
import android.view.View
import com.google.common.truth.Truth.assertThat
import org.mockito.kotlin.*
import org.junit.Before
import org.junit.Test
class BaseCurveProgressViewTest {
val baseCurve: BaseCurve = BaseCurve()
val baseCurveProgressView = mock<BaseCurveProgressView>()
@Before
fun setUp() {
doCallRealMethod().whenever(baseCurveProgressView).getMaxViewSquareSize(any(), any(), any(), any())
doCallRealMethod().whenever(baseCurveProgressView).getViewDimension(any(), any(), any())
}
@Test
fun getMaxViewSquareSize() {
assertThat(baseCurveProgressView.getMaxViewSquareSize(100, 200, 30, 50)).isEqualTo(100-50)
assertThat(baseCurveProgressView.getMaxViewSquareSize(220, 150, 30, 50)).isEqualTo(150-30)
}
@Test
fun getViewDimension_whenViewSizeIsZero() {
val defaultSize = 10f
assertThat(baseCurveProgressView.getViewDimension(View.MeasureSpec.AT_MOST, 0f, defaultSize)).isEqualTo(defaultSize)
}
@Test
fun getViewDimension_whenMeasureSpecIsExactly() {
val viewSize = 10f
assertThat(baseCurveProgressView.getViewDimension(View.MeasureSpec.EXACTLY, viewSize, 10f)).isEqualTo(viewSize)
}
@Test
fun getViewDimension_whenMeasureSpecIsAtMost() {
assertThat(baseCurveProgressView.getViewDimension(View.MeasureSpec.AT_MOST, 10f, 20f)).isEqualTo(10f)
assertThat(baseCurveProgressView.getViewDimension(View.MeasureSpec.AT_MOST, 30f, 20f)).isEqualTo(20f)
}
@Test
fun getViewDimension_whenMeasureSpecIsUnspecified() {
val defaultSize = 10f
assertThat(baseCurveProgressView.getViewDimension(View.MeasureSpec.UNSPECIFIED, 20f, defaultSize)).isEqualTo(defaultSize)
}
@Test
fun getT() {
assertThat(baseCurve.getT(0, 10)).isEqualTo(0.0f)
assertThat(baseCurve.getT(1, 10)).isEqualTo(0.62831855f)
assertThat(baseCurve.getT(10, 10)).isEqualTo(6.2831855f)
assertThat(baseCurve.getT(99, 8)).isEqualTo(77.75442f)
assertThat(baseCurve.getT(-1, 10)).isEqualTo(-0.62831855f)
assertThat(baseCurve.getT(-10, 10)).isEqualTo(-6.2831855f)
}
inner class BaseCurve : IBaseCurveView {
override fun getGraphX(t: Float): Float {
return 0f
}
override fun getGraphY(t: Float): Float {
return 0f
}
override fun invalidateProgressView() {}
override fun requestProgressViewLayout() {}
}
}
================================================
FILE: lemniscate/src/test/java/com/vlad1m1r/lemniscate/base/BaseProgressViewAttributesTest.kt
================================================
package com.vlad1m1r.lemniscate.base
import android.graphics.Color
import android.os.Build
import androidx.test.platform.app.InstrumentationRegistry
import com.google.common.truth.Truth.assertThat
import com.vlad1m1r.lemniscate.BernoullisProgressView
import com.vlad1m1r.lemniscate.R
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.Robolectric
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [Build.VERSION_CODES.P])
class BaseProgressViewAttributesTest {
val context = InstrumentationRegistry.getInstrumentation().targetContext
val atributeSet = Robolectric.buildAttributeSet()
.addAttribute(R.attr.maxLineLength, "0.81")
.addAttribute(R.attr.minLineLength, "0.23")
.addAttribute(R.attr.lineColor, "#000000")
.addAttribute(R.attr.hasHole, "true")
.addAttribute(R.attr.strokeWidth, "33px")
.addAttribute(R.attr.precision, "111")
.addAttribute(R.attr.duration, "999")
.build()
@Test
fun constructFromAttributeSet_whenProvided() {
val bernoullisProgressView = BernoullisProgressView(context, atributeSet)
assertThat(bernoullisProgressView.lineMaxLength).isEqualTo(0.81f)
assertThat(bernoullisProgressView.lineMinLength).isEqualTo(0.23f)
assertThat(bernoullisProgressView.color).isEqualTo(Color.BLACK)
assertThat(bernoullisProgressView.hasHole).isTrue()
assertThat(bernoullisProgressView.strokeWidth).isEqualTo(33f)
assertThat(bernoullisProgressView.precision).isEqualTo(111)
assertThat(bernoullisProgressView.duration).isEqualTo(999)
}
}
================================================
FILE: lemniscate/src/test/java/com/vlad1m1r/lemniscate/base/models/DrawStateTest.kt
================================================
package com.vlad1m1r.lemniscate.base.models
import android.graphics.Path
import com.google.common.truth.Truth.assertThat
import org.mockito.kotlin.*
import com.vlad1m1r.lemniscate.base.settings.CurveSettings
import org.junit.Test
import org.mockito.Mockito.inOrder
class DrawStateTest {
val path = mock<Path>()
val drawState = DrawState(path)
@Test
fun addPairOfPoints() {
val start = Point(0f,1f, 2f, 100f)
val end = Point(2f, 3f, 2f, 100f)
drawState.addPairOfPointsToPath(start, end)
verify(path).moveTo(start.x, start.y)
verify(path).quadTo(start.x, start.y, end.x, end.y)
verifyNoMoreInteractions(path)
}
@Test
fun addStartPoints() {
val start = Point(0f,1f, 2f, 100f)
drawState.addPairOfPointsToPath(start, null)
verify(path).moveTo(start.x, start.y)
verify(path).lineTo(start.x, start.y)
verifyNoMoreInteractions(path)
}
@Test
fun addEndPoints() {
val end = Point(2f, 3f, 2f, 100f)
drawState.addPairOfPointsToPath(null, end)
verify(path).moveTo(end.x, end.y)
verifyNoMoreInteractions(path)
}
@Test
fun addPointsToPathWhenListEmpty() {
val curveSettings = mock<CurveSettings>()
val viewSize = mock<ViewSize>()
drawState.addPointsToPath(emptyList(), curveSettings, viewSize)
verify(path).reset()
verifyNoMoreInteractions(path)
}
@Test
fun addPointsToPathWhenListHasPointsOutOfHole() {
val list = listOf(
Point(5f, 0f, 1f, 100f),
Point(3f, 0f, 1f, 100f)
)
val curveSettings = mock<CurveSettings>()
val viewSize = mock<ViewSize>()
whenever(curveSettings.hasHole).thenReturn(false)
whenever(curveSettings.strokeWidth).thenReturn(1f)
val drawStateSpy = spy(drawState)
drawStateSpy.addPointsToPath(list, curveSettings, viewSize)
verify(drawStateSpy).addPairOfPointsToPath(list[0], list[1])
verify(drawStateSpy).addPairOfPointsToPath(list[1], null)
}
@Test
fun addPointsToPathWhenListHasPointsInHole() {
val list = listOf(
Point(5f, 0f, 100f, 10f),
Point(3f, 0f, 100f, 10f)
)
val curveSettings = mock<CurveSettings>()
val viewSize = mock<ViewSize>()
whenever(curveSettings.hasHole).thenReturn(true)
whenever(curveSettings.strokeWidth).thenReturn(100f)
val drawStateSpy = spy(drawState)
drawStateSpy.addPointsToPath(list, curveSettings, viewSize)
verify(drawStateSpy).addPairOfPointsToPath(null, null)
}
@Test
fun recalculateLineLength() {
val drawStateSpy = spy(drawState)
val inOrder = inOrder(drawStateSpy)
val lineLength = LineLength()
drawStateSpy.recalculateLineLength(lineLength)
inOrder.verify(drawStateSpy).keepLineLengthInsideLimits(lineLength)
inOrder.verify(drawStateSpy).calculateNewCurrentLineLength(lineLength)
inOrder.verifyNoMoreInteractions()
}
@Test
fun recalculateLineLengthWhenMinGreaterThanMax() {
val lineLength = LineLength()
lineLength.lineMinLength = 0.8f
lineLength.lineMaxLength = 0.6f
drawState.recalculateLineLength(lineLength)
assertThat(drawState.currentLineLength).isEqualTo(lineLength.lineMaxLength)
}
@Test
fun keepLineLengthInsideLimitsWhenCurrentLessThanMin() {
val lineLength = LineLength()
lineLength.lineMinLength = 0.5f
drawState.currentLineLength = 0.4f
drawState.keepLineLengthInsideLimits(lineLength)
assertThat(drawState.currentLineLength).isEqualTo(lineLength.lineMinLength)
}
@Test
fun keepLineLengthInsideLimitsWhenCurrentGreaterThanMax() {
val lineLength = LineLength()
lineLength.lineMaxLength = 0.5f
drawState.currentLineLength = 0.7f
drawState.keepLineLengthInsideLimits(lineLength)
assertThat(drawState.currentLineLength).isEqualTo(lineLength.lineMaxLength)
}
@Test
fun calculateNewCurrentLineLengthWhenLessThanMaxAndExpanding() {
val lineLength = LineLength()
lineLength.lineMaxLength = 0.7f
drawState.isExpanding = true
drawState.currentLineLength = 0.5f
drawState.calculateNewCurrentLineLength(lineLength)
assertThat(drawState.currentLineLength).isEqualTo(0.501f)
}
@Test
fun calculateNewCurrentLineLengthWhenGreaterThanMinAndNotExpanding() {
val lineLength = LineLength()
lineLength.lineMinLength = 0.2f
drawState.isExpanding = false
drawState.currentLineLength = 0.5f
drawState.calculateNewCurrentLineLength(lineLength)
assertThat(drawState.currentLineLength).isEqualTo(0.499f)
}
@Test
fun calculateNewCurrentLineLengthWhenEqualMaxAndExpanding() {
val lineLength = LineLength()
drawState.currentLineLength = lineLength.lineMaxLength
drawState.isExpanding = true
drawState.calculateNewCurrentLineLength(lineLength)
assertThat(drawState.currentLineLength).isEqualTo(lineLength.lineMaxLength)
assertThat(drawState.isExpanding).isFalse()
}
@Test
fun calculateNewCurrentLineLengthWhenEqualMinAndNotExpanding() {
val lineLength = LineLength()
drawState.currentLineLength = lineLength.lineMinLength
drawState.isExpanding = false
drawState.calculateNewCurrentLineLength(lineLength)
assertThat(drawState.currentLineLength).isEqualTo(lineLength.lineMinLength)
assertThat(drawState.isExpanding).isTrue()
}
@Test(expected = IllegalArgumentException::class)
fun calculateNewCurrentLineLengthIsNotInsideLimits() {
val lineLength = LineLength()
drawState.currentLineLength = lineLength.lineMaxLength + 1
drawState.calculateNewCurrentLineLength(lineLength)
}
@Test
fun resetPath() {
drawState.resetPath()
verify(path).reset()
}
}
================================================
FILE: lemniscate/src/test/java/com/vlad1m1r/lemniscate/base/models/LineLengthParcelableTest.kt
================================================
package com.vlad1m1r.lemniscate.base.models
import android.os.Build
import android.os.Parcel
import com.google.common.truth.Truth.assertThat
import com.vlad1m1r.lemniscate.testutils.isEqualTo
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [Build.VERSION_CODES.P])
class LineLengthParcelableTest {
private lateinit var lineLength: LineLength
@Before
fun setUp() {
lineLength = LineLength().apply {
lineMinLength = 0.24f
lineMaxLength = 0.83f
}
}
@Test
fun parcelable() {
val parcel = Parcel.obtain()
lineLength.writeToParcel(parcel, 0)
parcel.setDataPosition(0)
val copy = LineLength(parcel)
parcel.recycle()
assertThat(lineLength.isEqualTo(copy)).isTrue()
}
}
================================================
FILE: lemniscate/src/test/java/com/vlad1m1r/lemniscate/base/models/LineLengthTest.kt
================================================
package com.vlad1m1r.lemniscate.base.models
import com.google.common.truth.Truth.assertThat
import org.junit.Test
class LineLengthTest {
private val lineLength = LineLength()
@Test
fun getLineMaxLength() {
lineLength.lineMaxLength = 0.9f
assertThat(lineLength.lineMaxLength).isEqualTo(0.9f)
}
@Test(expected = IllegalArgumentException::class)
fun setLineMaxLengthGreaterThan1ThrowsException() {
lineLength.lineMaxLength = 1.1f
}
@Test(expected = IllegalArgumentException::class)
fun setLineMaxLengthEqualTo0ThrowsException() {
lineLength.lineMaxLength = 0.0f
}
@Test(expected = IllegalArgumentException::class)
fun setLineMaxLengthLestThan0ThrowsException() {
lineLength.lineMaxLength = -1.0f
}
@Test
fun setLineMinLength() {
lineLength.lineMinLength = 0.1f
assertThat(lineLength.lineMinLength).isEqualTo(0.1f)
}
@Test(expected = IllegalArgumentException::class)
fun setLineMinLengthLestThan0ThrowsException() {
lineLength.lineMinLength = -0.1f
}
@Test(expected = IllegalArgumentException::class)
fun setLineMinLengthGreaterThan1ThrowsException() {
lineLength.lineMinLength = 1.1f
}
@Test(expected = IllegalArgumentException::class)
fun setLineMinLengthEqualTo0ThrowsException() {
lineLength.lineMinLength = 0.0f
}
}
================================================
FILE: lemniscate/src/test/java/com/vlad1m1r/lemniscate/base/models/PointTest.kt
================================================
package com.vlad1m1r.lemniscate.base.models
import com.google.common.truth.Truth.assertThat
import org.junit.Test
class PointTest {
@Test
fun translatesPoints_whenCreated() {
val point = Point(0.0f, 30.0f, 30.0f, 270.0f)
assertThat(point.x).isEqualTo(135.0f)
assertThat(point.y).isEqualTo(159.545455f)
}
}
================================================
FILE: lemniscate/src/test/java/com/vlad1m1r/lemniscate/base/models/PointsTest.kt
================================================
package com.vlad1m1r.lemniscate.base.models
import com.google.common.truth.Truth.assertThat
import org.junit.Test
class PointsTest {
val points = Points()
val point = Point(0f, 0f, 10f, 10f)
@Test
fun isNotEmpty_whenPointIsAdded() {
assertThat(points.isEmpty).isTrue()
points.addPoint(point)
assertThat(points.isEmpty).isFalse()
}
@Test
fun containsPoint_whenPointIsAdded() {
points.addPoint(point)
assertThat(points.getPoints()).containsExactly(point)
}
@Test
fun isEmpty_whenClearIsCalled() {
points.addPoint(point)
points.clear()
assertThat(points.isEmpty).isTrue()
}
}
================================================
FILE: lemniscate/src/test/java/com/vlad1m1r/lemniscate/base/settings/AnimationSettingsParcelableTest.kt
================================================
package com.vlad1m1r.lemniscate.base.settings
import android.os.Build
import android.os.Parcel
import com.google.common.truth.Truth.assertThat
import com.vlad1m1r.lemniscate.testutils.isEqualTo
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [Build.VERSION_CODES.P])
class AnimationSettingsParcelableTest {
private lateinit var animationSettings: AnimationSettings
@Before
fun setUp() {
animationSettings = AnimationSettings(123, 987)
}
@Test
fun parcelable() {
val parcel = Parcel.obtain()
animationSettings.writeToParcel(parcel, 0)
parcel.setDataPosition(0)
val copy = AnimationSettings(parcel)
parcel.recycle()
assertThat(animationSettings.isEqualTo(copy)).isTrue()
}
}
================================================
FILE: lemniscate/src/test/java/com/vlad1m1r/lemniscate/base/settings/CurveSettingsParcelableTest.kt
================================================
package com.vlad1m1r.lemniscate.base.settings
import android.os.Build
import android.os.Parcel
import com.vlad1m1r.lemniscate.base.models.LineLength
import com.vlad1m1r.lemniscate.testutils.isEqualTo
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [Build.VERSION_CODES.P])
class CurveSettingsParcelableTest {
private lateinit var curveSettings: CurveSettings
@Before
fun setUp() {
curveSettings = CurveSettings().apply {
color = 123
hasHole = true
lineLength = LineLength().apply {
lineMaxLength = 0.85f
lineMinLength = 0.26f
}
strokeWidth = 23.2f
precision = 123
}
}
@Test
fun parcelable() {
val parcel = Parcel.obtain()
curveSettings.writeToParcel(parcel, 0)
parcel.setDataPosition(0)
val copy = CurveSettings(parcel)
parcel.recycle()
curveSettings.isEqualTo(copy)
}
}
================================================
FILE: lemniscate/src/test/java/com/vlad1m1r/lemniscate/base/settings/CurveSettingsTest.kt
================================================
package com.vlad1m1r.lemniscate.base.settings
import android.graphics.Paint
import com.google.common.truth.Truth.assertThat
import org.mockito.kotlin.mock
import com.vlad1m1r.lemniscate.base.models.LineLength
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnitRunner
@RunWith(MockitoJUnitRunner::class)
class CurveSettingsTest {
private lateinit var curveSettings: CurveSettings
val paint = mock<Paint>()
@Before
fun setUp() {
val lineLength = LineLength()
curveSettings = CurveSettings(paint)
curveSettings.lineLength = lineLength
}
@Test
fun setStrokeWidth() {
curveSettings.strokeWidth = 10.0f
assertThat(curveSettings.strokeWidth).isEqualTo(10.0f)
verify(paint).strokeWidth = 10f
}
@Test(expected = IllegalArgumentException::class)
fun setStrokeWidthException() {
curveSettings.strokeWidth = -1.0f
}
@Test
fun setColor() {
curveSettings.color = 123
assertEquals(123, curveSettings.color.toLong())
verify(paint).color = 123
}
}
================================================
FILE: lemniscate/src/test/java/com/vlad1m1r/lemniscate/funny/CannabisProgressViewTest.kt
================================================
package com.vlad1m1r.lemniscate.funny
import android.os.Build
import androidx.test.platform.app.InstrumentationRegistry
import com.google.common.truth.Truth.assertThat
import org.mockito.kotlin.mock
import com.vlad1m1r.lemniscate.testutils.TestConstants
import com.vlad1m1r.lemniscate.testutils.isPeriodic
import com.vlad1m1r.lemniscate.testutils.setupDefaultMock
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import kotlin.math.PI
class CannabisProgressViewTest {
private val view = mock<CannabisProgressView>()
@Before
fun setUp() {
view.setupDefaultMock()
}
@Test
fun getGraphX() {
assertThat(view.getGraphX(0.0f)).isWithin(TestConstants.DELTA).of(34.833332f)
assertThat(view.getGraphX(0.1f)).isWithin(TestConstants.DELTA).of(25.860207f)
assertThat(view.getGraphX(0.5f)).isWithin(TestConstants.DELTA).of(9.527859f)
assertThat(view.getGraphX(1.0f)).isWithin(TestConstants.DELTA).of(14.251956f)
assertThat(view.getGraphX(PI.toFloat())).isWithin(TestConstants.DELTA).of(-34.83333f)
assertThat(view.getGraphX(2.0f)).isWithin(TestConstants.DELTA).of(-1.4506966f)
assertThat(view.getGraphX(2 * PI.toFloat())).isWithin(TestConstants.DELTA).of(34.833332f)
}
@Test
fun getGraphY() {
assertThat(view.getGraphY(0.0f)).isWithin(TestConstants.DELTA).of(25.0f)
assertThat(view.getGraphY(0.1f)).isWithin(TestConstants.DELTA).of(22.405325f)
assertThat(view.getGraphY(0.5f)).isWithin(TestConstants.DELTA).of(19.794907f)
assertThat(view.getGraphY(1.0f)).isWithin(TestConstants.DELTA).of(2.803894f)
assertThat(view.getGraphY(PI.toFloat())).isWithin(TestConstants.DELTA).of(25.0f)
assertThat(view.getGraphY(2.0f)).isWithin(TestConstants.DELTA).of(21.83017f)
assertThat(view.getGraphY(2 * PI.toFloat())).isWithin(TestConstants.DELTA).of(25.0f)
}
@Test
fun isPeriodic() {
view.isPeriodic(2 * PI.toFloat())
}
}
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [Build.VERSION_CODES.P])
class CannabisProgressViewHasHoleTest {
val context = InstrumentationRegistry.getInstrumentation().targetContext
private val view = CannabisProgressView(context)
@Test
fun hasHoleDisabled() {
view.hasHole = true
assertThat(view.hasHole).isFalse()
}
}
================================================
FILE: lemniscate/src/test/java/com/vlad1m1r/lemniscate/funny/HeartProgressViewTest.kt
================================================
package com.vlad1m1r.lemniscate.funny
import android.os.Build
import androidx.test.platform.app.InstrumentationRegistry
import com.google.common.truth.Truth.assertThat
import org.mockito.kotlin.mock
import com.vlad1m1r.lemniscate.testutils.TestConstants
import com.vlad1m1r.lemniscate.testutils.isPeriodic
import com.vlad1m1r.lemniscate.testutils.setupDefaultMock
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import kotlin.math.PI
class HeartProgressViewTest{
private val view = mock<HeartProgressView>()
@Before
fun setUp() {
view.setupDefaultMock()
}
@Test
fun getGraphX() {
assertThat(view.getGraphX(0.0f)).isWithin(TestConstants.DELTA).of(0.0f)
assertThat(view.getGraphX(0.1f)).isWithin(TestConstants.DELTA).of(0.046824045f)
assertThat(view.getGraphX(0.5f)).isWithin(TestConstants.DELTA).of(5.1856666f)
assertThat(view.getGraphX(1.0f)).isWithin(TestConstants.DELTA).of(28.038736f)
assertThat(view.getGraphX(PI.toFloat())).isWithin(TestConstants.DELTA).of(0.0f)
assertThat(view.getGraphX(2.0f)).isWithin(TestConstants.DELTA).of(35.38009f)
assertThat(view.getGraphX(2 * PI.toFloat())).isWithin(TestConstants.DELTA).of(0.0f)
}
@Test
fun getGraphY() {
assertThat(view.getGraphY(0.0f)).isWithin(TestConstants.DELTA).of(-14.705882f)
assertThat(view.getGraphY(0.1f)).isWithin(TestConstants.DELTA).of(-15.302903f)
assertThat(view.getGraphY(0.5f)).isWithin(TestConstants.DELTA).of(-26.416864f)
assertThat(view.getGraphY(1.0f)).isWithin(TestConstants.DELTA).of(-34.52439f)
assertThat(view.getGraphY(PI.toFloat())).isWithin(TestConstants.DELTA).of(50.0f)
assertThat(view.getGraphY(2.0f)).isWithin(TestConstants.DELTA).of(11.51921f)
assertThat(view.getGraphY(2 * PI.toFloat())).isWithin(TestConstants.DELTA).of(-14.705882f)
}
@Test
fun isPeriodic() {
view.isPeriodic(2 * PI.toFloat())
}
}
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [Build.VERSION_CODES.P])
class HeartProgressViewHasHoleTest {
val context = InstrumentationRegistry.getInstrumentation().targetContext
private val view = HeartProgressView(context)
@Test
fun hasHoleDisabled() {
view.hasHole = true
assertThat(view.hasHole).isFalse()
}
}
================================================
FILE: lemniscate/src/test/java/com/vlad1m1r/lemniscate/other/XProgressViewTest.kt
================================================
package com.vlad1m1r.lemniscate.other
import android.os.Build
import androidx.test.platform.app.InstrumentationRegistry
import com.google.common.truth.Truth.assertThat
import org.mockito.kotlin.mock
import com.vlad1m1r.lemniscate.testutils.TestConstants
import com.vlad1m1r.lemniscate.testutils.isPeriodic
import com.vlad1m1r.lemniscate.testutils.setupDefaultMock
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import kotlin.math.PI
class XProgressViewTest {
private val view = mock<XProgressView>()
@Before
fun setUp() {
view.setupDefaultMock()
}
@Test
fun getGraphX() {
assertThat(view.getGraphX(0.0f)).isWithin(TestConstants.DELTA).of(0.0f)
assertThat(view.getGraphX(0.1f)).isWithin(TestConstants.DELTA).of(9.933467f)
assertThat(view.getGraphX(0.5f)).isWithin(TestConstants.DELTA).of(42.073547f)
assertThat(view.getGraphX(1.0f)).isWithin(TestConstants.DELTA).of(45.464867f)
assertThat(view.getGraphX(PI.toFloat())).isWithin(TestConstants.DELTA).of(0.0f)
assertThat(view.getGraphX(2.0f)).isWithin(TestConstants.DELTA).of(-37.840126f)
assertThat(view.getGraphX(2 * PI.toFloat())).isWithin(TestConstants.DELTA).of(0.0f)
}
@Test
fun getGraphY() {
assertThat(view.getGraphY(0.0f)).isWithin(TestConstants.DELTA).of(0.0f)
assertThat(view.getGraphY(0.1f)).isWithin(TestConstants.DELTA).of(9.933467f)
assertThat(view.getGraphY(0.5f)).isWithin(TestConstants.DELTA).of(42.073547f)
assertThat(view.getGraphY(1.0f)).isWithin(TestConstants.DELTA).of(45.464867f)
assertThat(view.getGraphY(PI.toFloat())).isWithin(TestConstants.DELTA).of(0.0f)
assertThat(view.getGraphY(2.0f)).isWithin(TestConstants.DELTA).of(-37.840126f)
assertThat(view.getGraphY(2 * PI.toFloat())).isWithin(TestConstants.DELTA).of(0.0f)
}
@Test
fun isPeriodic() {
view.isPeriodic(2 * PI.toFloat())
}
}
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [Build.VERSION_CODES.P])
class XProgressViewHasHoleTest {
val context = InstrumentationRegistry.getInstrumentation().targetContext
private val view = XProgressView(context)
@Test
fun hasHoleDisabled() {
view.hasHole = true
assertThat(view.hasHole).isFalse()
}
}
================================================
FILE: lemniscate/src/test/java/com/vlad1m1r/lemniscate/roulette/BaseRouletteProgressViewAttributesTest.kt
================================================
package com.vlad1m1r.lemniscate.roulette
import android.os.Build
import androidx.test.platform.app.InstrumentationRegistry
import com.google.common.truth.Truth.assertThat
import com.vlad1m1r.lemniscate.R
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.Robolectric
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [Build.VERSION_CODES.P])
class BaseRouletteProgressViewAttributesTest {
val context = InstrumentationRegistry.getInstrumentation().targetContext
val atributeSet = Robolectric.buildAttributeSet()
.addAttribute(R.attr.radiusFixed, "34")
.addAttribute(R.attr.radiusMoving, "23")
.addAttribute(R.attr.numberOfCycles, "43")
.addAttribute(R.attr.distanceFromCenter, "31")
.build()
@Test
fun constructorWithAttributeSet() {
val epitrochoidProgressView = EpitrochoidProgressView(context, atributeSet)
assertThat(epitrochoidProgressView.radiusFixed).isEqualTo(34f)
assertThat(epitrochoidProgressView.radiusMoving).isEqualTo(23f)
assertThat(epitrochoidProgressView.numberOfCycles).isEqualTo(43f)
assertThat(epitrochoidProgressView.distanceFromCenter).isEqualTo(31f)
}
}
================================================
FILE: lemniscate/src/test/java/com/vlad1m1r/lemniscate/roulette/EpitrochoidProgressViewTest.kt
================================================
package com.vlad1m1r.lemniscate.roulette
import android.os.Build
import androidx.test.platform.app.InstrumentationRegistry
import com.google.common.truth.Truth.assertThat
import org.mockito.kotlin.*
import com.vlad1m1r.lemniscate.testutils.TestConstants.DELTA
import com.vlad1m1r.lemniscate.testutils.isPeriodic
import com.vlad1m1r.lemniscate.testutils.setupDefaultMock
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import kotlin.math.PI
class EpitrochoidProgressViewTest {
private val view = mock<EpitrochoidProgressView>()
@Before
fun setUp() {
view.setupDefaultMock()
doCallRealMethod().whenever(view).radiusSum
doCallRealMethod().whenever(view).sizeFactor
}
@Test
fun getGraphX() {
assertThat(view.getGraphX(0.0f)).isWithin(DELTA).of(30.0f)
assertThat(view.getGraphX(0.1f)).isWithin(DELTA).of(30.589556f)
assertThat(view.getGraphX(0.5f)).isWithin(DELTA).of(39.26477f)
assertThat(view.getGraphX(1.0f)).isWithin(DELTA).of(28.14853f)
assertThat(view.getGraphX(PI.toFloat())).isWithin(DELTA).of(-50.0f)
assertThat(view.getGraphX(2.0f)).isWithin(DELTA).of(-15.190873f)
assertThat(view.getGraphX(2 * PI.toFloat())).isWithin(DELTA).of(30.0f)
}
@Test
fun getGraphY() {
assertThat(view.getGraphY(0.0f)).isWithin(DELTA).of(0.0f)
assertThat(view.getGraphY(0.1f)).isWithin(DELTA).of(0.09915324f)
assertThat(view.getGraphY(0.5f)).isWithin(DELTA).of(10.084047f)
assertThat(view.getGraphY(1.0f)).isWithin(DELTA).of(41.226864f)
assertThat(view.getGraphY(PI.toFloat())).isWithin(DELTA).of(9.797174E-15f)
assertThat(view.getGraphY(2.0f)).isWithin(DELTA).of(26.478315f)
assertThat(view.getGraphY(2 * PI.toFloat())).isWithin(DELTA).of(0.0f)
}
@Test
fun isPeriodic() {
view.isPeriodic(2 * PI.toFloat())
}
}
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [Build.VERSION_CODES.P])
class EpitrochoidProgressViewHasHoleTest {
val context = InstrumentationRegistry.getInstrumentation().targetContext
private val view = EpitrochoidProgressView(context)
@Test
fun hasHoleDisabled() {
view.hasHole = true
assertThat(view.hasHole).isFalse()
}
}
================================================
FILE: lemniscate/src/test/java/com/vlad1m1r/lemniscate/roulette/HypotrochoidProgressViewTest.kt
================================================
package com.vlad1m1r.lemniscate.roulette
import android.os.Build
import androidx.test.platform.app.InstrumentationRegistry
import com.google.common.truth.Truth.assertThat
import org.mockito.kotlin.*
import com.vlad1m1r.lemniscate.testutils.TestConstants.DELTA
import com.vlad1m1r.lemniscate.testutils.isPeriodic
import com.vlad1m1r.lemniscate.testutils.setupDefaultMock
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import kotlin.math.PI
class HypotrochoidProgressViewTest {
private val view = mock<HypotrochoidProgressView>()
@Before
fun setUp() {
view.setupDefaultMock()
doCallRealMethod().whenever(view).radiusDiff
doCallRealMethod().whenever(view).sizeFactor
}
@Test
fun getGraphX() {
assertThat(view.getGraphX(0.0f)).isWithin(DELTA).of(16.666666f)
assertThat(view.getGraphX(0.1f)).isWithin(DELTA).of(16.832361f)
assertThat(view.getGraphX(0.5f)).isWithin(DELTA).of(20.247713f)
assertThat(view.getGraphX(1.0f)).isWithin(DELTA).of(24.945856f)
assertThat(view.getGraphX(PI.toFloat())).isWithin(DELTA).of(-50.0f)
assertThat(view.getGraphX(2.0f)).isWithin(DELTA).of(-2.9775007f)
assertThat(view.getGraphX(2 * PI.toFloat())).isWithin(DELTA).of(16.666666f)
}
@Test
fun getGraphY() {
assertThat(view.getGraphY(0.0f)).isWithin(DELTA).of(0.0f)
assertThat(view.getGraphY(0.1f)).isWithin(DELTA).of(6.638936f)
assertThat(view.getGraphY(0.5f)).isWithin(DELTA).of(30.005367f)
assertThat(view.getGraphY(1.0f)).isWithin(DELTA).of(43.203987f)
assertThat(view.getGraphY(PI.toFloat())).isWithin(DELTA).of(0.0f)
assertThat(view.getGraphY(2.0f)).isWithin(DELTA).of(17.696539f)
assertThat(view.getGraphY(2 * PI.toFloat())).isWithin(DELTA).of(0.0f)
}
@Test
fun isPeriodic() {
view.isPeriodic(2 * PI.toFloat())
}
}
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [Build.VERSION_CODES.P])
class HypotrochoidProgressViewHasHoleTest {
val context = InstrumentationRegistry.getInstrumentation().targetContext
private val view = HypotrochoidProgressView(context)
@Test
fun hasHoleDisabled() {
view.hasHole = true
assertThat(view.hasHole).isFalse()
}
}
================================================
FILE: lemniscate/src/test/java/com/vlad1m1r/lemniscate/roulette/scribble/RoundScribbleProgressViewTest.kt
================================================
package com.vlad1m1r.lemniscate.roulette.scribble
import android.os.Build
import androidx.test.platform.app.InstrumentationRegistry
import com.google.common.truth.Truth.assertThat
import org.mockito.kotlin.*
import com.vlad1m1r.lemniscate.testutils.TestConstants.DELTA
import com.vlad1m1r.lemniscate.testutils.isPeriodic
import com.vlad1m1r.lemniscate.testutils.setupDefaultMock
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import kotlin.math.PI
class RoundScribbleProgressViewTest {
private val view = mock<RoundScribbleProgressView>()
@Before
fun setUp() {
view.setupDefaultMock()
doCallRealMethod().whenever(view).radiusSum
doCallRealMethod().whenever(view).sizeFactor
}
@Test
fun getGraphX() {
assertThat(view.getGraphX(0.0f)).isWithin(DELTA).of(30.0f)
assertThat(view.getGraphX(0.1f)).isWithin(DELTA).of(30.589556f)
assertThat(view.getGraphX(0.5f)).isWithin(DELTA).of(39.26477f)
assertThat(view.getGraphX(1.0f)).isWithin(DELTA).of(28.14853f)
assertThat(view.getGraphX(PI.toFloat())).isWithin(DELTA).of(-50.0f)
assertThat(view.getGraphX(2.0f)).isWithin(DELTA).of(-15.190873f)
assertThat(view.getGraphX(2 * PI.toFloat())).isWithin(DELTA).of(30.0f)
}
@Test
fun getGraphY() {
assertThat(view.getGraphY(0.0f)).isWithin(DELTA).of(40.0f)
assertThat(view.getGraphY(0.1f)).isWithin(DELTA).of(35.905983f)
assertThat(view.getGraphY(0.5f)).isWithin(DELTA).of(26.010328f)
assertThat(view.getGraphY(1.0f)).isWithin(DELTA).of(29.180117f)
assertThat(view.getGraphY(PI.toFloat())).isWithin(DELTA).of(-40.0f)
assertThat(view.getGraphY(2.0f)).isWithin(DELTA).of(-26.539455f)
assertThat(view.getGraphY(2 * PI.toFloat())).isWithin(DELTA).of(40.0f)
}
@Test
fun isPeriodic() {
view.isPeriodic(2 * PI.toFloat())
}
}
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [Build.VERSION_CODES.P])
class RoundScribbleProgressViewHasHoleTest {
val context = InstrumentationRegistry.getInstrumentation().targetContext
private val view = RoundScribbleProgressView(context)
@Test
fun hasHoleDisabled() {
view.hasHole = true
assertThat(view.hasHole).isFalse()
}
}
================================================
FILE: lemniscate/src/test/java/com/vlad1m1r/lemniscate/roulette/scribble/ScribbleProgressViewTest.kt
================================================
package com.vlad1m1r.lemniscate.roulette.scribble
import android.os.Build
import androidx.test.platform.app.InstrumentationRegistry
import com.google.common.truth.Truth.assertThat
import org.mockito.kotlin.*
import com.vlad1m1r.lemniscate.testutils.TestConstants.DELTA
import com.vlad1m1r.lemniscate.testutils.isPeriodic
import com.vlad1m1r.lemniscate.testutils.setupDefaultMock
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.junit.MockitoJUnitRunner
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import kotlin.math.PI
@RunWith(MockitoJUnitRunner::class)
class ScribbleProgressViewTest {
private val view = mock<ScribbleProgressView>()
@Before
fun setUp() {
view.setupDefaultMock()
doCallRealMethod().whenever(view).radiusSum
doCallRealMethod().whenever(view).sizeFactor
}
@Test
fun getGraphX() {
assertThat(view.getGraphX(0.0f)).isWithin(DELTA).of(30.0f)
assertThat(view.getGraphX(0.1f)).isWithin(DELTA).of(30.589556f)
assertThat(view.getGraphX(0.5f)).isWithin(DELTA).of(39.26477f)
assertThat(view.getGraphX(1.0f)).isWithin(DELTA).of(28.14853f)
assertThat(view.getGraphX(PI.toFloat())).isWithin(DELTA).of(-50.0f)
assertThat(view.getGraphX(2.0f)).isWithin(DELTA).of(-15.190873f)
assertThat(view.getGraphX(2 * PI.toFloat())).isWithin(DELTA).of(30.0f)
}
@Test
fun getGraphY() {
assertThat(view.getGraphY(0.0f)).isWithin(DELTA).of(-10.0f)
assertThat(view.getGraphY(0.1f)).isWithin(DELTA).of(-5.217273f)
assertThat(view.getGraphY(0.5f)).isWithin(DELTA).of(23.33849f)
assertThat(view.getGraphY(1.0f)).isWithin(DELTA).of(40.195274f)
assertThat(view.getGraphY(PI.toFloat())).isWithin(DELTA).of(-10.0f)
assertThat(view.getGraphY(2.0f)).isWithin(DELTA).of(37.826897f)
assertThat(view.getGraphY(2 * PI.toFloat())).isWithin(DELTA).of(-10.0f)
}
@Test
fun isPeriodic() {
view.isPeriodic(2 * PI.toFloat())
}
}
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [Build.VERSION_CODES.P])
class ScribbleProgressViewHasHoleTest {
val context = InstrumentationRegistry.getInstrumentation().targetContext
private val view = ScribbleProgressView(context)
@Test
fun hasHoleDisabled() {
view.hasHole = true
assertThat(view.hasHole).isFalse()
}
}
================================================
FILE: lemniscate/src/test/java/com/vlad1m1r/lemniscate/roulette/settings/RouletteCurveSettingsParcelableTest.kt
================================================
package com.vlad1m1r.lemniscate.roulette.settings
import android.os.Build
import android.os.Parcel
import com.google.common.truth.Truth.assertThat
import com.vlad1m1r.lemniscate.testutils.isEqualTo
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [Build.VERSION_CODES.P])
class RouletteCurveSettingsParcelableTest {
private lateinit var rouletteCurveSettings: RouletteCurveSettings
@Before
fun setUp() {
rouletteCurveSettings = RouletteCurveSettings()
rouletteCurveSettings.distanceFromCenter = 0.24f
rouletteCurveSettings.numberOfCycles = 0.83f
rouletteCurveSettings.radiusFixed = 1.41f
rouletteCurveSettings.radiusMoving = 3.25f
}
@Test
fun parcelable() {
val parcel = Parcel.obtain()
rouletteCurveSettings.writeToParcel(parcel, 0)
parcel.setDataPosition(0)
val copy = RouletteCurveSettings(parcel)
parcel.recycle()
assertThat(rouletteCurveSettings.isEqualTo(copy)).isTrue()
}
}
================================================
FILE: lemniscate/src/test/java/com/vlad1m1r/lemniscate/testutils/CurveTestUtils.kt
================================================
package com.vlad1m1r.lemniscate.testutils
import com.google.common.truth.Truth
import org.mockito.kotlin.*
import com.vlad1m1r.lemniscate.base.BaseCurveProgressView
import com.vlad1m1r.lemniscate.roulette.BaseRouletteProgressView
fun BaseCurveProgressView.isPeriodic(period: Float) {
for (i in 1..10) {
val random = Math.random().toFloat()
Truth.assertThat(getGraphX(random)).isWithin(TestConstants.DELTA).of(getGraphX(random + period))
Truth.assertThat(getGraphY(random)).isWithin(TestConstants.DELTA).of(getGraphY(random + period))
}
}
fun BaseRouletteProgressView.setupDefaultMock() {
doCallRealMethod().whenever(this).getGraphX(any())
doCallRealMethod().whenever(this).getGraphY(any())
whenever(this.size).thenReturn(100f)
whenever(this.radiusFixed).thenReturn(3f)
whenever(this.radiusMoving).thenReturn(1f)
whenever(this.distanceFromCenter).thenReturn(1f)
}
fun BaseCurveProgressView.setupDefaultMock() {
doCallRealMethod().whenever(this).getGraphX(any())
doCallRealMethod().whenever(this).getGraphY(any())
whenever(this.size).thenReturn(100f)
}
================================================
FILE: lemniscate/src/test/java/com/vlad1m1r/lemniscate/testutils/EqualUtils.kt
================================================
package com.vlad1m1r.lemniscate.testutils
import com.vlad1m1r.lemniscate.base.models.LineLength
import com.vlad1m1r.lemniscate.base.settings.AnimationSettings
import com.vlad1m1r.lemniscate.base.settings.CurveSettings
import com.vlad1m1r.lemniscate.roulette.settings.RouletteCurveSettings
fun AnimationSettings.isEqualTo(animationSettings: AnimationSettings): Boolean {
return this.duration == animationSettings.duration &&
this.startingPointOnCurve == animationSettings.startingPointOnCurve
}
fun CurveSettings.isEqualTo(curveSettings: CurveSettings): Boolean {
return this.hasHole == curveSettings.hasHole &&
this.color == curveSettings.color &&
this.precision == curveSettings.precision &&
this.strokeWidth == curveSettings.strokeWidth &&
this.lineLength.isEqualTo(curveSettings.lineLength)
}
fun LineLength.isEqualTo(lineLength: LineLength): Boolean {
return this.lineMinLength == lineLength.lineMinLength &&
this.lineMaxLength == lineLength.lineMaxLength
}
fun RouletteCurveSettings.isEqualTo(rouletteCurveSettings: RouletteCurveSettings): Boolean {
return this.distanceFromCenter == rouletteCurveSettings.distanceFromCenter &&
this.numberOfCycles == rouletteCurveSettings.numberOfCycles &&
this.radiusFixed == rouletteCurveSettings.radiusFixed &&
this.radiusMoving == rouletteCurveSettings.radiusMoving
}
================================================
FILE: lemniscate/src/test/java/com/vlad1m1r/lemniscate/testutils/TestConstants.kt
================================================
package com.vlad1m1r.lemniscate.testutils
object TestConstants {
const val DELTA: Float = 0.001f
}
================================================
FILE: lemniscate/src/test/java/com/vlad1m1r/lemniscate/testutils/TestLayoutInflater.kt
================================================
package com.vlad1m1r.lemniscate.testutils
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
class TestLayoutInflater internal constructor(context: Context) : LayoutInflater(context) {
internal var resId: Int = 0
private set
internal var root: ViewGroup? = null
private set
override fun inflate(resource: Int, root: ViewGroup?): View? {
this.resId = resource
this.root = root
return null
}
override fun inflate(resource: Int, root: ViewGroup?, attachToRoot: Boolean): View? {
this.resId = resource
this.root = root
return null
}
override fun cloneInContext(newContext: Context): LayoutInflater? {
return null
}
}
================================================
FILE: lemniscate/src/test/java/com/vlad1m1r/lemniscate/utils/CurveUtilsTest.kt
================================================
package com.vlad1m1r.lemniscate.utils
import com.google.common.truth.Truth.assertThat
import com.vlad1m1r.lemniscate.base.models.Point
import org.junit.Test
class CurveUtilsTest {
val point = Point(5f, 0f, 1f, 10f)
@Test
fun returnPoint_whenPointIsNotInHole() {
assertThat(CurveUtils.checkPointForHole(point, 0.2f, 10f)).isSameInstanceAs(point)
}
@Test
fun returnNull_whenPointIsInHole() {
assertThat(CurveUtils.checkPointForHole(point, 5.0f, 10f)).isNull()
}
@Test
fun returnNull_whenPointIsNull() {
assertThat(CurveUtils.checkPointForHole(null, 0.2f, 10f)).isNull()
}
}
================================================
FILE: lemniscate/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
================================================
mock-maker-inline
================================================
FILE: remote_data/legal/privacy_policy.html
================================================
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width'>
<title>Privacy Policy</title>
<style> body { font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; padding:1em; } </style>
</head>
<body>
<strong>Privacy Policy</strong> <p>
Vladimir Jovanovic built the Lemniscate app as
an Open Source app. This SERVICE is provided by
Vladimir Jovanovic at no cost and is intended for use as
is.
</p> <p>
This page is used to inform visitors regarding my
policies with the collection, use, and disclosure of Personal
Information if anyone decided to use my Service.
</p> <p>
If you choose to use my Service, then you agree to
the collection and use of information in relation to this
policy. The Personal Information that I collect is
used for providing and improving the Service. I will not use or share your information with
anyone except as described in this Privacy Policy.
</p> <p>
The terms used in this Privacy Policy have the same meanings
as in our Terms and Conditions, which are accessible at
Lemniscate unless otherwise defined in this Privacy Policy.
</p> <p><strong>Information Collection and Use</strong></p> <p>
For a better experience, while using our Service, I
may require you to provide us with certain personally
identifiable information. The information that
I request will be retained on your device and is not collected by me in any way.
</p> <!----> <p><strong>Log Data</strong></p> <p>
I want to inform you that whenever you
use my Service, in a case of an error in the app
I collect data and information (through third-party
products) on your phone called Log Data. This Log Data may
include information such as your device Internet Protocol
(“IP”) address, device name, operating system version, the
configuration of the app when utilizing my Service,
the time and date of your use of the Service, and other
statistics.
</p> <p><strong>Cookies</strong></p> <p>
Cookies are files with a small amount of data that are
commonly used as anonymous unique identifiers. These are sent
to your browser from the websites that you visit and are
stored on your device's internal memory.
</p> <p>
This Service does not use these “cookies” explicitly. However,
the app may use third-party code and libraries that use
“cookies” to collect information and improve their services.
You have the option to either accept or refuse these cookies
and know when a cookie is being sent to your device. If you
choose to refuse our cookies, you may not be able to use some
portions of this Service.
</p> <p><strong>Service Providers</strong></p> <p>
I may employ third-party companies and
individuals due to the following reasons:
</p> <ul><li>To facilitate our Service;</li> <li>To provide the Service on our behalf;</li> <li>To perform Service-related services; or</li> <li>To assist us in analyzing how our Service is used.</li></ul> <p>
I want to inform users of this Service
that these third parties have access to their Personal
Information. The reason is to perform the tasks assigned to
them on our behalf. However, they are obligated not to
disclose or use the information for any other purpose.
</p> <p><strong>Security</strong></p> <p>
I value your trust in providing us your
Personal Information, thus we are striving to use commercially
acceptable means of protecting it. But remember that no method
of transmission over the internet, or method of electronic
storage is 100% secure and reliable, and I cannot
guarantee its absolute security.
</p> <p><strong>Links to Other Sites</strong></p> <p>
This Service may contain links to other sites. If you click on
a third-party link, you will be directed to that site. Note
that these external sites are not operated by me.
Therefore, I strongly advise you to review the
Privacy Policy of these websites. I have
no control over and assume no responsibility for the content,
privacy policies, or practices of any third-party sites or
services.
</p> <p><strong>Children’s Privacy</strong></p> <!----> <div><p>
I do not knowingly collect personally
identifiable information from children. I
encourage all children to never submit any personally
identifiable information through
the Application and/or Services.
I encourage parents and legal guardians to monitor
their children's Internet usage and to help enforce this Policy by instructing
their children never to provide personally identifiable information through the Application and/or Services without their permission. If you have reason to believe that a child
has provided personally identifiable information to us through the Application and/or Services,
please contact us. You must also be at least 16 years of age to consent to the processing
of your personally identifiable information in your country (in some countries we may allow your parent
or guardian to do so on your behalf).
</p></div> <p><strong>Changes to This Privacy Policy</strong></p> <p>
I may update our Privacy Policy from
time to time. Thus, you are advised to review this page
periodically for any changes. I will
notify you of any changes by posting the new Privacy Policy on
this page.
</p> <p>This policy is effective as of 2022-06-18</p> <p><strong>Contact Us</strong></p> <p>
If you have any questions or suggestions about my
Privacy Policy, do not hesitate to contact me at write@vladimirj.dev.
</p> <p>This privacy policy page was created at <a href="https://privacypolicytemplate.net" target="_blank" rel="noopener noreferrer">privacypolicytemplate.net </a>and modified/generated by <a href="https://app-privacy-policy-generator.nisrulz.com/" target="_blank" rel="noopener noreferrer">App Privacy Policy Generator</a></p>
</body>
</html>
================================================
FILE: remote_data/legal/terms_and_conditions.html
================================================
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width'>
<title>Terms & Conditions</title>
<style> body { font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; padding:1em; } </style>
</head>
<body>
<strong>Terms & Conditions</strong> <p>
By downloading or using the app, these terms will
automatically apply to you – you should make sure therefore
that you read them carefully before using the app. You’re not
allowed to copy or modify the app, any part of the app, or
our trademarks in any way. You’re not allowed to attempt to
extract the source code of the app, and you also shouldn’t try
to translate the app into other languages or make derivative
versions. The app itself, and all the trademarks, copyright,
database rights, and other intellectual property rights related
to it, still belong to Vladimir Jovanovic.
</p> <p>
Vladimir Jovanovic is committed to ensuring that the app is
as useful and efficient as possible. For that reason, we
reserve the right to make changes to the app or to charge for
its services, at any time and for any reason. We will never
charge you for the app or its services without making it very
clear to you exactly what you’re paying for.
</p> <p>
The Lemniscate app stores and processes personal data that
you have provided to us, to provide my
Service. It’s your responsibility to keep your phone and
access to the app secure. We therefore recommend that you do
not jailbreak or root your phone, which is the process of
removing software restrictions and limitations imposed by the
official operating system of your device. It could make your
phone vulnerable to malware/viruses/malicious programs,
compromise your phone’s security features and it could mean
that the Lemniscate app won’t work properly or at all.
</p> <!----> <p>
You should be aware that there are certain things that
Vladimir Jovanovic will not take responsibility for. Certain
functions of the app will require the app to have an active
internet connection. The connection can be Wi-Fi or provided
by your mobile network provider, but Vladimir Jovanovic
cannot take responsibility for the app not working at full
functionality if you don’t have access to Wi-Fi, and you don’t
have any of your data allowance left.
</p> <p></p> <p>
If you’re using the app outside of an area with Wi-Fi, you
should remember that the terms of the agreement with your
mobile network provider will still apply. As a result, you may
be charged by your mobile provider for the cost of data for
the duration of the connection while accessing the app, or
other third-party charges. In using the app, you’re accepting
responsibility for any such charges, including roaming data
charges if you use the app outside of your home territory
(i.e. region or country) without turning off data roaming. If
you are not the bill payer for the device on which you’re
using the app, please be aware that we assume that you have
received permission from the bill payer for using the app.
</p> <p>
Along the same lines, Vladimir Jovanovic cannot always take
responsibility for the way you use the app i.e. You need to
make sure that your device stays charged – if it runs out of
battery and you can’t turn it on to avail the Service,
Vladimir Jovanovic cannot accept responsibility.
</p> <p>
With respect to Vladimir Jovanovic’s responsibility for your
use of the app, when you’re using the app, it’s important to
bear in mind that although we endeavor to ensure that it is
updated and correct at all times, we do rely on third parties
to provide information to us so that we can make it available
to you. Vladimir Jovanovic accepts no liability for any
loss, direct or indirect, you experience as a result of
relying wholly on this functionality of the app.
</p> <p>
At some point, we may wish to update the app. The app is
currently available on Android – the requirements for the
system(and for any additional systems we
decide to extend the availability of the app to) may change,
and you’ll need to download the updates if you want to keep
using the app. Vladimir Jovanovic does not promise that it
will always update the app so that it is relevant to you
and/or works with the Android version that you have
installed on your device. However, you promise to always
accept updates to the application when offered to you, We may
also wish to stop providing the app, and may terminate use of
it at any time without giving notice of termination to you.
Unless we tell you otherwise, upon any termination, (a) the
rights and licenses granted to you in these terms will end;
(b) you must stop using the app, and (if needed) delete it
from your device.
</p> <p><strong>Changes to This Terms and Conditions</strong></p> <p>
I may update our Terms and Conditions
from time to time. Thus, you are advised to review this page
periodically for any changes. I will
notify you of any changes by posting the new Terms and
Conditions on this page.
</p> <p>
These terms and conditions are effective as of 2022-06-18
</p> <p><strong>Contact Us</strong></p> <p>
If you have any questions or suggestions about my
Terms and Conditions, do not hesitate to contact me
at write@vladimirj.dev.
</p> <p>This Terms and Conditions page was generated by <a href="https://app-privacy-policy-generator.nisrulz.com/" target="_blank" rel="noopener noreferrer">App Privacy Policy Generator</a></p>
</body>
</html>
================================================
FILE: sample/.gitignore
================================================
/build
================================================
FILE: sample/build.gradle
================================================
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
android {
namespace "com.vlad1m1r.lemniscate.sample"
defaultConfig {
applicationId "com.vlad1m1r.lemniscate.sample"
minSdkVersion Versions.sample_min_sdk
targetSdkVersion Versions.target_sdk
compileSdk Versions.compile_sdk
versionCode Versions.sample_version_code
versionName Versions.sample_version_name
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_17.toString()
}
buildFeatures {
viewBinding true
}
}
dependencies {
implementation Deps.appcompat
implementation Deps.circleindicator
implementation Deps.kotlin_stdlib
implementation project(':lemniscate')
}
================================================
FILE: sample/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/vladimirjovanovic/Library/Android/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
================================================
FILE: sample/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.vlad1m1r.lemniscate.sample">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
android:name=".PresentationActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".MainActivity" />
</activity>
</application>
</manifest>
================================================
FILE: sample/src/main/java/com/vlad1m1r/lemniscate/sample/CurveData.kt
================================================
/*
* Copyright 2016 Vladimir Jovanovic
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.vlad1m1r.lemniscate.sample
import android.os.Parcel
import android.os.Parcelable
class CurveData(var precision: Int = 200,
var strokeWidth: Float = 10.0f,
var sizeMultiplier: Float = 1.0f,
var lineMinLength: Float = 0.4f,
var lineMaxLength: Float = 0.8f,
var color: Int = 0,
var duration: Int = 1000,
var hasHole: Boolean = false,
var radiusFixed: Float = 4.0f,
var radiusMoving: Float = 1.0f,
var distanceFromCenter: Float = 3.0f,
var numberOfCycles: Int = 1) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readInt(),
parcel.readFloat(),
parcel.readFloat(),
parcel.readFloat(),
parcel.readFloat(),
parcel.readInt(),
parcel.readInt(),
parcel.readByte() != 0.toByte(),
parcel.readFloat(),
parcel.readFloat(),
parcel.readFloat(),
parcel.readInt())
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeInt(precision)
parcel.writeFloat(strokeWidth)
parcel.writeFloat(sizeMultiplier)
parcel.writeFloat(lineMinLength)
parcel.writeFloat(lineMaxLength)
parcel.writeInt(color)
parcel.writeInt(duration)
parcel.writeByte(if (hasHole) 1 else 0)
parcel.writeFloat(radiusFixed)
parcel.writeFloat(radiusMoving)
parcel.writeFloat(distanceFromCenter)
parcel.writeInt(numberOfCycles)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<CurveData> {
override fun createFromParcel(parcel: Parcel): CurveData {
return CurveData(parcel)
}
override fun newArray(size: Int): Array<CurveData?> {
return arrayOfNulls(size)
}
}
}
================================================
FILE: sample/src/main/java/com/vlad1m1r/lemniscate/sample/FragmentCurve.kt
================================================
/*
* Copyright 2016 Vladimir Jovanovic
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.vlad1m1r.lemniscate.sample
import android.content.Context
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.TextView
import com.vlad1m1r.lemniscate.BernoullisBowProgressView
import com.vlad1m1r.lemniscate.BernoullisProgressView
import com.vlad1m1r.lemniscate.BernoullisSharpProgressView
import com.vlad1m1r.lemniscate.GeronosProgressView
import com.vlad1m1r.lemniscate.base.BaseCurveProgressView
import com.vlad1m1r.lemniscate.funny.CannabisProgressView
import com.vlad1m1r.lemniscate.funny.HeartProgressView
import com.vlad1m1r.lemniscate.other.XProgressView
import com.vlad1m1r.lemniscate.roulette.EpitrochoidProgressView
import com.vlad1m1r.lemniscate.roulette.HypotrochoidProgressView
import com.vlad1m1r.lemniscate.roulette.scribble.RoundScribbleProgressView
import com.vlad1m1r.lemniscate.roulette.scribble.ScribbleProgressView
class FragmentCurve : Fragment() {
private var listener: OnViewCreated? = null
private var baseCurveProgressView: BaseCurveProgressView? = null
private lateinit var curveName: TextView
private lateinit var layoutViewHolder: LinearLayout
private var position: Int = 0
interface OnViewCreated {
fun onViewShown(position: Int, baseCurveProgressView: BaseCurveProgressView?)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (savedInstanceState != null && savedInstanceState.containsKey(KEY_POSITION))
position = savedInstanceState.getInt(KEY_POSITION)
if (baseCurveProgressView == null) {
baseCurveProgressView = getViewForPosition(position).apply {
id = position
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT)
}
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val root = inflater.inflate(R.layout.fragment_curve, container, false) as ViewGroup
curveName = root.findViewById(R.id.textCurveName)
layoutViewHolder = root.findViewById(R.id.layoutViewHolder)
(baseCurveProgressView?.parent as ViewGroup?)?.removeView(baseCurveProgressView)
layoutViewHolder.addView(baseCurveProgressView)
curveName.text = baseCurveProgressView?.javaClass?.simpleName
return root
}
private fun getViewForPosition(position: Int): BaseCurveProgressView {
when (position) {
0 -> return BernoullisProgressView(context!!)
1 -> return GeronosProgressView(context!!)
2 -> return BernoullisBowProgressView(context!!)
3 -> return BernoullisSharpProgressView(context!!)
4 -> return EpitrochoidProgressView(context!!)
5 -> return HypotrochoidProgressView(context!!)
6 -> return XProgressView(context!!)
7 -> return RoundScribbleProgressView(context!!)
8 -> return ScribbleProgressView(context!!)
9 -> return CannabisProgressView(context!!)
10 -> return HeartProgressView(context!!)
else -> return BernoullisProgressView(context!!)
}
}
override fun onAttach(context: Context) {
super.onAttach(context)
listener = context as OnViewCreated
}
override fun onResume() {
super.onResume()
listener?.onViewShown(position, baseCurveProgressView)
}
override fun onDetach() {
listener = null
super.onDetach()
}
override fun onSaveInstanceState(outState: Bundle) {
outState.putInt(KEY_POSITION, position)
super.onSaveInstanceState(outState)
}
companion object {
private const val KEY_POSITION = "position"
fun getInstance(fragmentsPosition: Int) =
FragmentCurve().apply {
position = fragmentsPosition
retainInstance = true
}
}
}
================================================
FILE: sample/src/main/java/com/vlad1m1r/lemniscate/sample/FragmentSettings.kt
================================================
/*
* Copyright 2016 Vladimir Jovanovic
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.vlad1m1r.lemniscate.sample
import android.content.res.Resources
import android.os.Bundle
import android.view.LayoutInflater
import androidx.fragment.app.Fragment
import androidx.core.content.ContextCompat
import android.view.View
import android.view.ViewGroup
import android.widget.CompoundButton
import android.widget.SeekBar
import com.vlad1m1r.lemniscate.BernoullisBowProgressView
import com.vlad1m1r.lemniscate.BernoullisProgressView
import com.vlad1m1r.lemniscate.BernoullisSharpProgressView
import com.vlad1m1r.lemniscate.GeronosProgressView
import com.vlad1m1r.lemniscate.base.BaseCurveProgressView
import com.vlad1m1r.lemniscate.roulette.BaseRouletteProgressView
import com.vlad1m1r.lemniscate.sample.databinding.FragmentSettingsBinding
import kotlin.math.round
class FragmentSettings : Fragment(), SeekBar.OnSeekBarChangeListener, CompoundButton.OnCheckedChangeListener, View.OnClickListener {
private lateinit var curveData: CurveData
private var baseCurveProgressView: BaseCurveProgressView? = null
private var _binding: FragmentSettingsBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentSettingsBinding.inflate(layoutInflater)
return binding.root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
curveData = if(savedInstanceState != null && savedInstanceState.containsKey("curve_data")) {
savedInstanceState.getParcelable("curve_data")!!
} else {
CurveData(color = ContextCompat.getColor(requireContext(), R.color.picker_color_1))
}
setupViews()
}
private fun setupViews() {
binding.seekBarStrokeWidth.max = 50
binding.seekBarStrokeWidth.progress = curveData.strokeWidth.toInt()
binding.seekBarStrokeWidth.setOnSeekBarChangeListener(this)
binding.seekBarMaxLineLength.max = 99
binding.seekBarMaxLineLength.progress = round(100 * curveData.lineMaxLength).toInt() - 1
binding.seekBarMaxLineLength.setOnSeekBarChangeListener(this)
binding.seekBarSizeMultiplier.max = 15
binding.seekBarSizeMultiplier.progress = 5
binding.seekBarSizeMultiplier.setOnSeekBarChangeListener(this)
binding.seekBarMinLineLength.max = 99
binding. seekBarMinLineLength.progress = round(100 * curveData.lineMinLength).toInt() - 1
binding.seekBarMinLineLength.setOnSeekBarChangeListener(this)
binding.seekBarAnimationDuration.max = 199
binding.seekBarAnimationDuration.progress = curveData.duration / 10 - 1
binding.seekBarAnimationDuration.setOnSeekBarChangeListener(this)
binding.checkBoxHasHole.setOnCheckedChangeListener(this)
binding.checkBoxHasHole.isChecked = curveData.hasHole
binding.seekBarPrecision.max = 990
binding.seekBarPrecision.progress = curveData.precision
binding.seekBarPrecision.setOnSeekBarChangeListener(this)
binding.seekBarA.max = 10
binding.seekBarA.progress = (curveData.radiusFixed - 1).toInt()
binding.seekBarA.setOnSeekBarChangeListener(this)
binding.seekBarB.max = 10
binding.seekBarB.progress = (curveData.radiusMoving - 1).toInt()
binding.seekBarB.setOnSeekBarChangeListener(this)
binding.seekBarD.max = 10
binding.seekBarD.progress = (curveData.distanceFromCenter - 1).toInt()
binding.seekBarD.setOnSeekBarChangeListener(this)
binding.seekBarNumberOfCycles.max = 5
binding.seekBarNumberOfCycles.progress = curveData.numberOfCycles - 1
binding.seekBarNumberOfCycles.setOnSeekBarChangeListener(this)
binding.viewColor1.setOnClickListener(this)
binding.viewColor2.setOnClickListener(this)
binding.viewColor3.setOnClickListener(this)
binding.viewColor4.setOnClickListener(this)
binding.viewColor5.setOnClickListener(this)
binding.viewColor6.setOnClickListener(this)
}
fun setBaseCurveProgressView(baseCurveProgressView: BaseCurveProgressView) {
this.baseCurveProgressView = baseCurveProgressView
//Checkbox
binding.checkBoxHasHole.isEnabled = this.baseCurveProgressView is BernoullisProgressView ||
this.baseCurveProgressView is GeronosProgressView ||
this.baseCurveProgressView is BernoullisBowProgressView ||
this.baseCurveProgressView is BernoullisSharpProgressView
//Roulette params
if (this.baseCurveProgressView is BaseRouletteProgressView) {
binding.seekBarA.isEnabled = true
binding.seekBarB.isEnabled = true
binding.seekBarD.isEnabled = true
binding.seekBarNumberOfCycles!!.isEnabled = true
} else {
binding.seekBarA.isEnabled = false
binding.seekBarB.isEnabled = false
binding.seekBarD.isEnabled = false
binding.seekBarNumberOfCycles.isEnabled = false
}
invalidateView(this.baseCurveProgressView)
updateValues()
}
override fun onProgressChanged(seekBar: SeekBar, i: Int, fromUser: Boolean) {
when (seekBar.id) {
R.id.seekBarStrokeWidth -> curveData.strokeWidth = resources.dpToPx(i / 3.0f)
R.id.seekBarMaxLineLength -> if (i < binding.seekBarMinLineLength.progress) {
binding.seekBarMaxLineLength.progress = binding.seekBarMinLineLength.progress
} else
curveData.lineMaxLength = (i + 1) / 100.0f
R.id.seekBarMinLineLength -> if (i > binding.seekBarMaxLineLength.progress) {
binding.seekBarMinLineLength.progress = binding.seekBarMaxLineLength.progress
} else
curveData.lineMinLength = (i + 1) / 100.0f
R.id.seekBarSizeMultiplier -> curveData.sizeMultiplier = (i + 5) / 10.0f
R.id.seekBarAnimationDuration -> curveData.duration = (i + 1) * 10
R.id.seekBarPrecision -> curveData.precision = i + 10
R.id.seekBarA -> curveData.radiusFixed = (i + 1).toFloat()
R.id.seekBarB -> curveData.radiusMoving = (i + 1).toFloat()
R.id.seekBarD -> curveData.distanceFromCenter = (i + 1).toFloat()
R.id.seekBarNumberOfCycles -> curveData.numberOfCycles = i + 1
}
invalidateView(baseCurveProgressView)
updateValues()
}
private fun updateValues() {
binding.textStrokeWidth.text = curveData.strokeWidth.toString()
binding.textMaxLineLength.text = String.format(resources.getString(R.string.format_percentage), (curveData.lineMaxLength * 100).toInt())
binding.textMinLineLength.text = String.format(resources.getString(R.string.format_percentage), (curveData.lineMinLength * 100).toInt())
binding.textSizeMultiplier.text = curveData.sizeMultiplier.toString()
binding.textAnimationDuration.text = String.format(resources.getString(R.string.format_ms), curveData.duration)
binding.textPrecision.text = String.format(resources.getString(R.string.format_points), curveData.precision)
}
private fun invalidateView(baseCurveProgressView: BaseCurveProgressView?) {
baseCurveProgressView?.apply {
precision = curveData.precision
strokeWidth = curveData.strokeWidth
lineMaxLength = curveData.lineMaxLength
lineMinLength = curveData.lineMinLength
duration = curveData.duration
hasHole = curveData.hasHole
color = curveData.color
sizeMultiplier = curveData.sizeMultiplier
if (this is BaseRouletteProgressView) {
radiusFixed = curveData.radiusFixed
radiusMoving = curveData.radiusMoving
distanceFromCenter = curveData.distanceFromCenter
numberOfCycles = curveData.numberOfCycles.toFloat()
}
}
}
override fun onStartTrackingTouch(seekBar: SeekBar) {}
override fun onStopTrackingTouch(seekBar: SeekBar) {}
override fun onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) {
when (buttonView.id) {
R.id.checkBoxHasHole -> curveData.hasHole = isChecked
}
invalidateView(baseCurveProgressView)
}
fun applySettings(baseCurveProgressView: BaseCurveProgressView) {
invalidateView(baseCurveProgressView)
}
override fun onClick(v: View) {
when (v.id) {
R.id.viewColor1 -> curveData.color = ContextCompat.getColor(requireContext(), R.color.picker_color_1)
R.id.viewColor2 -> curveData.color = ContextCompat.getColor(requireContext(), R.color.picker_color_2)
R.id.viewColor3 -> curveData.color = ContextCompat.getColor(requireContext(), R.color.picker_color_3)
R.id.viewColor4 -> curveData.color = ContextCompat.getColor(requireContext(), R.color.picker_color_4)
R.id.viewColor5 -> curveData.color = ContextCompat.getColor(requireContext(), R.color.picker_color_5)
R.id.viewColor6 -> curveData.color = ContextCompat.getColor(requireContext(), R.color.picker_color_6)
}
invalidateView(baseCurveProgressView)
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putParcelable("curve_data", curveData)
}
}
fun Resources.dpToPx(dp: Float): Float {
return dp * this.displayMetrics.density
}
================================================
FILE: sample/src/main/java/com/vlad1m1r/lemniscate/sample/MainActivity.kt
================================================
/*
* Copyright 2016 Vladimir Jovanovic
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.vlad1m1r.lemniscate.sample
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentStatePagerAdapter
import androidx.viewpager.widget.ViewPager
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import android.view.Menu
import android.view.MenuItem
import android.view.View
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.vlad1m1r.lemniscate.base.BaseCurveProgressView
import me.relex.circleindicator.CircleIndicator
private const val NUM_PAGES = 11
class MainActivity : AppCompatActivity(), FragmentCurve.OnViewCreated {
private lateinit var fragmentSettings: FragmentSettings
private lateinit var pager: ViewPager
private lateinit var pagerAdapter: CurvesPagerAdapter
private lateinit var toolbar: Toolbar
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
toolbar = findViewById(R.id.toolbar)
setSupportActionBar(toolbar)
fragmentSettings = supportFragmentManager.findFragmentById(R.id.fragment_settings) as FragmentSettings
pager = findViewById(R.id.viewPager)
pagerAdapter = CurvesPagerAdapter(supportFragmentManager)
val indicator = findViewById<CircleIndicator>(R.id.indicator)
pager.adapter = pagerAdapter
indicator.setViewPager(pager)
val rootView = findViewById<View>(R.id.root_view)
ViewCompat.setOnApplyWindowInsetsListener(rootView) { view, insets ->
val systemInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars())
view.setPadding(0, systemInsets.top, 0, systemInsets.bottom)
insets
}
}
override fun onViewShown(position: Int, baseCurveProgressView: BaseCurveProgressView?) {
if (pager.currentItem == position) {
fragmentSettings.setBaseCurveProgressView(baseCurveProgressView!!)
}
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_main_activity, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.action_github -> {
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.url_github)))
startActivity(browserIntent)
return true
}
R.id.action_presentation -> {
startActivity(Intent(this, PresentationActivity::class.java))
return true
}
}
return super.onOptionsItemSelected(item)
}
private inner class CurvesPagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
override fun getItem(position: Int): Fragment {
return FragmentCurve.getInstance(position)
}
override fun getCount(): Int {
return NUM_PAGES
}
}
}
================================================
FILE: sample/src/main/java/com/vlad1m1r/lemniscate/sample/PresentationActivity.kt
================================================
/*
* Copyright 2016 Vladimir Jovanovic
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.vlad1m1r.lemniscate.sample
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
class PresentationActivity : AppCompatActivity() {
private lateinit var toolbar: Toolbar
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_presentation)
toolbar = findViewById(R.id.toolbar)
setSupportActionBar(toolbar)
supportActionBar?.apply {
setTitle(R.string.screen_presentation)
setDisplayHomeAsUpEnabled(true)
setDisplayShowHomeEnabled(true)
}
val rootView = findViewById<View>(R.id.root_view)
ViewCompat.setOnApplyWindowInsetsListener(rootView) { view, insets ->
val systemInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars())
view.setPadding(0, systemInsets.top, 0, systemInsets.bottom)
insets
}
}
}
================================================
FILE: sample/src/main/res/drawable/indicator.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid
android:color="@color/color_primary"/>
</shape>
================================================
FILE: sample/src/main/res/drawable/indicator_selected.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid
android:color="@color/color_primary_dark"/>
</shape>
================================================
FILE: sample/src/main/res/drawable/shadow.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<gradient
android:angle="90"
android:startColor="@color/shadow_start"
android:endColor="@color/shadow_end"
android:type="linear" />
</shape>
================================================
FILE: sample/src/main/res/drawable-v26/ic_launcher_background.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<vector
android:height="108dp"
android:width="108dp"
android:viewportHeight="108"
android:viewportWidth="108"
xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@color/color_primary"
android:pathData="M0,0h108v108h-108z"/>
</vector>
================================================
FILE: sample/src/main/res/drawable-v26/ic_launcher_foreground.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108.0"
android:viewportHeight="108.0">
<path
android:pathData="m85.76,70.11c0.16,-0.08 0.62,-0.33 0.48,-0.23 -0.26,0.18 -1.06,0.67 -0.82,0.47 0.33,-0.28 0.73,-0.46 1.09,-0.7 0.48,-0.32 0.95,-0.64 1.42,-0.97 2.12,-1.46 1.64,-1.13 3.87,-2.73 3.19,-2.37 6.3,-4.91 8.86,-7.97 1.8,-2.15 1.95,-2.62 3.38,-4.99 1.25,-2.45 2.19,-5.04 2.71,-7.75 0.16,-0.84 0.24,-1.69 0.36,-2.54 0,0 1.49,-0.73 1.49,-0.73v0c-0.12,0.85 -0.2,1.71 -0.36,2.56 -0.52,2.72 -1.47,5.34 -2.72,7.81 -1.42,2.38 -1.59,2.87 -3.38,5.03 -2.54,3.06 -5.63,5.6 -8.8,7.98 -2.36,1.71 -1.56,1.15 -3.78,2.7 -1.71,1.19 -3.43,2.38 -5.32,3.28 0,0 1.51,-1.2 1.51,-1.2z"
android:strokeLineCap="square"
android:fillAlpha="0"
android:strokeColor="#00000000"
android:fillColor="#000000"
android:strokeWidth="1.51181102"
android:strokeLineJoin="miter"
android:strokeAlpha="0.5"/>
<path
android:pathData="m101.76,107.99 l-27.78,-0.09 -45.61,-45.61 11.63,3.12 11.09,-8.09 50.68,50.68v0"
android:strokeLineCap="butt"
android:fillAlpha="0.14117648"
android:strokeColor="#00000000"
android:fillColor="#000000"
android:strokeWidth="1"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="m51.09,57.31 l-13.77,-13.77 -7.66,2.05 -3.13,11.68 6.97,6.97h9.72z"
android:strokeLineCap="butt"
android:fillAlpha="0.08058824"
android:strokeColor="#00000000"
android:fillColor="#000000"
android:strokeWidth="1"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="m56.79,50.81 l13.61,13.57 7.3,-1.93 3.23,-12.07 -8.08,-8.08 -10.91,2.92z"
android:strokeLineCap="butt"
android:fillAlpha="0.08058824"
android:strokeColor="#00000000"
android:fillColor="#000000"
android:strokeWidth="1"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="m70.4,64.38 l37.67,37.67v6.07l-6.31,-0.14 -64.44,-64.44 7.15,2.11 17.52,17.52z"
android:strokeLineCap="butt"
android:fillAlpha="0.08058824"
android:strokeColor="#00000000"
android:fillColor="#000000"
android:strokeWidth="1"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="m80.01,46.12 l27.98,27.98 0.09,27.95 -37.67,-37.67 8.52,-3.09 2.67,-9.96z"
android:strokeLineCap="butt"
android:fillAlpha="0.14117647"
android:strokeColor="#00000000"
android:fillColor="#000000"
android:strokeWidth="1"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="m70.11,41.31c-5.48,0 -9.58,3.84 -12.87,6.93l-0.36,0.36c-0.64,0.6 -0.67,1.6 -0.06,2.23 0.6,0.63 1.61,0.66 2.25,0.06l0.36,-0.36c3.18,-2.98 6.47,-6.07 10.68,-6.07 4.56,0 9.28,3.57 9.28,9.54 0,5.97 -4.72,9.54 -9.28,9.54C63.64,63.54 55.96,53.83 55.06,52.8 54.24,51.88 46.11,41.31 37.89,41.31 31.77,41.31 25.44,46.05 25.44,54c0,7.95 6.33,12.69 12.46,12.69 5.48,0 9.58,-3.84 12.87,-6.93l0.28,-0.26c0.64,-0.6 0.67,-1.6 0.06,-2.23 -0.6,-0.63 -1.61,-0.66 -2.25,-0.06l-0.28,0.27c-3.18,2.98 -6.47,6.07 -10.68,6.07 -4.56,0 -9.28,-3.57 -9.28,-9.54 0,-5.97 4.72,-9.54 9.28,-9.54 6.57,0 13.91,9.44 14.74,10.38C53.53,55.87 62,66.69 70.11,66.69 76.23,66.69 82.56,61.95 82.56,54c0,-7.95 -6.33,-12.69 -12.46,-12.69z"
android:fillColor="#ffffff"/>
</vector>
================================================
FILE: sample/src/main/res/layout/activity_main.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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/root_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
tools:context="com.vlad1m1r.lemniscate.sample.MainActivity">
<include layout="@layout/toolbar" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.viewpager.widget.ViewPager
android:id="@+id/viewPager"
android:layout_width="wrap_content"
android:layout_height="200dp" />
<me.relex.circleindicator.CircleIndicator
android:id="@+id/indicator"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_gravity="bottom"
app:ci_drawable="@drawable/indicator_selected"
app:ci_drawable_unselected="@drawable/indicator" />
</FrameLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/divider" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<fragment
android:id="@+id/fragment_settings"
android:name="com.vlad1m1r.lemniscate.sample.FragmentSettings"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<View
android:layout_width="match_parent"
android:layout_height="5dp"
android:layout_gravity="top"
android:background="@drawable/shadow" />
</FrameLayout>
</LinearLayout>
================================================
FILE: sample/src/main/res/layout/activity_presentation.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/root_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="horizontal">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center"
android:gravity="center"
android:orientation="vertical">
<com.vlad1m1r.lemniscate.BernoullisProgressView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/margin_normal"
app:duration="1250"
app:hasHole="true"
app:lineColor="@color/color_primary"
app:maxLineLength="0.8"
app:minLineLength="0.8"
app:precision="80"
app:strokeWidth="5dp" />
<com.vlad1m1r.lemniscate.roulette.EpitrochoidProgressView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/margin_normal"
app:distanceFromCenter="1"
app:duration="1250"
app:hasHole="true"
app:lineColor="@color/color_primary"
app:maxLineLength="0.8"
app:minLineLength="0.8"
app:numberOfCycles="1"
app:precision="150"
app:radiusFixed="3"
app:radiusMoving="1"
app:strokeWidth="5dp" />
<com.vlad1m1r.lemniscate.funny.HeartProgressView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/margin_normal"
app:duration="1250"
app:lineColor="@color/color_primary"
app:maxLineLength="0.8"
app:minLineLength="0.8"
app:precision="150"
app:strokeWidth="5dp" />
<com.vlad1m1r.lemniscate.roulette.HypotrochoidProgressView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/margin_normal"
app:distanceFromCenter="2"
app:duration="1250"
app:lineColor="@color/color_primary"
app:maxLineLength="0.8"
app:minLineLength="0.8"
app:numberOfCycles="2"
app:precision="80"
app:radiusFixed="5"
app:radiusMoving="2"
app:strokeWidth="5dp" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center"
android:gravity="center"
gitextract_ucgiast4/ ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── build.gradle ├── buildSrc/ │ ├── .gitignore │ ├── build.gradle.kts │ └── src/ │ └── main/ │ └── java/ │ └── Dependencies.kt ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── lemniscate/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── com/ │ │ │ └── vlad1m1r/ │ │ │ └── lemniscate/ │ │ │ ├── BernoullisBowProgressView.kt │ │ │ ├── BernoullisProgressView.kt │ │ │ ├── BernoullisSharpProgressView.kt │ │ │ ├── GeronosProgressView.kt │ │ │ ├── base/ │ │ │ │ ├── BaseCurveContract.kt │ │ │ │ ├── BaseCurvePresenter.kt │ │ │ │ ├── BaseCurveProgressView.kt │ │ │ │ ├── models/ │ │ │ │ │ ├── DrawState.kt │ │ │ │ │ ├── LineLength.kt │ │ │ │ │ ├── Point.kt │ │ │ │ │ ├── Points.kt │ │ │ │ │ └── ViewSize.kt │ │ │ │ └── settings/ │ │ │ │ ├── AnimationSettings.kt │ │ │ │ └── CurveSettings.kt │ │ │ ├── funny/ │ │ │ │ ├── CannabisProgressView.kt │ │ │ │ └── HeartProgressView.kt │ │ │ ├── other/ │ │ │ │ └── XProgressView.kt │ │ │ ├── roulette/ │ │ │ │ ├── BaseRouletteProgressView.kt │ │ │ │ ├── EpitrochoidProgressView.kt │ │ │ │ ├── HypotrochoidProgressView.kt │ │ │ │ ├── scribble/ │ │ │ │ │ ├── RoundScribbleProgressView.kt │ │ │ │ │ └── ScribbleProgressView.kt │ │ │ │ └── settings/ │ │ │ │ └── RouletteCurveSettings.kt │ │ │ └── utils/ │ │ │ └── CurveUtils.kt │ │ └── res/ │ │ └── values/ │ │ ├── attrs.xml │ │ └── dimens.xml │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── vlad1m1r/ │ │ └── lemniscate/ │ │ ├── BernoullisBowProgressViewTest.kt │ │ ├── BernoullisProgressViewTest.kt │ │ ├── BernoullisSharpProgressViewTest.kt │ │ ├── GeronosProgressViewTest.kt │ │ ├── base/ │ │ │ ├── BaseCurvePresenterTest.kt │ │ │ ├── BaseCurveProgressViewTest.kt │ │ │ ├── BaseProgressViewAttributesTest.kt │ │ │ ├── models/ │ │ │ │ ├── DrawStateTest.kt │ │ │ │ ├── LineLengthParcelableTest.kt │ │ │ │ ├── LineLengthTest.kt │ │ │ │ ├── PointTest.kt │ │ │ │ └── PointsTest.kt │ │ │ └── settings/ │ │ │ ├── AnimationSettingsParcelableTest.kt │ │ │ ├── CurveSettingsParcelableTest.kt │ │ │ └── CurveSettingsTest.kt │ │ ├── funny/ │ │ │ ├── CannabisProgressViewTest.kt │ │ │ └── HeartProgressViewTest.kt │ │ ├── other/ │ │ │ └── XProgressViewTest.kt │ │ ├── roulette/ │ │ │ ├── BaseRouletteProgressViewAttributesTest.kt │ │ │ ├── EpitrochoidProgressViewTest.kt │ │ │ ├── HypotrochoidProgressViewTest.kt │ │ │ ├── scribble/ │ │ │ │ ├── RoundScribbleProgressViewTest.kt │ │ │ │ └── ScribbleProgressViewTest.kt │ │ │ └── settings/ │ │ │ └── RouletteCurveSettingsParcelableTest.kt │ │ ├── testutils/ │ │ │ ├── CurveTestUtils.kt │ │ │ ├── EqualUtils.kt │ │ │ ├── TestConstants.kt │ │ │ └── TestLayoutInflater.kt │ │ └── utils/ │ │ └── CurveUtilsTest.kt │ └── resources/ │ └── mockito-extensions/ │ └── org.mockito.plugins.MockMaker ├── remote_data/ │ └── legal/ │ ├── privacy_policy.html │ └── terms_and_conditions.html ├── sample/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ ├── java/ │ │ └── com/ │ │ └── vlad1m1r/ │ │ └── lemniscate/ │ │ └── sample/ │ │ ├── CurveData.kt │ │ ├── FragmentCurve.kt │ │ ├── FragmentSettings.kt │ │ ├── MainActivity.kt │ │ └── PresentationActivity.kt │ └── res/ │ ├── drawable/ │ │ ├── indicator.xml │ │ ├── indicator_selected.xml │ │ └── shadow.xml │ ├── drawable-v26/ │ │ ├── ic_launcher_background.xml │ │ └── ic_launcher_foreground.xml │ ├── layout/ │ │ ├── activity_main.xml │ │ ├── activity_presentation.xml │ │ ├── fragment_curve.xml │ │ ├── fragment_settings.xml │ │ └── toolbar.xml │ ├── layout-land/ │ │ ├── activity_main.xml │ │ └── activity_presentation.xml │ ├── menu/ │ │ └── menu_main_activity.xml │ ├── mipmap-anydpi-v26/ │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ ├── values/ │ │ ├── colors.xml │ │ ├── constants.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── values-w820dp/ │ └── dimens.xml └── settings.gradle
Condensed preview — 106 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (233K chars).
[
{
"path": ".gitignore",
"chars": 472,
"preview": "# Built application files\n*.apk\n*.ap_\n\n# Files for the ART/Dalvik VM\n*.dex\n\n# Java class files\n*.class\n\n# Generated file"
},
{
"path": "CHANGELOG.md",
"chars": 2926,
"preview": "Change Log\r\n==========\r\n\r\nVersion 2.0.2 *(2019-07-27)*\r\n----------------------------\r\n\r\n* Fix `lineMinLength` setter\r\n\r\n"
},
{
"path": "LICENSE",
"chars": 10172,
"preview": " Apache License\n Version 2.0, January 2004\n "
},
{
"path": "README.md",
"chars": 4957,
"preview": "\r\n\r\n\r\n[\n google()\n }\n dependencies {\n classpath Deps.andr"
},
{
"path": "buildSrc/.gitignore",
"chars": 7,
"preview": "/build\n"
},
{
"path": "buildSrc/build.gradle.kts",
"chars": 670,
"preview": "/*\n * Copyright 2017 Vladimir Jovanovic\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may"
},
{
"path": "buildSrc/src/main/java/Dependencies.kt",
"chars": 1522,
"preview": "\nobject Versions {\n const val kotlin = \"2.0.20\"\n\n const val android_x = \"1.7.0\"\n\n const val circleindicator = \""
},
{
"path": "gradle/wrapper/gradle-wrapper.properties",
"chars": 231,
"preview": "#Sat Sep 07 13:09:29 CEST 2024\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\:/"
},
{
"path": "gradle.properties",
"chars": 754,
"preview": "# Project-wide Gradle settings.\n\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will o"
},
{
"path": "gradlew",
"chars": 5028,
"preview": "#!/usr/bin/env bash\n\n##############################################################################\n##\n## Gradle start "
},
{
"path": "gradlew.bat",
"chars": 2415,
"preview": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@r"
},
{
"path": "lemniscate/.gitignore",
"chars": 7,
"preview": "/build\n"
},
{
"path": "lemniscate/build.gradle",
"chars": 2522,
"preview": "apply plugin: 'com.android.library'\napply plugin: 'kotlin-android'\napply plugin: 'jacoco'\n\njacoco {\n toolVersion = Ve"
},
{
"path": "lemniscate/proguard-rules.pro",
"chars": 675,
"preview": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /U"
},
{
"path": "lemniscate/src/main/AndroidManifest.xml",
"chars": 46,
"preview": "<manifest package=\"com.vlad1m1r.lemniscate\"/>\n"
},
{
"path": "lemniscate/src/main/java/com/vlad1m1r/lemniscate/BernoullisBowProgressView.kt",
"chars": 1346,
"preview": "/*\n * Copyright 2016 Vladimir Jovanovic\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may"
},
{
"path": "lemniscate/src/main/java/com/vlad1m1r/lemniscate/BernoullisProgressView.kt",
"chars": 1336,
"preview": "/*\n * Copyright 2016 Vladimir Jovanovic\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may"
},
{
"path": "lemniscate/src/main/java/com/vlad1m1r/lemniscate/BernoullisSharpProgressView.kt",
"chars": 1335,
"preview": "/*\n * Copyright 2016 Vladimir Jovanovic\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may"
},
{
"path": "lemniscate/src/main/java/com/vlad1m1r/lemniscate/GeronosProgressView.kt",
"chars": 1269,
"preview": "/*\n * Copyright 2016 Vladimir Jovanovic\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may"
},
{
"path": "lemniscate/src/main/java/com/vlad1m1r/lemniscate/base/BaseCurveContract.kt",
"chars": 1720,
"preview": "package com.vlad1m1r.lemniscate.base\n\nimport com.vlad1m1r.lemniscate.base.models.DrawState\nimport com.vlad1m1r.lemniscat"
},
{
"path": "lemniscate/src/main/java/com/vlad1m1r/lemniscate/base/BaseCurvePresenter.kt",
"chars": 2457,
"preview": "package com.vlad1m1r.lemniscate.base\n\nimport com.vlad1m1r.lemniscate.base.models.DrawState\nimport com.vlad1m1r.lemniscat"
},
{
"path": "lemniscate/src/main/java/com/vlad1m1r/lemniscate/base/BaseCurveProgressView.kt",
"chars": 9476,
"preview": "/*\n * Copyright 2016 Vladimir Jovanovic\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may"
},
{
"path": "lemniscate/src/main/java/com/vlad1m1r/lemniscate/base/models/DrawState.kt",
"chars": 3684,
"preview": "/*\n * Copyright 2016 Vladimir Jovanovic\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may"
},
{
"path": "lemniscate/src/main/java/com/vlad1m1r/lemniscate/base/models/LineLength.kt",
"chars": 1846,
"preview": "/*\n * Copyright 2016 Vladimir Jovanovic\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may"
},
{
"path": "lemniscate/src/main/java/com/vlad1m1r/lemniscate/base/models/Point.kt",
"chars": 1310,
"preview": "/*\n * Copyright 2016 Vladimir Jovanovic\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may"
},
{
"path": "lemniscate/src/main/java/com/vlad1m1r/lemniscate/base/models/Points.kt",
"chars": 974,
"preview": "/*\n * Copyright 2016 Vladimir Jovanovic\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may"
},
{
"path": "lemniscate/src/main/java/com/vlad1m1r/lemniscate/base/models/ViewSize.kt",
"chars": 730,
"preview": "/*\n * Copyright 2016 Vladimir Jovanovic\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may"
},
{
"path": "lemniscate/src/main/java/com/vlad1m1r/lemniscate/base/settings/AnimationSettings.kt",
"chars": 1445,
"preview": "/*\n * Copyright 2016 Vladimir Jovanovic\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may"
},
{
"path": "lemniscate/src/main/java/com/vlad1m1r/lemniscate/base/settings/CurveSettings.kt",
"chars": 2523,
"preview": "/*\n * Copyright 2016 Vladimir Jovanovic\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may"
},
{
"path": "lemniscate/src/main/java/com/vlad1m1r/lemniscate/funny/CannabisProgressView.kt",
"chars": 1915,
"preview": "/*\n * Copyright 2016 Vladimir Jovanovic\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may"
},
{
"path": "lemniscate/src/main/java/com/vlad1m1r/lemniscate/funny/HeartProgressView.kt",
"chars": 1624,
"preview": "/*\n * Copyright 2016 Vladimir Jovanovic\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may"
},
{
"path": "lemniscate/src/main/java/com/vlad1m1r/lemniscate/other/XProgressView.kt",
"chars": 1504,
"preview": "/*\n * Copyright 2016 Vladimir Jovanovic\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may"
},
{
"path": "lemniscate/src/main/java/com/vlad1m1r/lemniscate/roulette/BaseRouletteProgressView.kt",
"chars": 5109,
"preview": "/*\n * Copyright 2016 Vladimir Jovanovic\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may"
},
{
"path": "lemniscate/src/main/java/com/vlad1m1r/lemniscate/roulette/EpitrochoidProgressView.kt",
"chars": 1733,
"preview": "/*\n * Copyright 2016 Vladimir Jovanovic\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may"
},
{
"path": "lemniscate/src/main/java/com/vlad1m1r/lemniscate/roulette/HypotrochoidProgressView.kt",
"chars": 1686,
"preview": "/*\n * Copyright 2016 Vladimir Jovanovic\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may"
},
{
"path": "lemniscate/src/main/java/com/vlad1m1r/lemniscate/roulette/scribble/RoundScribbleProgressView.kt",
"chars": 1660,
"preview": "/*\n * Copyright 2016 Vladimir Jovanovic\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may"
},
{
"path": "lemniscate/src/main/java/com/vlad1m1r/lemniscate/roulette/scribble/ScribbleProgressView.kt",
"chars": 1666,
"preview": "/*\n * Copyright 2016 Vladimir Jovanovic\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may"
},
{
"path": "lemniscate/src/main/java/com/vlad1m1r/lemniscate/roulette/settings/RouletteCurveSettings.kt",
"chars": 1415,
"preview": "package com.vlad1m1r.lemniscate.roulette.settings\n\nimport android.os.Parcel\nimport android.os.Parcelable\n\nclass Roulette"
},
{
"path": "lemniscate/src/main/java/com/vlad1m1r/lemniscate/utils/CurveUtils.kt",
"chars": 1019,
"preview": "/*\n * Copyright 2016 Vladimir Jovanovic\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may"
},
{
"path": "lemniscate/src/main/res/values/attrs.xml",
"chars": 841,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <declare-styleable name=\"BaseCurveProgressView\">\n <attr na"
},
{
"path": "lemniscate/src/main/res/values/dimens.xml",
"chars": 197,
"preview": "<resources>\n <dimen name=\"lemniscate_stroke_width\">10dp</dimen>\n <dimen name=\"lemniscate_preferred_width\">80dp</di"
},
{
"path": "lemniscate/src/test/java/com/vlad1m1r/lemniscate/BernoullisBowProgressViewTest.kt",
"chars": 1886,
"preview": "package com.vlad1m1r.lemniscate\n\nimport com.google.common.truth.Truth.assertThat\nimport com.vlad1m1r.lemniscate.testutil"
},
{
"path": "lemniscate/src/test/java/com/vlad1m1r/lemniscate/BernoullisProgressViewTest.kt",
"chars": 1877,
"preview": "package com.vlad1m1r.lemniscate\n\nimport com.google.common.truth.Truth.assertThat\nimport org.mockito.kotlin.mock\nimport c"
},
{
"path": "lemniscate/src/test/java/com/vlad1m1r/lemniscate/BernoullisSharpProgressViewTest.kt",
"chars": 1888,
"preview": "package com.vlad1m1r.lemniscate\n\nimport com.google.common.truth.Truth.assertThat\nimport com.vlad1m1r.lemniscate.testutil"
},
{
"path": "lemniscate/src/test/java/com/vlad1m1r/lemniscate/GeronosProgressViewTest.kt",
"chars": 1868,
"preview": "package com.vlad1m1r.lemniscate\n\nimport com.google.common.truth.Truth.assertThat\nimport org.mockito.kotlin.mock\nimport c"
},
{
"path": "lemniscate/src/test/java/com/vlad1m1r/lemniscate/base/BaseCurvePresenterTest.kt",
"chars": 4422,
"preview": "package com.vlad1m1r.lemniscate.base\n\nimport com.google.common.truth.Truth.assertThat\nimport org.mockito.kotlin.*\nimport"
},
{
"path": "lemniscate/src/test/java/com/vlad1m1r/lemniscate/base/BaseCurveProgressViewTest.kt",
"chars": 2521,
"preview": "package com.vlad1m1r.lemniscate.base\n\nimport android.view.View\nimport com.google.common.truth.Truth.assertThat\nimport or"
},
{
"path": "lemniscate/src/test/java/com/vlad1m1r/lemniscate/base/BaseProgressViewAttributesTest.kt",
"chars": 1743,
"preview": "package com.vlad1m1r.lemniscate.base\n\nimport android.graphics.Color\nimport android.os.Build\nimport androidx.test.platfor"
},
{
"path": "lemniscate/src/test/java/com/vlad1m1r/lemniscate/base/models/DrawStateTest.kt",
"chars": 6103,
"preview": "package com.vlad1m1r.lemniscate.base.models\n\nimport android.graphics.Path\nimport com.google.common.truth.Truth.assertTha"
},
{
"path": "lemniscate/src/test/java/com/vlad1m1r/lemniscate/base/models/LineLengthParcelableTest.kt",
"chars": 953,
"preview": "package com.vlad1m1r.lemniscate.base.models\n\nimport android.os.Build\nimport android.os.Parcel\nimport com.google.common.t"
},
{
"path": "lemniscate/src/test/java/com/vlad1m1r/lemniscate/base/models/LineLengthTest.kt",
"chars": 1407,
"preview": "package com.vlad1m1r.lemniscate.base.models\n\nimport com.google.common.truth.Truth.assertThat\nimport org.junit.Test\n\nclas"
},
{
"path": "lemniscate/src/test/java/com/vlad1m1r/lemniscate/base/models/PointTest.kt",
"chars": 344,
"preview": "package com.vlad1m1r.lemniscate.base.models\n\nimport com.google.common.truth.Truth.assertThat\nimport org.junit.Test\n\nclas"
},
{
"path": "lemniscate/src/test/java/com/vlad1m1r/lemniscate/base/models/PointsTest.kt",
"chars": 688,
"preview": "package com.vlad1m1r.lemniscate.base.models\n\nimport com.google.common.truth.Truth.assertThat\nimport org.junit.Test\n\nclas"
},
{
"path": "lemniscate/src/test/java/com/vlad1m1r/lemniscate/base/settings/AnimationSettingsParcelableTest.kt",
"chars": 933,
"preview": "package com.vlad1m1r.lemniscate.base.settings\n\nimport android.os.Build\nimport android.os.Parcel\nimport com.google.common"
},
{
"path": "lemniscate/src/test/java/com/vlad1m1r/lemniscate/base/settings/CurveSettingsParcelableTest.kt",
"chars": 1143,
"preview": "package com.vlad1m1r.lemniscate.base.settings\n\nimport android.os.Build\nimport android.os.Parcel\nimport com.vlad1m1r.lemn"
},
{
"path": "lemniscate/src/test/java/com/vlad1m1r/lemniscate/base/settings/CurveSettingsTest.kt",
"chars": 1210,
"preview": "package com.vlad1m1r.lemniscate.base.settings\n\nimport android.graphics.Paint\nimport com.google.common.truth.Truth.assert"
},
{
"path": "lemniscate/src/test/java/com/vlad1m1r/lemniscate/funny/CannabisProgressViewTest.kt",
"chars": 2464,
"preview": "package com.vlad1m1r.lemniscate.funny\n\nimport android.os.Build\nimport androidx.test.platform.app.InstrumentationRegistry"
},
{
"path": "lemniscate/src/test/java/com/vlad1m1r/lemniscate/funny/HeartProgressViewTest.kt",
"chars": 2449,
"preview": "package com.vlad1m1r.lemniscate.funny\n\nimport android.os.Build\nimport androidx.test.platform.app.InstrumentationRegistry"
},
{
"path": "lemniscate/src/test/java/com/vlad1m1r/lemniscate/other/XProgressViewTest.kt",
"chars": 2417,
"preview": "package com.vlad1m1r.lemniscate.other\n\nimport android.os.Build\nimport androidx.test.platform.app.InstrumentationRegistry"
},
{
"path": "lemniscate/src/test/java/com/vlad1m1r/lemniscate/roulette/BaseRouletteProgressViewAttributesTest.kt",
"chars": 1317,
"preview": "package com.vlad1m1r.lemniscate.roulette\n\nimport android.os.Build\nimport androidx.test.platform.app.InstrumentationRegis"
},
{
"path": "lemniscate/src/test/java/com/vlad1m1r/lemniscate/roulette/EpitrochoidProgressViewTest.kt",
"chars": 2385,
"preview": "package com.vlad1m1r.lemniscate.roulette\n\nimport android.os.Build\nimport androidx.test.platform.app.InstrumentationRegis"
},
{
"path": "lemniscate/src/test/java/com/vlad1m1r/lemniscate/roulette/HypotrochoidProgressViewTest.kt",
"chars": 2390,
"preview": "package com.vlad1m1r.lemniscate.roulette\n\nimport android.os.Build\nimport androidx.test.platform.app.InstrumentationRegis"
},
{
"path": "lemniscate/src/test/java/com/vlad1m1r/lemniscate/roulette/scribble/RoundScribbleProgressViewTest.kt",
"chars": 2397,
"preview": "package com.vlad1m1r.lemniscate.roulette.scribble\n\nimport android.os.Build\nimport androidx.test.platform.app.Instrumenta"
},
{
"path": "lemniscate/src/test/java/com/vlad1m1r/lemniscate/roulette/scribble/ScribbleProgressViewTest.kt",
"chars": 2458,
"preview": "package com.vlad1m1r.lemniscate.roulette.scribble\n\nimport android.os.Build\nimport androidx.test.platform.app.Instrumenta"
},
{
"path": "lemniscate/src/test/java/com/vlad1m1r/lemniscate/roulette/settings/RouletteCurveSettingsParcelableTest.kt",
"chars": 1172,
"preview": "package com.vlad1m1r.lemniscate.roulette.settings\n\nimport android.os.Build\nimport android.os.Parcel\nimport com.google.co"
},
{
"path": "lemniscate/src/test/java/com/vlad1m1r/lemniscate/testutils/CurveTestUtils.kt",
"chars": 1124,
"preview": "package com.vlad1m1r.lemniscate.testutils\n\nimport com.google.common.truth.Truth\nimport org.mockito.kotlin.*\nimport com.v"
},
{
"path": "lemniscate/src/test/java/com/vlad1m1r/lemniscate/testutils/EqualUtils.kt",
"chars": 1440,
"preview": "package com.vlad1m1r.lemniscate.testutils\n\nimport com.vlad1m1r.lemniscate.base.models.LineLength\nimport com.vlad1m1r.lem"
},
{
"path": "lemniscate/src/test/java/com/vlad1m1r/lemniscate/testutils/TestConstants.kt",
"chars": 104,
"preview": "package com.vlad1m1r.lemniscate.testutils\n\nobject TestConstants {\n const val DELTA: Float = 0.001f\n}\n"
},
{
"path": "lemniscate/src/test/java/com/vlad1m1r/lemniscate/testutils/TestLayoutInflater.kt",
"chars": 792,
"preview": "package com.vlad1m1r.lemniscate.testutils\n\nimport android.content.Context\nimport android.view.LayoutInflater\nimport andr"
},
{
"path": "lemniscate/src/test/java/com/vlad1m1r/lemniscate/utils/CurveUtilsTest.kt",
"chars": 642,
"preview": "package com.vlad1m1r.lemniscate.utils\n\nimport com.google.common.truth.Truth.assertThat\nimport com.vlad1m1r.lemniscate.ba"
},
{
"path": "lemniscate/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker",
"chars": 17,
"preview": "mock-maker-inline"
},
{
"path": "remote_data/legal/privacy_policy.html",
"chars": 6014,
"preview": "<!DOCTYPE html>\n<html>\n<head>\n <meta charset='utf-8'>\n <meta name='viewport' content='width=device-width'>\n <ti"
},
{
"path": "remote_data/legal/terms_and_conditions.html",
"chars": 5770,
"preview": "<!DOCTYPE html>\n<html>\n<head>\n <meta charset='utf-8'>\n <meta name='viewport' content='width=device-width'>\n <ti"
},
{
"path": "sample/.gitignore",
"chars": 7,
"preview": "/build\n"
},
{
"path": "sample/build.gradle",
"chars": 1053,
"preview": "apply plugin: 'com.android.application'\napply plugin: 'kotlin-android'\n\nandroid {\n namespace \"com.vlad1m1r.lemniscate"
},
{
"path": "sample/proguard-rules.pro",
"chars": 675,
"preview": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /U"
},
{
"path": "sample/src/main/AndroidManifest.xml",
"chars": 997,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n pa"
},
{
"path": "sample/src/main/java/com/vlad1m1r/lemniscate/sample/CurveData.kt",
"chars": 2607,
"preview": "/*\n * Copyright 2016 Vladimir Jovanovic\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may"
},
{
"path": "sample/src/main/java/com/vlad1m1r/lemniscate/sample/FragmentCurve.kt",
"chars": 4822,
"preview": "/*\n * Copyright 2016 Vladimir Jovanovic\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may"
},
{
"path": "sample/src/main/java/com/vlad1m1r/lemniscate/sample/FragmentSettings.kt",
"chars": 10362,
"preview": "/*\n * Copyright 2016 Vladimir Jovanovic\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may"
},
{
"path": "sample/src/main/java/com/vlad1m1r/lemniscate/sample/MainActivity.kt",
"chars": 3784,
"preview": "/*\n * Copyright 2016 Vladimir Jovanovic\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may"
},
{
"path": "sample/src/main/java/com/vlad1m1r/lemniscate/sample/PresentationActivity.kt",
"chars": 1714,
"preview": "/*\n * Copyright 2016 Vladimir Jovanovic\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may"
},
{
"path": "sample/src/main/res/drawable/indicator.xml",
"chars": 198,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:sha"
},
{
"path": "sample/src/main/res/drawable/indicator_selected.xml",
"chars": 203,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:sha"
},
{
"path": "sample/src/main/res/drawable/shadow.xml",
"chars": 318,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:"
},
{
"path": "sample/src/main/res/drawable-v26/ic_launcher_background.xml",
"chars": 340,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector\n android:height=\"108dp\"\n android:width=\"108dp\"\n android:viewport"
},
{
"path": "sample/src/main/res/drawable-v26/ic_launcher_foreground.xml",
"chars": 3684,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"108dp\"\n android:height="
},
{
"path": "sample/src/main/res/layout/activity_main.xml",
"chars": 1848,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmln"
},
{
"path": "sample/src/main/res/layout/activity_presentation.xml",
"chars": 5843,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout\n xmlns:android=\"http://schemas.android.com/apk/res/android\"\n x"
},
{
"path": "sample/src/main/res/layout/fragment_curve.xml",
"chars": 998,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout\n xmlns:android=\"http://schemas.android.com/apk/res/android\"\n a"
},
{
"path": "sample/src/main/res/layout/fragment_settings.xml",
"chars": 13707,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmlns:"
},
{
"path": "sample/src/main/res/layout/toolbar.xml",
"chars": 516,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.appcompat.widget.Toolbar\n xmlns:android=\"http://schemas.android.com/"
},
{
"path": "sample/src/main/res/layout-land/activity_main.xml",
"chars": 1836,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmln"
},
{
"path": "sample/src/main/res/layout-land/activity_presentation.xml",
"chars": 5845,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout\n xmlns:android=\"http://schemas.android.com/apk/res/android\"\n x"
},
{
"path": "sample/src/main/res/menu/menu_main_activity.xml",
"chars": 719,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu\n xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmlns:ap"
},
{
"path": "sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
"chars": 270,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <b"
},
{
"path": "sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
"chars": 270,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <b"
},
{
"path": "sample/src/main/res/values/colors.xml",
"chars": 796,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <color name=\"color_primary\">#3F51B5</color>\n <color name=\"colo"
},
{
"path": "sample/src/main/res/values/constants.xml",
"chars": 144,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <string name=\"url_github\">https://github.com/vlad1m1r990/Lemnisca"
},
{
"path": "sample/src/main/res/values/dimens.xml",
"chars": 520,
"preview": "<resources>\n <!-- Default screen margins, per the Android Design guidelines. -->\n <dimen name=\"activity_horizontal"
},
{
"path": "sample/src/main/res/values/strings.xml",
"chars": 1238,
"preview": "<resources>\n <string name=\"app_name\">Lemniscate Library</string>\n\n <string name=\"stroke_width\">Stroke width</strin"
},
{
"path": "sample/src/main/res/values/styles.xml",
"chars": 385,
"preview": "<resources>\n\n <!-- Base application theme. -->\n <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.NoActionBar\">"
},
{
"path": "sample/src/main/res/values-w820dp/dimens.xml",
"chars": 358,
"preview": "<resources>\n <!-- Example customization of dimensions originally defined in res/values/dimens.xml\n (such as s"
},
{
"path": "settings.gradle",
"chars": 33,
"preview": "include ':sample', ':lemniscate'\n"
}
]
// ... and 1 more files (download for full content)
About this extraction
This page contains the full source code of the vlad1m1r990/Lemniscate GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 106 files (209.9 KB), approximately 57.3k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.