diff --git a/.github/DISCUSSION_TEMPLATE/questions.yml b/.github/DISCUSSION_TEMPLATE/questions.yml deleted file mode 100644 index e9b796947..000000000 --- a/.github/DISCUSSION_TEMPLATE/questions.yml +++ /dev/null @@ -1,16 +0,0 @@ -body: - - type: textarea - attributes: - label: Question - validations: - required: true - - type: input - attributes: - label: Vico version(s) - - type: dropdown - attributes: - label: UI framework(s) - multiple: true - options: - - Jetpack Compose - - Views diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml deleted file mode 100644 index 71e62a188..000000000 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: Bug -description: Report incorrect behavior. -labels: ["bug"] -body: - - type: textarea - attributes: - label: How to reproduce - description: Please provide a detailed description of how to reproduce the bug. - validations: - required: true - - type: textarea - attributes: - label: Observed behavior - validations: - required: true - - type: textarea - attributes: - label: Expected behavior - validations: - required: true - - type: dropdown - attributes: - label: Vico version(s) - description: If you’re using an outdated version, please try updating Vico first. - multiple: true - options: - - 2.0.0-alpha.20 - - 1.15.0-alpha.1 - - 1.14.0 - validations: - required: true - - type: input - attributes: - label: Android version(s) - validations: - required: true - - type: textarea - attributes: - label: Additional information diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index 6f33698aa..000000000 --- a/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1,5 +0,0 @@ -blank_issues_enabled: false -contact_links: - - name: Something else - about: Please create a discussion. - url: https://github.com/patrykandpatrick/vico/discussions/new/choose diff --git a/.github/ISSUE_TEMPLATE/documentation-error.yml b/.github/ISSUE_TEMPLATE/documentation-error.yml deleted file mode 100644 index 37d922040..000000000 --- a/.github/ISSUE_TEMPLATE/documentation-error.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: Documentation error -description: Report a mistake in the documentation. -labels: ["documentation error"] -body: - - type: textarea - attributes: - label: Area - description: Which part of the documentation is affected? - validations: - required: true - - type: textarea - attributes: - label: Error - description: What’s wrong? - validations: - required: true - - type: textarea - attributes: - label: Additional information diff --git a/.github/workflows/build-debug-apk.yml b/.github/workflows/build-debug-apk.yml index 5e0bd55e0..d9c505bf6 100644 --- a/.github/workflows/build-debug-apk.yml +++ b/.github/workflows/build-debug-apk.yml @@ -1,6 +1,5 @@ name: Build debug APK on: - push: pull_request: jobs: build-debug-apk: @@ -17,3 +16,7 @@ jobs: with: name: Debug APK path: sample/build/outputs/apk/debug/**.apk + - uses: actions/upload-artifact@v4 + with: + name: Debug Tangem Demo APK + path: tangem-demo/build/outputs/apk/debug/**.apk diff --git a/.github/workflows/check-formatting.yml b/.github/workflows/check-formatting.yml deleted file mode 100644 index be0534620..000000000 --- a/.github/workflows/check-formatting.yml +++ /dev/null @@ -1,16 +0,0 @@ -name: Check formatting -on: - push: - pull_request: -jobs: - check-formatting: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-java@v4 - with: - java-version: 21 - distribution: zulu - - run: | - curl -sSL -o ktfmt.jar https://github.com/facebook/ktfmt/releases/download/v0.49/ktfmt-0.49-jar-with-dependencies.jar - java -jar ktfmt.jar --dry-run --google-style --set-exit-if-changed . diff --git a/.github/workflows/neutral-build.yml b/.github/workflows/neutral-build.yml new file mode 100644 index 000000000..9bf7c7a5a --- /dev/null +++ b/.github/workflows/neutral-build.yml @@ -0,0 +1,66 @@ +name: Neutral Build + +on: + push: + branches: + - 'tangem-master' + workflow_dispatch: + +env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_DVELOPMENT_ANDROID }} + +jobs: + tag: + name: Create tag + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + path: vico + + - name: set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: 17 + distribution: zulu + + - uses: gradle/gradle-build-action@v3 + - run: | + VERSION_NAME=$(grep -oP "(?<=version_name).*(?=\",)" versions.gradle | grep -oP "(?<=\").*") + echo "VERSION_NAME=$VERSION_NAME" >> $GITHUB_ENV + echo "TAG_NAME=v$VERSION_NAME" >> $GITHUB_ENV + ./gradlew assembleDebug + + - name: Create tag + uses: actions/github-script@v3 + with: + github-token: ${{ github.token }} + script: | + github.git.createRef({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: "refs/tags/${{ env.VERSION_NAME }}", + sha: context.sha + }) + + - name: Build and publish + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_ACTOR: ${{ secrets.GITHUB_ACTOR }} + run: | + echo sdk.dir = $ANDROID_HOME > local.properties + ./gradlew -PgithubUser=${{ secrets.GITHUB_ACTOR }} -PgithubPass=${{ secrets.GITHUB_TOKEN }} -PartifactVersion=${{ env.VERSION_NAME }} publish + + - name: Build notification + if: always() + uses: adamkdean/simple-slack-notify@master + with: + channel: '#development-android' + status: ${{ job.status }} + success_text: 'Tangem Vico library build (${{ env.VERSION_NAME }}) has been created and uploaded to Nexus.' + failure_text: 'GitHub Action #${{ github.run_number }} failed. Tag has not been not created.' + cancelled_text: 'GitHub Action #${{ github.run_number }} was cancelled' + fields: | + [{"title": "TAG", "value": "${{ env.VERSION_NAME }}"}, + {"title": "Action URL", "value": "${env.GITHUB_SERVER_URL}/${env.GITHUB_REPOSITORY}/actions/runs/${env.GITHUB_RUN_ID}"}] diff --git a/.github/workflows/release-update.yml b/.github/workflows/release-update.yml deleted file mode 100644 index 350e3dd2a..000000000 --- a/.github/workflows/release-update.yml +++ /dev/null @@ -1,56 +0,0 @@ -name: Release update -on: - workflow_dispatch: -jobs: - release-update: - runs-on: ubuntu-latest - if: github.repository == 'patrykandpatrick/vico' - permissions: - contents: write - steps: - - uses: actions/checkout@v4 - with: - path: vico - - uses: actions/setup-java@v4 - with: - java-version: 17 - distribution: zulu - - uses: gradle/gradle-build-action@v3 - - run: | - git config --global user.email "96002241+patrykandpatrickbot@users.noreply.github.com" - git config --global user.name "Patryk & Patrick Bot" - git clone https://${{ secrets.BOT_PAT }}@github.com/patrykandpatrick/patrykandpatrick.com - cd ${{ github.workspace }}/vico - VERSION_NAME=$(grep -oP "(?<=version_name).*(?=\",)" versions.gradle | grep -oP "(?<=\").*") - echo "VERSION_NAME=$VERSION_NAME" >> $GITHUB_ENV - echo "TAG_NAME=v$VERSION_NAME" >> $GITHUB_ENV - IS_PRERELEASE=false - echo $VERSION_NAME | grep -q "alpha\|beta" && IS_PRERELEASE=true - echo "IS_PRERELEASE=$IS_PRERELEASE" >> $GITHUB_ENV - ./gradlew assembleDebug - - uses: softprops/action-gh-release@v2 - with: - tag_name: ${{ env.TAG_NAME }} - token: ${{ secrets.BOT_PAT }} - prerelease: ${{ env.IS_PRERELEASE }} - draft: true - files: ${{ github.workspace }}/vico/sample/build/outputs/apk/debug/*.apk - - run: | - cd ${{ github.workspace }}/vico - git remote set-url origin https://patrykandpatrickbot:${{ secrets.BOT_PAT }}@github.com/patrykandpatrick/vico.git - ./gradlew dokkaHtmlMultiModule - API_REFERENCE_DESTINATION=${{ github.workspace }}/patrykandpatrick.com/src/vico/static/api/$(if $IS_PRERELEASE; then echo $VERSION_NAME; else echo main; fi) - rm -fr $API_REFERENCE_DESTINATION/* - cp -R ${{ github.workspace }}/vico/vico/build/dokka/htmlMultiModule/. $API_REFERENCE_DESTINATION - cd ${{ github.workspace }}/patrykandpatrick.com - git remote set-url origin https://patrykandpatrickbot:${{ secrets.BOT_PAT }}@github.com/patrykandpatrick/patrykandpatrick.com.git - git add src/vico/static/api - git diff --staged --quiet HEAD || git commit -m "Update Vico API reference" - git push origin - cd ${{ github.workspace }}/vico - git push origin - ./gradlew publish - env: - ORG_GRADLE_PROJECT_GPG_KEY: ${{ secrets.GPG_KEY }} - ORG_GRADLE_PROJECT_GPG_KEY_PASSWORD: ${{ secrets.GPG_KEY_PASSWORD }} - ORG_GRADLE_PROJECT_OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 6e1e358aa..4a6e5f0a0 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -1,6 +1,5 @@ name: Run tests on: - push: pull_request: jobs: run-tests: @@ -16,11 +15,3 @@ jobs: - run: chmod +x gradlew - id: unit-tests run: ./gradlew vico:core:testDebug - - id: paparazzi - if: ${{ success() || steps.unit-tests.conclusion == 'failure' }} - run: ./gradlew sample:verifyPaparazziDebug - - uses: actions/upload-artifact@v4 - if: ${{ !cancelled() && steps.paparazzi.conclusion == 'failure' }} - with: - name: Paparazzi deltas - path: sample/build/paparazzi/failures/delta-*.png diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 1cb76b6ac..d208d08e9 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -1,6 +1,6 @@ - + diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 94a25f7f4..dcb6b8c4c 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,6 +1,6 @@ - + - \ No newline at end of file + diff --git a/common-scripts.gradle b/common-scripts.gradle index 08fdb90a6..436fe022a 100644 --- a/common-scripts.gradle +++ b/common-scripts.gradle @@ -64,9 +64,9 @@ String getArtifactId(Project project) { void customizePom(MavenPom pom) { - pom.name = "Vico" + pom.name = "Tangem Vico" pom.description = "A light and extensible chart library for Android." - pom.url = "https://github.com/patrykandpatrick/vico" + pom.url = "https://github.com/tangem/vico" pom.licenses { @@ -77,25 +77,10 @@ void customizePom(MavenPom pom) { } pom.scm { - connection = "scm:git:git://github.com/patrykandpatrick/vico.git" - developerConnection = "scm:git:ssh://github.com/patrykandpatrick/vico.git" + connection = "scm:git:git://github.com/tangem/vico.git" + developerConnection = "scm:git:ssh://github.com/tangem/vico.git" url = "https://github.com/patrykandpatrick/vico" } - - pom.developers { - - developer { - id = "patrykgoworowski" - name = "Patryk Goworowski" - email = "contact@patrykgoworowski.pl" - } - - developer { - id = "patrickmichalik" - name = "Patrick Michalik" - email = "contact@patrickmichalik.com" - } - } } boolean isReleaseVersion() { @@ -104,25 +89,15 @@ boolean isReleaseVersion() { void setUpRepositories(RepositoryHandler handler) { handler.maven { - def releaseUrl = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/" - def snapshotUrl = "https://s01.oss.sonatype.org/content/repositories/snapshots/" - url = isReleaseVersion() ? releaseUrl : snapshotUrl - + name = "GitHubPackages" + url = uri("https://maven.pkg.github.com/tangem/vico") credentials { - username = "patrykandpatrick" - password = findProperty("OSSRH_PASSWORD") + username = "$githubUser" + password = "$githubPass" } } } void setUpSigning(SigningExtension signing, MavenPublication publication) { - signing.setRequired { - isReleaseVersion() && gradle.taskGraph.allTasks.any { it.is(PublishToMavenRepository) } - } - - def gpgKey = findProperty("GPG_KEY") - def gpgKeyPassword = findProperty("GPG_KEY_PASSWORD") - signing.useInMemoryPgpKeys(gpgKey, gpgKeyPassword) - signing.sign publication } diff --git a/gradle.properties b/gradle.properties index 153f79ef9..5de7e47f8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -26,3 +26,6 @@ android.useAndroidX=true kotlin.code.style=official # https://blog.jetbrains.com/kotlin/2022/07/a-new-approach-to-incremental-compilation-in-kotlin/ kotlin.incremental.useClasspathSnapshot=true + +githubUser=github_user +githubPass=github_pass diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 746e7d981..b690c663e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,6 +8,7 @@ appcompat = "1.7.0" composeBom = "2024.05.00" composeNavigation = "2.7.7" coroutines = "1.8.1" +desugar_jdk_libs = "2.0.4" dokka = "1.9.20" jUnit = "4.13.2" jUnitExt = "1.1.5" @@ -35,6 +36,7 @@ composeUI = { group = "androidx.compose.ui", name = "ui" } composeUITooling = { group = "androidx.compose.ui", name = "ui-tooling" } composeViewBinding = { group = "androidx.compose.ui", name = "ui-viewbinding" } coroutinesCore = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "coroutines" } +desugarJdkLibs = { module = "com.android.tools:desugar_jdk_libs", version.ref = "desugar_jdk_libs" } jUnit = { module = "junit:junit", version.ref = "jUnit" } jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "jupiter" } jupiterParams = { module = "org.junit.jupiter:junit-jupiter-params", version.ref = "jupiter" } diff --git a/settings.gradle.kts b/settings.gradle.kts index 5f9e9f89b..59537b5dc 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -33,6 +33,7 @@ dependencyResolutionManagement { rootProject.name = "Vico" include( + "tangem-demo", "sample", "vico", "vico:compose", diff --git a/tangem-demo/build.gradle b/tangem-demo/build.gradle new file mode 100644 index 000000000..850955d44 --- /dev/null +++ b/tangem-demo/build.gradle @@ -0,0 +1,78 @@ +plugins { + id "app.cash.paparazzi" + id "com.android.application" + id "kotlin-android" + id "org.jetbrains.kotlin.plugin.compose" +} + +android { + defaultConfig { + minSdk library.compose_min_sdk + targetSdk library.target_sdk + compileSdk library.target_sdk + versionCode library.version_code + versionName library.version_name + } + + signingConfigs { + release { + storeFile file("keystore") + storePassword '123456' + keyAlias 'key' + keyPassword '123456' + + v1SigningEnabled true + v2SigningEnabled true + } + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" + signingConfig = signingConfigs.release + applicationIdSuffix = ".release" + } + + debug { + debuggable = true + } + } + + buildFeatures { + compose true + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + coreLibraryDesugaringEnabled true + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_1_8.toString() + } + + namespace "com.tangem.vico.demo" +} + +dependencies { + implementation project(":vico:compose-m3") + implementation platform(libs.composeBom) + coreLibraryDesugaring(libs.desugarJdkLibs) + implementation libs.activityCompose + implementation libs.androidXCore + implementation libs.appcompat + implementation libs.composeMaterial3 + implementation libs.composeNavigation + implementation libs.composePreview + implementation libs.composeUI + implementation libs.composeViewBinding + implementation libs.coroutinesCore + implementation libs.kotlinStdLib + implementation libs.lifecycleRuntime + implementation libs.lifecycleRuntimeCompose + implementation libs.material + implementation libs.systemUIController + implementation libs.viewModelCompose +} diff --git a/tangem-demo/keystore b/tangem-demo/keystore new file mode 100644 index 000000000..6d37319c6 Binary files /dev/null and b/tangem-demo/keystore differ diff --git a/tangem-demo/proguard-rules.pro b/tangem-demo/proguard-rules.pro new file mode 100644 index 000000000..e69de29bb diff --git a/tangem-demo/src/main/AndroidManifest.xml b/tangem-demo/src/main/AndroidManifest.xml new file mode 100644 index 000000000..ebfc62ebf --- /dev/null +++ b/tangem-demo/src/main/AndroidManifest.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + diff --git a/tangem-demo/src/main/java/com/tangem/vico/demo/ChartHost.kt b/tangem-demo/src/main/java/com/tangem/vico/demo/ChartHost.kt new file mode 100644 index 000000000..83d2d04cf --- /dev/null +++ b/tangem-demo/src/main/java/com/tangem/vico/demo/ChartHost.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2024 by Patryk Goworowski and Patrick Michalik. + * + * 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.tangem.vico.demo + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.imePadding +import androidx.compose.foundation.layout.systemBarsPadding +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.tangem.vico.demo.chart.MarketDetailsChart + +@Composable +fun ChartHost( + chartId: Int, +) { + val chartScreen = ChartScreen.entries.first { it.id == chartId } + + Box( + modifier = Modifier + .fillMaxSize() + .systemBarsPadding() + .imePadding(), + ) { + when (chartScreen) { + ChartScreen.MarketDetails -> { + MarketDetailsChart() + } + } + } +} diff --git a/tangem-demo/src/main/java/com/tangem/vico/demo/ChartList.kt b/tangem-demo/src/main/java/com/tangem/vico/demo/ChartList.kt new file mode 100644 index 000000000..f58adfac9 --- /dev/null +++ b/tangem-demo/src/main/java/com/tangem/vico/demo/ChartList.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2024 by Patryk Goworowski and Patrick Michalik. + * + * 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.tangem.vico.demo + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.imePadding +import androidx.compose.foundation.layout.navigationBarsPadding +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.systemBarsPadding +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.navigation.NavController + +@Composable +fun ChartList(navController: NavController) { + Column( + modifier = Modifier + .systemBarsPadding() + .imePadding() + .padding(vertical = 48.dp) + .fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + ChartScreen.entries.forEach { + Button( + onClick = { navController.navigate("chart/${it.id}") }, + ) { + Row(horizontalArrangement = Arrangement.Center) { + Text(text = it.screenName) + } + } + } + Spacer(modifier = Modifier.height(24.dp)) + } +} diff --git a/tangem-demo/src/main/java/com/tangem/vico/demo/ChartScreen.kt b/tangem-demo/src/main/java/com/tangem/vico/demo/ChartScreen.kt new file mode 100644 index 000000000..7bc905737 --- /dev/null +++ b/tangem-demo/src/main/java/com/tangem/vico/demo/ChartScreen.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2024 by Patryk Goworowski and Patrick Michalik. + * + * 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.tangem.vico.demo + +enum class ChartScreen(val id : Int, val screenName: String) { + MarketDetails(0, "Market Details Chart") +} diff --git a/tangem-demo/src/main/java/com/tangem/vico/demo/DemoApp.kt b/tangem-demo/src/main/java/com/tangem/vico/demo/DemoApp.kt new file mode 100644 index 000000000..6ddfedbbc --- /dev/null +++ b/tangem-demo/src/main/java/com/tangem/vico/demo/DemoApp.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2024 by Patryk Goworowski and Patrick Michalik. + * + * 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.tangem.vico.demo + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.navigation.NavType +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.compose.rememberNavController +import androidx.navigation.navArgument +import com.patrykandpatrick.vico.compose.common.VicoTheme + +@Composable +fun DemoApp() { + val navController = rememberNavController() + DemoVicoTheme { + NavHost(navController = navController, startDestination = "chart/1") { +// NavHost(navController = navController, startDestination = "chartList") { + composable("chartList") { ChartList(navController) } + composable( + "chart/{chartId}", + listOf( + navArgument("chartId") { type = NavType.IntType }, + ), + ) { backStackEntry -> + val arguments = requireNotNull(backStackEntry.arguments) + ChartHost(chartId = arguments.getInt("chartId")) + } + } + } +} diff --git a/tangem-demo/src/main/java/com/tangem/vico/demo/MainActivity.kt b/tangem-demo/src/main/java/com/tangem/vico/demo/MainActivity.kt new file mode 100644 index 000000000..46560d318 --- /dev/null +++ b/tangem-demo/src/main/java/com/tangem/vico/demo/MainActivity.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2024 by Patryk Goworowski and Patrick Michalik. + * + * 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.tangem.vico.demo + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge + +internal class MainActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + enableEdgeToEdge() + setContent { DemoApp() } + } +} diff --git a/tangem-demo/src/main/java/com/tangem/vico/demo/VicoTheme.kt b/tangem-demo/src/main/java/com/tangem/vico/demo/VicoTheme.kt new file mode 100644 index 000000000..da682d518 --- /dev/null +++ b/tangem-demo/src/main/java/com/tangem/vico/demo/VicoTheme.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2024 by Patryk Goworowski and Patrick Michalik. + * + * 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.tangem.vico.demo + +import android.os.Build +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext + +@Composable +fun DemoVicoTheme(useDynamicColor: Boolean = true, content: @Composable () -> Unit) { + val isSystemInDarkTheme = isSystemInDarkTheme() + MaterialTheme( + colorScheme = + when { + useDynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { + val context = LocalContext.current + if (isSystemInDarkTheme) dynamicDarkColorScheme(context) + else dynamicLightColorScheme(context) + } + isSystemInDarkTheme -> darkColorScheme() + else -> lightColorScheme() + }, + content = content, + ) +} diff --git a/tangem-demo/src/main/java/com/tangem/vico/demo/chart/MarketDetailsChart.kt b/tangem-demo/src/main/java/com/tangem/vico/demo/chart/MarketDetailsChart.kt new file mode 100644 index 000000000..ef6a8ea09 --- /dev/null +++ b/tangem-demo/src/main/java/com/tangem/vico/demo/chart/MarketDetailsChart.kt @@ -0,0 +1,587 @@ +/* + * Copyright 2024 by Patryk Goworowski and Patrick Michalik. + * + * 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.tangem.vico.demo.chart + +import android.os.Build +import android.util.Log +import androidx.compose.animation.core.LinearEasing +import androidx.compose.animation.core.animate +import androidx.compose.animation.core.tween +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableFloatStateOf +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.rememberTextMeasurer +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.patrykandpatrick.vico.compose.cartesian.CartesianChartHost +import com.patrykandpatrick.vico.compose.cartesian.axis.rememberAxisGuidelineComponent +import com.patrykandpatrick.vico.compose.cartesian.axis.rememberAxisLabelComponent +import com.patrykandpatrick.vico.compose.cartesian.axis.rememberBottomAxis +import com.patrykandpatrick.vico.compose.cartesian.axis.rememberUnrestrictedStartAxis +import com.patrykandpatrick.vico.compose.cartesian.fullWidth +import com.patrykandpatrick.vico.compose.cartesian.layer.rememberLineCartesianLayer +import com.patrykandpatrick.vico.compose.cartesian.layer.rememberLineSpec +import com.patrykandpatrick.vico.compose.cartesian.layer.rememberSplitLineSpec +import com.patrykandpatrick.vico.compose.cartesian.rememberCartesianChart +import com.patrykandpatrick.vico.compose.cartesian.rememberVicoZoomState +import com.patrykandpatrick.vico.compose.common.of +import com.patrykandpatrick.vico.compose.common.shader.BrushShader +import com.patrykandpatrick.vico.core.cartesian.HorizontalLayout +import com.patrykandpatrick.vico.core.cartesian.Zoom +import com.patrykandpatrick.vico.core.cartesian.axis.AxisItemPlacer +import com.patrykandpatrick.vico.core.cartesian.axis.AxisPosition +import com.patrykandpatrick.vico.core.cartesian.axis.BaseAxis +import com.patrykandpatrick.vico.core.cartesian.axis.HorizontalAxis +import com.patrykandpatrick.vico.core.cartesian.axis.VerticalAxis +import com.patrykandpatrick.vico.core.cartesian.data.AxisValueOverrider +import com.patrykandpatrick.vico.core.cartesian.data.CartesianChartModelProducer +import com.patrykandpatrick.vico.core.cartesian.data.CartesianValueFormatter +import com.patrykandpatrick.vico.core.cartesian.data.lineSeries +import com.patrykandpatrick.vico.core.cartesian.layer.LineCartesianLayer +import com.patrykandpatrick.vico.core.common.Dimensions +import com.patrykandpatrick.vico.core.common.component.LineComponent +import com.patrykandpatrick.vico.core.common.shader.ColorShader +import com.patrykandpatrick.vico.core.common.shader.LinearGradientShader +import com.patrykandpatrick.vico.core.common.shape.Shape +import com.tangem.vico.demo.utils.MyVerticalAxisItemPlacer +import java.time.LocalDateTime +import java.time.OffsetDateTime +import java.time.ZoneOffset +import java.time.format.DateTimeFormatter +import java.util.Locale +import java.util.concurrent.TimeUnit +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + + +private val y = listOf( + 59270, 61748, 61941, 62889, 62740, 62989, 63858, 63608, 63951, 63917, + 63253, 63765, 63862, 64502, 63876, 64028, 63875, 64531, 64249, 63680, + 63228, 63159, 63249, 63664, 63469, 63711, 63003, 62333, 62882, 62183, + 62231, 62244, 62200, 61188, 61683, 61199, 61036, 62116, 62436, 63065, + 62908, 63067, 63294, 60904, 60677, 60790, 60705, 61041, 60667, 61158, + 61112, 60795, 60900, 60771, 61124, 61343, 61371, 61484, 61133, 62372, + 62704, 63007, 63092, 62905, 62470, 62019, 61756, 61776, 61557, 61540, + 61904, 62151, 62419, 64663, 65955, 66229, 65991, 66176, 66517, 65838, + 65210, 65216, 65611, 66467, 66209, 67247, 66913, 67059, 66978, 66845, + 67240, 66874, 66993, 66940, 67185, 67330, 67340, 66845, 66062, 66273, + 66645, 66865, 67005, 67382, 70049, 71464, 71293, 70875, 71137, 69720, + 69323, 70139, 69964, 69716, 69855, 70442, 69774, 69125, 69404, 69714, + 69967, 68042, 67077, 67938, 67833, 67182, 67306, 68327, 69093, 68517, + 68726, 68759, 69061, 68880, 69148, 69305, 69044, 69313, 69122, 68821, + 68854, 68506, 68823, 68613, 68420, 70356, 69241, 69401, 68004, 67630, + 68311, 68311, 68291, 68302, 68717, 67926, 67690, 67314, 67285, 67567, + 68026, 67552, 67740, 68519, 68611, 68363, 68503, 68171, 68326, 67121, + 67626, 67481, 67657, 67567, 67608, 67607, 67683, 67729, 67733, 67709, +) +val timestamps: List = listOf( + 1714752000000, + 1714766400000, + 1714780800000, + 1714795200000, + 1714809600000, + 1714824000000, + 1714838400000, + 1714852800000, + 1714867200000, + 1714881600000, + 1714896000000, + 1714910400000, + 1714924800000, + 1714939200000, + 1714953600000, + 1714968000000, + 1714982400000, + 1714996800000, + 1715011200000, + 1715025600000, + 1715040000000, + 1715054400000, + 1715068800000, + 1715083200000, + 1715097600000, + 1715112000000, + 1715126400000, + 1715140800000, + 1715155200000, + 1715169600000, + 1715184000000, + 1715198400000, + 1715212800000, + 1715227200000, + 1715241600000, + 1715256000000, + 1715270400000, + 1715284800000, + 1715299200000, + 1715313600000, + 1715328000000, + 1715342400000, + 1715356800000, + 1715371200000, + 1715385600000, + 1715400000000, + 1715414400000, + 1715428800000, + 1715443200000, + 1715457600000, + 1715472000000, + 1715486400000, + 1715500800000, + 1715515200000, + 1715529600000, + 1715544000000, + 1715558400000, + 1715572800000, + 1715587200000, + 1715601600000, + 1715616000000, + 1715630400000, + 1715644800000, + 1715659200000, + 1715673600000, + 1715688000000, + 1715702400000, + 1715716800000, + 1715731200000, + 1715745600000, + 1715760000000, + 1715774400000, + 1715788800000, + 1715803200000, + 1715817600000, + 1715832000000, + 1715846400000, + 1715860800000, + 1715875200000, + 1715889600000, + 1715904000000, + 1715918400000, + 1715932800000, + 1715947200000, + 1715961600000, + 1715976000000, + 1715990400000, + 1716004800000, + 1716019200000, + 1716033600000, + 1716048000000, + 1716062400000, + 1716076800000, + 1716091200000, + 1716105600000, + 1716120000000, + 1716134400000, + 1716148800000, + 1716163200000, + 1716177600000, + 1716192000000, + 1716206400000, + 1716220800000, + 1716235200000, + 1716249600000, + 1716264000000, + 1716278400000, + 1716292800000, + 1716307200000, + 1716321600000, + 1716336000000, + 1716350400000, + 1716364800000, + 1716379200000, + 1716393600000, + 1716408000000, + 1716422400000, + 1716436800000, + 1716451200000, + 1716465600000, + 1716480000000, + 1716494400000, + 1716508800000, + 1716523200000, + 1716537600000, + 1716552000000, + 1716566400000, + 1716580800000, + 1716595200000, + 1716609600000, + 1716624000000, + 1716638400000, + 1716652800000, + 1716667200000, + 1716681600000, + 1716696000000, + 1716710400000, + 1716724800000, + 1716739200000, + 1716753600000, + 1716768000000, + 1716782400000, + 1716796800000, + 1716811200000, + 1716825600000, + 1716840000000, + 1716854400000, + 1716868800000, + 1716883200000, + 1716897600000, + 1716912000000, + 1716926400000, + 1716940800000, + 1716955200000, + 1716969600000, + 1716984000000, + 1716998400000, + 1717012800000, + 1717027200000, + 1717041600000, + 1717056000000, + 1717070400000, + 1717084800000, + 1717099200000, + 1717113600000, + 1717128000000, + 1717142400000, + 1717156800000, + 1717171200000, + 1717185600000, + 1717200000000, + 1717214400000, + 1717228800000, + 1717243200000, + 1717257600000, + 1717272000000, + 1717286400000, + 1717300800000, + 1717315200000, + 1717329600000, +) + +private val x = List(y.size) { index -> index } + +private val redColor = Color(0xFFFF3333) +private val blueColor = Color(0xFF0099FF) + +@Composable +fun MarketDetailsChart(modifier: Modifier = Modifier) { + val initDrawAnimationStart = remember { mutableStateOf(true) } + var markerFraction: Float? by remember { + mutableStateOf(null) + } + var lineColor by remember { + mutableStateOf(redColor) + } + + val layer = rememberLayer( + lineColor = lineColor, + startDrawingAnimation = initDrawAnimationStart, + markerFraction = markerFraction, + ) + + val chart = rememberCartesianChart( + layer, + startAxis = rememberMyStartAxis(), + bottomAxis = rememberMyBottomAxis(), + ) + + val modelProducer = remember { + CartesianChartModelProducer.build() + } + + LaunchedEffect(Unit) { + withContext(Dispatchers.Default) { + modelProducer.tryRunTransaction { + lineSeries { + series(x, y) + } + } + } + } + + var valueText by remember { + mutableStateOf("") + } + var dateText by remember { + mutableStateOf("") + } + var maxCanvasX by remember { + mutableIntStateOf(0) + } + + Column { + Text(text = "Value: $valueText") + Text(text = "Date: $dateText") + + CartesianChartHost( + modifier = Modifier + .fillMaxWidth() + .onGloballyPositioned { + maxCanvasX = it.size.width + }, + chart = chart, + modelProducer = modelProducer, + zoomState = rememberVicoZoomState(initialZoom = Zoom.Content, zoomEnabled = false), + horizontalLayout = HorizontalLayout.fullWidth(), + diffAnimationSpec = null, + markerVisibilityListener = rememberMarkerVisibilityListener { y, x, xCanvas -> + valueText = y?.toString() ?: "" + dateText = x?.let { + timestamps[it.toInt()].toLocalDateTime() + .format(DateTimeFormatter.ofPattern("dd.MM", Locale.getDefault())) + } ?: "" + + markerFraction = if (xCanvas == null || xCanvas == 0f) { + null + } else { + val max = maxCanvasX.toFloat() + if (max == 0f) { + null + } else { + xCanvas / maxCanvasX.toFloat() + } + } + }, + marker = rememberTangemChartMarker(color = lineColor), + ) + + Spacer(modifier = Modifier.height(16.dp)) + Button(onClick = { initDrawAnimationStart.value = true }) { + Text("Start Drawing Animation") + } + Button( + onClick = { + lineColor = if (lineColor == redColor) { + blueColor + } else { + redColor + } + }, + ) { + Text(text = "Change Line Color") + } + } +} + +@Composable +fun rememberMyStartAxis(modifier: Modifier = Modifier): VerticalAxis { + return rememberUnrestrictedStartAxis( + axis = null, + tick = null, + guideline = rememberMyAxisGuidelineComponent(), + label = rememberAxisLabelComponent( + color = Color(0xFF909090), + padding = Dimensions.of( + start = 4.dp, + ), + textSize = 14.sp, + textAlignment = android.text.Layout.Alignment.ALIGN_CENTER, + ), + horizontalLabelPosition = VerticalAxis.HorizontalLabelPosition.Inside, + verticalLabelPosition = VerticalAxis.VerticalLabelPosition.Center, + itemPlacer = remember { AxisItemPlacer.Vertical.count({ 3 }, false) }, + valueFormatter = remember { CartesianValueFormatter.decimal() }, + ) +} + +@Composable +fun rememberMyBottomAxis(modifier: Modifier = Modifier): HorizontalAxis { + return rememberBottomAxis( + label = rememberAxisLabelComponent( + color = Color(0xFF909090), + padding = Dimensions.of( + top = 24.dp, + ), + ), + tick = null, + axis = null, + guideline = null, + sizeConstraint = BaseAxis.SizeConstraint.Auto(24.dp.value, 48.dp.value), + itemPlacer = remember { + AxisItemPlacer.Horizontal.default( + spacing = 20, + offset = 60, + shiftExtremeTicks = false, + addExtremeLabelPadding = false, + ) + }, + valueFormatter = CartesianValueFormatter { value, chartValues, verticalAxisPosition -> + timestamps[value.toInt()].toLocalDateTime() + .format(DateTimeFormatter.ofPattern("dd.MM", Locale.getDefault())) + }, + ) +} + +@Composable +fun rememberMyAxisGuidelineComponent(): LineComponent { + return rememberAxisGuidelineComponent( + color = Color(0x1fc9c9c9), + shape = Shape.Rectangle, + margins = Dimensions( + startDp = (measureTextWidth( + text = "55555", + style = TextStyle.Default, + ) + 16.dp).value, //get longest label's text + endDp = 0f, + topDp = 0f, + bottomDp = 0f, + ), + thickness = 2.dp, + ) +} + +@Composable +fun measureTextWidth(text: String, style: TextStyle): Dp { + val textMeasurer = rememberTextMeasurer() + val density = LocalDensity.current + return remember(textMeasurer, density, text, style) { + val widthInPixels = textMeasurer.measure(text, style).size.width + with(density) { widthInPixels.toDp() } + } +} + +@Composable +fun rememberLayer( + lineColor: Color, + markerFraction: Float? = null, + startDrawingAnimation: MutableState, +): LineCartesianLayer { + val backColor = listOf(lineColor.copy(alpha = 0.24f), Color.Transparent) + + val lineColor2 = Color(0x33909090) + val backColor2 = listOf(Color(0x3d909090), Color.Transparent) + + var value by remember { + mutableFloatStateOf(0f) + } + + LaunchedEffect(startDrawingAnimation.value) { + if (startDrawingAnimation.value) { + animate( + initialValue = 0f, + targetValue = 1f, + animationSpec = tween(easing = LinearEasing, durationMillis = 2000), + ) { start, end -> + value = start + if (start == 1f) { + startDrawingAnimation.value = false + } + } + } + } + + return buildLayer( + fractionValue = markerFraction ?: value, + lineColor = if (markerFraction != null) { + lineColor2 + } else { + lineColor + }, + backColor = if (markerFraction != null) { + backColor2 + } else { + backColor + }, + lineColorRight = if (markerFraction != null) { + lineColor + } else { + Color.Transparent + }, + backColorRight = if (markerFraction != null) { + backColor + } else { + listOf(Color.Transparent, Color.Transparent) + }, + ) +} + +@Composable +fun buildLayer( + fractionValue: Float, + lineColor: Color, + backColor: List, + lineColorRight: Color? = null, + backColorRight: List? = null, +): LineCartesianLayer { + val alineColor = remember(lineColor) { lineColor.toArgb() } + val alineColorRight = remember(lineColorRight) { lineColorRight?.toArgb() } + + if (alineColorRight == null || backColorRight == null) { + return rememberLineCartesianLayer( + listOf( + rememberLineSpec( + shader = ColorShader( + color = alineColor, + ), + thickness = 0.5.dp, + backgroundShader = BrushShader( + brush = Brush.verticalGradient(backColor), + ), + ), + ), + + ) + } + + return rememberLineCartesianLayer( + listOf( + rememberSplitLineSpec( + shader = LinearGradientShader( + colors = intArrayOf(alineColor, alineColorRight), + positions = floatArrayOf(fractionValue, fractionValue), + isHorizontal = true, + ), + backgroundShaderFirst = BrushShader( + brush = Brush.verticalGradient(backColor), + ), + backgroundShaderSecond = BrushShader( + brush = Brush.verticalGradient(backColorRight), + ), + xSplitFraction = fractionValue, + ), + ), + axisValueOverrider = remember { AxisValueOverrider.fixed(minY = y.min().toFloat()) }, + ) + +} + + +fun Long.toLocalDateTime(isMillis: Boolean = true): LocalDateTime { + val zoneOffset = OffsetDateTime.now(ZoneOffset.systemDefault()).offset + val epochSeconds = if (isMillis) TimeUnit.MILLISECONDS.toSeconds(this) else this + return LocalDateTime.ofEpochSecond(epochSeconds, 0, zoneOffset) +} + diff --git a/tangem-demo/src/main/java/com/tangem/vico/demo/chart/TangemChartMarker.kt b/tangem-demo/src/main/java/com/tangem/vico/demo/chart/TangemChartMarker.kt new file mode 100644 index 000000000..d0222ad27 --- /dev/null +++ b/tangem-demo/src/main/java/com/tangem/vico/demo/chart/TangemChartMarker.kt @@ -0,0 +1,99 @@ +/* + * Copyright 2024 by Patryk Goworowski and Patrick Michalik. + * + * 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.tangem.vico.demo.chart + +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import com.patrykandpatrick.vico.compose.common.component.rememberLayeredComponent +import com.patrykandpatrick.vico.compose.common.component.rememberShapeComponent +import com.patrykandpatrick.vico.compose.common.component.rememberUnboundedLineComponent +import com.patrykandpatrick.vico.compose.common.of +import com.patrykandpatrick.vico.compose.common.shape.dashed +import com.patrykandpatrick.vico.core.cartesian.CartesianMeasureContext +import com.patrykandpatrick.vico.core.cartesian.HorizontalDimensions +import com.patrykandpatrick.vico.core.cartesian.Insets +import com.patrykandpatrick.vico.core.cartesian.marker.CartesianMarker +import com.patrykandpatrick.vico.core.cartesian.marker.DefaultCartesianMarker +import com.patrykandpatrick.vico.core.common.Defaults +import com.patrykandpatrick.vico.core.common.Dimensions +import com.patrykandpatrick.vico.core.common.component.TextComponent +import com.patrykandpatrick.vico.core.common.shape.Shape + +@Composable +fun rememberTangemChartMarker( + color: Color, +): CartesianMarker { + val indicatorFrontComponent = + rememberShapeComponent(Shape.Pill, MaterialTheme.colorScheme.surface) + val indicatorCenterComponent = rememberShapeComponent( + shape = Shape.Pill, + color = color, + ) + val indicatorRearComponent = rememberShapeComponent( + Shape.Pill, + color = color.copy(alpha = .24f) + ) + val indicator = + rememberLayeredComponent( + rear = indicatorRearComponent, + front = rememberLayeredComponent( + rear = indicatorCenterComponent, + front = indicatorFrontComponent, + padding = Dimensions.of(3.dp), + ), + padding = Dimensions.of(3.dp), + ) + val guideline = rememberUnboundedLineComponent( + color = color, + verticalAddDrawSpace = 24.dp, + shape = Shape.dashed(Shape.Rectangle, Defaults.DASH_LENGTH.dp, Defaults.DASH_GAP.dp), + ) + return remember(indicator, guideline) { + object : + DefaultCartesianMarker( + label = TextComponent.build { textSizeSp = 0f }, + indicator = indicator, + indicatorSizeDp = 16f, +// setIndicatorColor = { bColor -> +// indicatorRearComponent.color = color.toArgb().copyColor(alpha = .24f) +// indicatorCenterComponent.color = color.toArgb() +// }, + guideline = guideline, + ) { + override fun getInsets( + context: CartesianMeasureContext, + outInsets: Insets, + horizontalDimensions: HorizontalDimensions, + ) { + with(context) { + super.getInsets(context, outInsets, horizontalDimensions) + val baseShadowInsetDp = + CLIPPING_FREE_SHADOW_RADIUS_MULTIPLIER * LABEL_BACKGROUND_SHADOW_RADIUS_DP + outInsets.top += (baseShadowInsetDp - LABEL_BACKGROUND_SHADOW_DY_DP).pixels + outInsets.bottom += (baseShadowInsetDp + LABEL_BACKGROUND_SHADOW_DY_DP).pixels + } + } + } + } +} + +private const val LABEL_BACKGROUND_SHADOW_RADIUS_DP = 4f +private const val LABEL_BACKGROUND_SHADOW_DY_DP = 2f +private const val CLIPPING_FREE_SHADOW_RADIUS_MULTIPLIER = 1.4f diff --git a/tangem-demo/src/main/java/com/tangem/vico/demo/chart/TangemMarkerVisibilityListener.kt b/tangem-demo/src/main/java/com/tangem/vico/demo/chart/TangemMarkerVisibilityListener.kt new file mode 100644 index 000000000..f278a3eeb --- /dev/null +++ b/tangem-demo/src/main/java/com/tangem/vico/demo/chart/TangemMarkerVisibilityListener.kt @@ -0,0 +1,56 @@ +/* + * Copyright 2024 by Patryk Goworowski and Patrick Michalik. + * + * 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.tangem.vico.demo.chart + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.hapticfeedback.HapticFeedbackType +import androidx.compose.ui.platform.LocalHapticFeedback +import com.patrykandpatrick.vico.core.cartesian.marker.CartesianMarker +import com.patrykandpatrick.vico.core.cartesian.marker.CartesianMarkerVisibilityListener +import com.patrykandpatrick.vico.core.cartesian.marker.LineCartesianLayerMarkerTarget + +@Composable +fun rememberMarkerVisibilityListener( + yxFractionCallback : (y : Float?, x : Float?, xCanvas : Float?) -> Unit +): CartesianMarkerVisibilityListener { + val hapticFeedback = LocalHapticFeedback.current + + return remember { + object : CartesianMarkerVisibilityListener { + override fun onShown(marker: CartesianMarker, targets: List) { + hapticFeedback.performHapticFeedback(HapticFeedbackType.TextHandleMove) + + val tg = targets[0] as LineCartesianLayerMarkerTarget + val point = tg.points[0] + yxFractionCallback(point.entry.y, point.entry.x, tg.canvasX) + } + + override fun onHidden(marker: CartesianMarker) { + yxFractionCallback(null, null, null) + } + + override fun onUpdated(marker: CartesianMarker, targets: List) { + hapticFeedback.performHapticFeedback(HapticFeedbackType.TextHandleMove) + val tg = targets[0] as LineCartesianLayerMarkerTarget + val point = tg.points[0] + yxFractionCallback(point.entry.y, point.entry.x, tg.canvasX) + } + } + } + +} diff --git a/tangem-demo/src/main/java/com/tangem/vico/demo/utils/MyVerticalAxisItemPlacer.kt b/tangem-demo/src/main/java/com/tangem/vico/demo/utils/MyVerticalAxisItemPlacer.kt new file mode 100644 index 000000000..8f59060c2 --- /dev/null +++ b/tangem-demo/src/main/java/com/tangem/vico/demo/utils/MyVerticalAxisItemPlacer.kt @@ -0,0 +1,100 @@ +/* + * Copyright 2024 by Patryk Goworowski and Patrick Michalik. + * + * 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.tangem.vico.demo.utils + +import com.patrykandpatrick.vico.core.cartesian.CartesianDrawContext +import com.patrykandpatrick.vico.core.cartesian.CartesianMeasureContext +import com.patrykandpatrick.vico.core.cartesian.axis.AxisItemPlacer +import com.patrykandpatrick.vico.core.cartesian.axis.AxisPosition +import com.patrykandpatrick.vico.core.cartesian.axis.VerticalAxis + +object MyVerticalAxisItemPlacer : AxisItemPlacer.Vertical { + + private val default = AxisItemPlacer.Vertical.count({ 3 }, false) + + private fun List.trans(): List { + return mapIndexed { index, fl -> + fl +// when (index) { +// 0 -> { +// fl + 4 +// } +// +// 1 -> { +// fl + 2 +// } +// +// else -> fl +// } + } + } + + override fun getLabelValues( + context: CartesianDrawContext, + axisHeight: Float, + maxLabelHeight: Float, + position: AxisPosition.Vertical, + ): List { + return default.getLabelValues(context, axisHeight, maxLabelHeight, position) + .trans() + } + + override fun getWidthMeasurementLabelValues( + context: CartesianMeasureContext, + axisHeight: Float, + maxLabelHeight: Float, + position: AxisPosition.Vertical, + ): List { + return default.getWidthMeasurementLabelValues(context, axisHeight, maxLabelHeight, position) + .trans() + } + + override fun getHeightMeasurementLabelValues( + context: CartesianMeasureContext, + position: AxisPosition.Vertical, + ): List { + return default.getHeightMeasurementLabelValues(context, position).trans() + } + + override fun getTopVerticalAxisInset( + context: CartesianMeasureContext, + verticalLabelPosition: VerticalAxis.VerticalLabelPosition, + maxLabelHeight: Float, + maxLineThickness: Float, + ): Float { + return default.getTopVerticalAxisInset( + context, + verticalLabelPosition, + maxLabelHeight, + maxLineThickness, + ) + } + + override fun getBottomVerticalAxisInset( + context: CartesianMeasureContext, + verticalLabelPosition: VerticalAxis.VerticalLabelPosition, + maxLabelHeight: Float, + maxLineThickness: Float, + ): Float { + return default.getBottomVerticalAxisInset( + context, + verticalLabelPosition, + maxLabelHeight, + maxLineThickness, + ) + } +} diff --git a/tangem-demo/src/main/res/drawable/ic_baseline_android_24.xml b/tangem-demo/src/main/res/drawable/ic_baseline_android_24.xml new file mode 100644 index 000000000..787c7624a --- /dev/null +++ b/tangem-demo/src/main/res/drawable/ic_baseline_android_24.xml @@ -0,0 +1,26 @@ + + + + + diff --git a/tangem-demo/src/main/res/drawable/ic_caret_left.xml b/tangem-demo/src/main/res/drawable/ic_caret_left.xml new file mode 100644 index 000000000..21fa62907 --- /dev/null +++ b/tangem-demo/src/main/res/drawable/ic_caret_left.xml @@ -0,0 +1,29 @@ + + + + + diff --git a/tangem-demo/src/main/res/drawable/ic_caret_right.xml b/tangem-demo/src/main/res/drawable/ic_caret_right.xml new file mode 100644 index 000000000..dfdf962aa --- /dev/null +++ b/tangem-demo/src/main/res/drawable/ic_caret_right.xml @@ -0,0 +1,29 @@ + + + + + diff --git a/tangem-demo/src/main/res/drawable/ic_compose.xml b/tangem-demo/src/main/res/drawable/ic_compose.xml new file mode 100644 index 000000000..3d59afdf9 --- /dev/null +++ b/tangem-demo/src/main/res/drawable/ic_compose.xml @@ -0,0 +1,33 @@ + + + + + + diff --git a/tangem-demo/src/main/res/drawable/ic_views.xml b/tangem-demo/src/main/res/drawable/ic_views.xml new file mode 100644 index 000000000..fa11e1b38 --- /dev/null +++ b/tangem-demo/src/main/res/drawable/ic_views.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/tangem-demo/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/tangem-demo/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 000000000..a1255b61d --- /dev/null +++ b/tangem-demo/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/tangem-demo/src/main/res/mipmap-hdpi/ic_launcher_background.png b/tangem-demo/src/main/res/mipmap-hdpi/ic_launcher_background.png new file mode 100644 index 000000000..2749743ee Binary files /dev/null and b/tangem-demo/src/main/res/mipmap-hdpi/ic_launcher_background.png differ diff --git a/tangem-demo/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/tangem-demo/src/main/res/mipmap-hdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..66976955e Binary files /dev/null and b/tangem-demo/src/main/res/mipmap-hdpi/ic_launcher_foreground.png differ diff --git a/tangem-demo/src/main/res/mipmap-hdpi/ic_launcher_round.png b/tangem-demo/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 000000000..2cfb63c7e Binary files /dev/null and b/tangem-demo/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/tangem-demo/src/main/res/mipmap-mdpi/ic_launcher_background.png b/tangem-demo/src/main/res/mipmap-mdpi/ic_launcher_background.png new file mode 100644 index 000000000..ea144ce4c Binary files /dev/null and b/tangem-demo/src/main/res/mipmap-mdpi/ic_launcher_background.png differ diff --git a/tangem-demo/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/tangem-demo/src/main/res/mipmap-mdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..958bb8a6b Binary files /dev/null and b/tangem-demo/src/main/res/mipmap-mdpi/ic_launcher_foreground.png differ diff --git a/tangem-demo/src/main/res/mipmap-mdpi/ic_launcher_round.png b/tangem-demo/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 000000000..eb390fd84 Binary files /dev/null and b/tangem-demo/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/tangem-demo/src/main/res/mipmap-xhdpi/ic_launcher_background.png b/tangem-demo/src/main/res/mipmap-xhdpi/ic_launcher_background.png new file mode 100644 index 000000000..7d8945003 Binary files /dev/null and b/tangem-demo/src/main/res/mipmap-xhdpi/ic_launcher_background.png differ diff --git a/tangem-demo/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/tangem-demo/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..8bbe1fa4f Binary files /dev/null and b/tangem-demo/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png differ diff --git a/tangem-demo/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/tangem-demo/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 000000000..8cee72bbf Binary files /dev/null and b/tangem-demo/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/tangem-demo/src/main/res/mipmap-xxhdpi/ic_launcher_background.png b/tangem-demo/src/main/res/mipmap-xxhdpi/ic_launcher_background.png new file mode 100644 index 000000000..f0cbfe27c Binary files /dev/null and b/tangem-demo/src/main/res/mipmap-xxhdpi/ic_launcher_background.png differ diff --git a/tangem-demo/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/tangem-demo/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..477e01e68 Binary files /dev/null and b/tangem-demo/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png differ diff --git a/tangem-demo/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/tangem-demo/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 000000000..960124078 Binary files /dev/null and b/tangem-demo/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/tangem-demo/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png b/tangem-demo/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png new file mode 100644 index 000000000..658c710a2 Binary files /dev/null and b/tangem-demo/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png differ diff --git a/tangem-demo/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/tangem-demo/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..9d58bda5d Binary files /dev/null and b/tangem-demo/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ diff --git a/tangem-demo/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/tangem-demo/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 000000000..f3dbb59f4 Binary files /dev/null and b/tangem-demo/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/tangem-demo/src/main/res/values/themes.xml b/tangem-demo/src/main/res/values/themes.xml new file mode 100644 index 000000000..8842f2063 --- /dev/null +++ b/tangem-demo/src/main/res/values/themes.xml @@ -0,0 +1,22 @@ + + + + + + +