diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml new file mode 100644 index 0000000..c76712e --- /dev/null +++ b/.github/workflows/android.yml @@ -0,0 +1,26 @@ +name: Android CI + +on: + push: + branches: [ "develop" ] + pull_request: + branches: [ "develop" ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: "17" + distribution: "temurin" + cache: gradle + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + - name: Build with Gradle + run: ./gradlew ktlintCheck buildDebug diff --git a/.gitignore b/.gitignore index 347e252..a001ee5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ +# Created by https://www.toptal.com/developers/gitignore/api/android,androidstudio,macos,windows,intellij,java,kotlin +# Edit at https://www.toptal.com/developers/gitignore?templates=android,androidstudio,macos,windows,intellij,java,kotlin + +### Android ### # Gradle files .gradle/ build/ @@ -31,3 +35,330 @@ google-services.json # Android Profiling *.hprof + +### Android Patch ### +gen-external-apklibs + +# Replacement of .externalNativeBuild directories introduced +# with Android Studio 3.5. + +### Intellij ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### Intellij Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint +.idea/**/sonarlint/ + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin +.idea/**/sonarIssues.xml + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced +.idea/**/markdown-navigator.xml +.idea/**/markdown-navigator-enh.xml +.idea/**/markdown-navigator/ + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 +.idea/$CACHE_FILE$ + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream +.idea/codestream.xml + +# Azure Toolkit for IntelliJ plugin +# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij +.idea/**/azureSettings.xml + +### Java ### +# Compiled class file +*.class + +# Log file + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +replay_pid* + +### Kotlin ### +# Compiled class file + +# Log file + +# BlueJ files + +# Mobile Tools for Java (J2ME) + +# Package Files # + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +### AndroidStudio ### +# Covers files to be ignored for android development using Android Studio. + +# Built application files +*.ap_ +*.aab + +# Files for the ART/Dalvik VM +*.dex + +# Java class files + +# Generated files +bin/ +gen/ + +# Gradle files +.gradle + +# Signing files +.signing/ + +# Local configuration file (sdk path, etc) + +# Proguard folder generated by Eclipse +proguard/ + +# Log Files + +# Android Studio +/*/build/ +/*/local.properties +/*/out +/*/*/build +/*/*/production +.navigation/ +*.ipr +*~ +*.swp + +# Keystore files + +# Google Services (e.g. APIs or Firebase) +# google-services.json + +# Android Patch + +# External native build folder generated in Android Studio 2.2 and later +.externalNativeBuild + +# NDK +obj/ + +# IntelliJ IDEA +/out/ + +# User-specific configurations +.idea/caches/ +.idea/libraries/ +.idea/shelf/ +.idea/workspace.xml +.idea/tasks.xml +.idea/.name +.idea/compiler.xml +.idea/copyright/profiles_settings.xml +.idea/encodings.xml +.idea/misc.xml +.idea/modules.xml +.idea/scopes/scope_settings.xml +.idea/dictionaries +.idea/vcs.xml +.idea/jsLibraryMappings.xml +.idea/datasources.xml +.idea/dataSources.ids +.idea/sqlDataSources.xml +.idea/dynamic.xml +.idea/uiDesigner.xml +.idea/assetWizardSettings.xml +.idea/gradle.xml +.idea/jarRepositories.xml +.idea/navEditor.xml + +# Legacy Eclipse project files +.classpath +.project +.cproject +.settings/ + +# Mobile Tools for Java (J2ME) + +# Package Files # + +# virtual machine crash logs (Reference: http://www.java.com/en/download/help/error_hotspot.xml) + +## Plugin-specific files: + +# mpeltonen/sbt-idea plugin + +# JIRA plugin + +# Mongo Explorer plugin +.idea/mongoSettings.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) + +### AndroidStudio Patch ### + +!/gradle/wrapper/gradle-wrapper.jar + +# End of https://www.toptal.com/developers/gitignore/api/android,androidstudio,macos,windows,intellij,java,kotlin \ No newline at end of file diff --git a/.media/brand_image.png b/.media/brand_image.png new file mode 100644 index 0000000..fcfe238 Binary files /dev/null and b/.media/brand_image.png differ diff --git a/.media/brand_image.psd b/.media/brand_image.psd new file mode 100644 index 0000000..195dcf3 Binary files /dev/null and b/.media/brand_image.psd differ diff --git a/.media/icon.png b/.media/icon.png new file mode 100644 index 0000000..1f9f350 Binary files /dev/null and b/.media/icon.png differ diff --git a/.media/icon.psd b/.media/icon.psd new file mode 100644 index 0000000..e5189a1 Binary files /dev/null and b/.media/icon.psd differ diff --git a/LICENSE b/LICENSE index 261eeb9..cc1e2c6 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + Copyright 2023 Florian Schuster Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index 8b88771..d55aa84 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,5 @@ # hydro + simple hydration reminder app + +![logo](.media/icon.png) \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..769ab73 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,102 @@ +plugins { + id "com.android.application" + id "org.jetbrains.kotlin.android" + id "kotlin-parcelize" + id "org.jlleitschuh.gradle.ktlint" + id "kotlinx-serialization" + id "com.google.devtools.ksp" +} + +android { + namespace "at.florianschuster.hydro" + compileSdk 34 + + defaultConfig { + applicationId "at.florianschuster.hydro" + minSdk 28 + targetSdk 34 + versionCode 1 + versionName "0.1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + vectorDrawables { + useSupportLibrary true + } + ksp { + arg("room.schemaLocation", "$projectDir/schemas") + } + } + + buildTypes { + debug { + debuggable true + } + release { + debuggable false + minifyEnabled false + proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 + } + kotlinOptions { + jvmTarget = "17" + } + buildFeatures { + compose true + buildConfig true + } + composeOptions { + kotlinCompilerExtensionVersion "1.5.1" + } + packagingOptions { + resources { + excludes += "/META-INF/{AL2.0,LGPL2.1}" + } + exclude("META-INF/versions/9/previous-compilation-data.bin") + } +} + +dependencies { + implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.9.0")) + implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0") + implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0-RC" + + implementation("androidx.core:core-ktx:1.10.1") + implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1") + implementation("androidx.lifecycle:lifecycle-runtime-compose:2.6.1") + implementation("androidx.lifecycle:lifecycle-process:2.6.1") + implementation("androidx.activity:activity-compose:1.7.2") + implementation("androidx.core:core-splashscreen:1.0.1") + + implementation("androidx.browser:browser:1.6.0") + implementation("androidx.datastore:datastore-preferences:1.0.0") + + implementation(platform("androidx.compose:compose-bom:2023.06.01")) + implementation("androidx.compose.animation:animation") + implementation("androidx.compose.ui:ui") + implementation("androidx.compose.ui:ui-graphics") + implementation("androidx.compose.ui:ui-tooling-preview") + implementation("androidx.compose.material3:material3") + implementation("androidx.compose.material:material-icons-extended") + implementation("androidx.constraintlayout:constraintlayout-compose:1.0.1") + + def room_version = "2.5.2" + implementation("androidx.room:room-runtime:$room_version") + implementation("androidx.room:room-ktx:$room_version") + ksp("androidx.room:room-compiler:$room_version") + + implementation("dev.olshevski.navigation:reimagined:1.4.0") + implementation("dev.olshevski.navigation:reimagined-material3:1.4.0") + implementation("com.airbnb.android:lottie-compose:6.0.1") + + debugImplementation("androidx.compose.ui:ui-tooling") + debugImplementation("androidx.compose.ui:ui-test-manifest") + + implementation("androidx.work:work-runtime-ktx:2.8.1") + implementation("androidx.health.connect:connect-client:1.1.0-alpha03") + + testImplementation("junit:junit:4.13.2") +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/app/schemas/at.florianschuster.hydro.service.DayDatabase/1.json b/app/schemas/at.florianschuster.hydro.service.DayDatabase/1.json new file mode 100644 index 0000000..64da49a --- /dev/null +++ b/app/schemas/at.florianschuster.hydro.service.DayDatabase/1.json @@ -0,0 +1,62 @@ +{ + "formatVersion": 1, + "database": { + "version": 1, + "identityHash": "19bad9520ed04230a868077de3e49ddd", + "entities": [ + { + "tableName": "day", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`date` INTEGER NOT NULL, `hydration` TEXT NOT NULL, `goalMilliliters` INTEGER NOT NULL, `id` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hydration", + "columnName": "hydration", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "goalMilliliters", + "columnName": "goalMilliliters", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_day_date", + "unique": true, + "columnNames": [ + "date" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_day_date` ON `${TABLE_NAME}` (`date`)" + } + ], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '19bad9520ed04230a868077de3e49ddd')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..f067524 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/at/florianschuster/hydro/App.kt b/app/src/main/java/at/florianschuster/hydro/App.kt new file mode 100644 index 0000000..db95a75 --- /dev/null +++ b/app/src/main/java/at/florianschuster/hydro/App.kt @@ -0,0 +1,74 @@ +package at.florianschuster.hydro + +import android.app.Application +import androidx.lifecycle.DefaultLifecycleObserver +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.ProcessLifecycleOwner +import androidx.lifecycle.lifecycleScope +import at.florianschuster.hydro.service.AndroidLocaleChangedService +import at.florianschuster.hydro.service.Channel +import at.florianschuster.hydro.service.HydrationHistoryStore +import at.florianschuster.hydro.service.NotificationService +import at.florianschuster.hydro.service.PreferencesStore +import at.florianschuster.hydro.service.ReminderAlarmService +import at.florianschuster.hydro.service.SqliteHydrationHistoryStore + +class App : Application() { + + lateinit var store: AppStore + lateinit var hydrationHistoryStore: HydrationHistoryStore + + override fun onCreate() { + super.onCreate() + + instance = this + + Channel.registerAll(this) + + val processLifecycleOwner = ProcessLifecycleOwner.get() + hydrationHistoryStore = SqliteHydrationHistoryStore( + context = applicationContext, + isDebug = isDebug + ) + val preferencesStore = PreferencesStore( + context = applicationContext + ) + val notificationService = NotificationService( + context = applicationContext + ) + val reminderAlarmService = ReminderAlarmService( + context = applicationContext, + preferencesStore = preferencesStore, + processLifecycleOwner.lifecycleScope + ) + val localeChangedService = AndroidLocaleChangedService( + context = applicationContext + ) + store = AppStore( + isDebug = isDebug, + scope = processLifecycleOwner.lifecycleScope, + preferencesStore = preferencesStore, + hydrationHistoryStore = hydrationHistoryStore, + reminderAlarmService = reminderAlarmService, + notificationService = notificationService, + localeChangedService = localeChangedService + ) + + processLifecycleOwner.lifecycle.addObserver( + object : DefaultLifecycleObserver { + override fun onResume(owner: LifecycleOwner) { + store.dispatch(AppAction.SetAppInForeground(true)) + } + + override fun onPause(owner: LifecycleOwner) { + store.dispatch(AppAction.SetAppInForeground(false)) + } + } + ) + } + + companion object { + private val isDebug = BuildConfig.DEBUG + lateinit var instance: App + } +} diff --git a/app/src/main/java/at/florianschuster/hydro/AppStore.kt b/app/src/main/java/at/florianschuster/hydro/AppStore.kt new file mode 100644 index 0000000..0389a50 --- /dev/null +++ b/app/src/main/java/at/florianschuster/hydro/AppStore.kt @@ -0,0 +1,264 @@ +package at.florianschuster.hydro + +import at.florianschuster.hydro.model.Cup +import at.florianschuster.hydro.model.Day +import at.florianschuster.hydro.model.LiquidUnit +import at.florianschuster.hydro.model.Milliliters +import at.florianschuster.hydro.model.Now +import at.florianschuster.hydro.model.NowInstant +import at.florianschuster.hydro.model.Percent +import at.florianschuster.hydro.model.Reminder +import at.florianschuster.hydro.model.Theme +import at.florianschuster.hydro.model.Today +import at.florianschuster.hydro.model.defaultCups +import at.florianschuster.hydro.model.defaultSelectedCups +import at.florianschuster.hydro.model.reachedGoal +import at.florianschuster.hydro.model.sumOfMilliliters +import at.florianschuster.hydro.model.times +import at.florianschuster.hydro.service.HydrationHistoryStore +import at.florianschuster.hydro.service.LocaleChangedService +import at.florianschuster.hydro.service.NotificationService +import at.florianschuster.hydro.service.PreferencesStore +import at.florianschuster.hydro.service.ReminderAlarmService +import at.florianschuster.hydro.service.hasCelebratedGoalToday +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import kotlinx.datetime.Instant + +data class AppState( + val isDebug: Boolean, + val dailyGoal: Milliliters, + val todayHydration: Milliliters, + val reminder: Reminder?, + val theme: Theme, + val canScheduleAlarms: Boolean, + val defaultCups: List, + val selectedCups: List, + val appInForeground: Boolean, + val liquidUnit: LiquidUnit, + val onboardingShown: Boolean +) { + val hydrationProgress: Percent = Percent( + todayHydration.value / dailyGoal.value.toFloat() + ) + val dailyGoalReached: Boolean = hydrationProgress.value >= 1f + val allCups = (defaultCups + selectedCups).distinct().sorted() +} + +sealed interface AppAction { + data class SetDailyGoal(val value: Milliliters) : AppAction + data class AddHydration(val value: Milliliters) : AppAction + data class SetReminder(val value: Reminder?) : AppAction + data object RestartReminder : AppAction + data class ShowHydrationReminderNotification(val forced: Boolean = false) : AppAction + data class SetTheme(val value: Theme) : AppAction + data class SetSelectedCups(val value: List) : AppAction + data class SetAppInForeground(val value: Boolean) : AppAction + data class SetLiquidUnit(val value: LiquidUnit) : AppAction + data object SetHydrationForOnboarding : AppAction + data object SetOnboardingShown : AppAction + data object DeleteAll : AppAction + data object ResetToday : AppAction +} + +class AppStore( + private val hydrationHistoryStore: HydrationHistoryStore, + isDebug: Boolean, + localeChangedService: LocaleChangedService, + private val notificationService: NotificationService, + private val preferencesStore: PreferencesStore, + private val reminderAlarmService: ReminderAlarmService, + private val scope: CoroutineScope +) { + private val _state = MutableStateFlow( + AppState( + isDebug = isDebug, + dailyGoal = runBlocking { + preferencesStore.dailyGoal.first() ?: Milliliters.DAILY_GOAL_DEFAULT + }, + todayHydration = runBlocking { + hydrationHistoryStore.day(Today).first()?.hydration?.sumOfMilliliters() + ?: Milliliters.ZERO + }, + reminder = runBlocking { preferencesStore.reminder.first() }, + theme = runBlocking { preferencesStore.theme.first() }, + canScheduleAlarms = reminderAlarmService.canScheduleAlarms.value, + defaultCups = defaultCups(), + selectedCups = runBlocking { + preferencesStore.selectedCups.first().sorted() + .ifEmpty { defaultSelectedCups() } + }, + appInForeground = true, + liquidUnit = runBlocking { preferencesStore.liquidUnit.first() }, + onboardingShown = runBlocking { preferencesStore.onboardingShown.first() } + ) + ) + val state: StateFlow = _state.asStateFlow() + + init { + preferencesStore.dailyGoal.onEach { milliliters -> + _state.update { it.copy(dailyGoal = milliliters ?: Milliliters.DAILY_GOAL_DEFAULT) } + }.launchIn(scope) + preferencesStore.reminder.onEach { reminder -> + _state.update { it.copy(reminder = reminder) } + }.launchIn(scope) + preferencesStore.theme.onEach { theme -> + _state.update { it.copy(theme = theme) } + }.launchIn(scope) + preferencesStore.selectedCups.onEach { selectedCups -> + _state.update { + it.copy( + selectedCups = selectedCups.sorted() + .ifEmpty { defaultSelectedCups() } + ) + } + }.launchIn(scope) + preferencesStore.liquidUnit.onEach { liquidUnit -> + _state.update { it.copy(liquidUnit = liquidUnit) } + }.launchIn(scope) + preferencesStore.onboardingShown.onEach { onboardingShown -> + _state.update { it.copy(onboardingShown = onboardingShown) } + }.launchIn(scope) + + hydrationHistoryStore.day(Today).onEach { day -> + val todayHydration = day?.hydration?.sumOfMilliliters() + ?: Milliliters.ZERO + _state.update { it.copy(todayHydration = todayHydration) } + }.launchIn(scope) + + reminderAlarmService.canScheduleAlarms.onEach { canScheduleAlarms -> + _state.update { it.copy(canScheduleAlarms = canScheduleAlarms) } + }.launchIn(scope) + + localeChangedService.onChanged.onEach { locale -> + _state.update { it.copy(defaultCups = defaultCups(locale)) } + }.launchIn(scope) + } + + fun dispatch(action: AppAction) { + when (action) { + is AppAction.SetDailyGoal -> scope.launch { + preferencesStore.setDailyGoal(action.value) + val day = hydrationHistoryStore.day(Today).first() + if (day != null) { + hydrationHistoryStore.setDay(day.copy(goal = action.value)) + } + } + + is AppAction.AddHydration -> scope.launch { + notificationService.cancelHydrationReminderNotification() + val currentState = _state.value + val storedDay = hydrationHistoryStore.day(Today).first() + if (storedDay != null) { + val updatedDay = storedDay.copy( + hydration = storedDay.hydration + Day.Hydration(action.value, Now), + goal = currentState.dailyGoal + ) + val goalReachedOnUpdatedDay = updatedDay.reachedGoal() + if (goalReachedOnUpdatedDay && !preferencesStore.hasCelebratedGoalToday()) { + if (!currentState.appInForeground) { + notificationService.showDailyGoalReachedNotification() + } + preferencesStore.setLastGoalCelebration(NowInstant) + } + hydrationHistoryStore.setDay(updatedDay) + } else { + hydrationHistoryStore.setDay( + Day( + date = Today, + hydration = listOf(Day.Hydration(action.value, Now)), + goal = currentState.dailyGoal + ) + ) + } + } + + is AppAction.SetReminder -> scope.launch { + if (action.value != null) { + reminderAlarmService.setAlarm(action.value) + } else { + reminderAlarmService.clear() + } + preferencesStore.setReminder(action.value) + } + + is AppAction.RestartReminder -> scope.launch { + val reminder = preferencesStore.reminder.first() + if (reminder != null) { + reminderAlarmService.setAlarm(reminder) + } + } + + is AppAction.ShowHydrationReminderNotification -> scope.launch { + if (action.forced || !preferencesStore.hasCelebratedGoalToday()) { + val todayMilliliters = hydrationHistoryStore.day(Today).first() + ?.hydration + ?.sumOfMilliliters() + ?: Milliliters.ZERO + val selectedCups = preferencesStore.selectedCups.first() + .ifEmpty { defaultSelectedCups() } + .sorted() + notificationService.showHydrationReminderNotification( + todayMilliliters = todayMilliliters, + todayProgress = _state.value.hydrationProgress, + selectedCups = selectedCups, + liquidUnit = preferencesStore.liquidUnit.first() + ) + } + } + + is AppAction.SetTheme -> scope.launch { + preferencesStore.setTheme(action.value) + } + + is AppAction.SetSelectedCups -> scope.launch { + preferencesStore.setSelectedCups(action.value) + } + + is AppAction.DeleteAll -> scope.launch { + preferencesStore.clear() + hydrationHistoryStore.clear() + reminderAlarmService.clear() + notificationService.clear() + } + + is AppAction.SetAppInForeground -> _state.update { + it.copy(appInForeground = action.value) + } + + is AppAction.ResetToday -> scope.launch { + val today = hydrationHistoryStore.day(Today).first() ?: return@launch + hydrationHistoryStore.setDay(today.copy(hydration = emptyList())) + preferencesStore.setLastGoalCelebration(Instant.DISTANT_PAST) + } + + is AppAction.SetLiquidUnit -> scope.launch { + preferencesStore.setLiquidUnit(action.value) + } + + is AppAction.SetHydrationForOnboarding -> scope.launch { + val goal = _state.value.dailyGoal + hydrationHistoryStore.setDay( + Day( + date = Today, + hydration = listOf(Day.Hydration(goal * 0.55f, Now)), + goal = goal + ) + ) + } + + is AppAction.SetOnboardingShown -> scope.launch { + preferencesStore.setOnboardingShown(true) + dispatch(AppAction.ResetToday) + } + } + } +} diff --git a/app/src/main/java/at/florianschuster/hydro/DeveloperInfo.kt b/app/src/main/java/at/florianschuster/hydro/DeveloperInfo.kt new file mode 100644 index 0000000..b555d9b --- /dev/null +++ b/app/src/main/java/at/florianschuster/hydro/DeveloperInfo.kt @@ -0,0 +1,41 @@ +package at.florianschuster.hydro + +import android.app.Activity +import android.content.ActivityNotFoundException +import android.content.Intent +import android.net.Uri +import android.widget.Toast +import androidx.browser.customtabs.CustomTabsIntent + +private const val DEV_URL = "https://florianschuster.at/" +private const val DEV_EMAIL = "hydro.mobileapp@gmail.com" + +fun Activity.openInfoCustomTab() { + val customTab = CustomTabsIntent.Builder().apply { + setShowTitle(true) + setInstantAppsEnabled(true) + }.build() + runCatching { + customTab.launchUrl(this, Uri.parse(DEV_URL)) + }.onFailure { + val intent = Intent(Intent.ACTION_VIEW, Uri.parse(DEV_URL)) + startActivity(intent) + } +} + +fun Activity.openFeedback() { + try { + startActivity( + Intent(Intent.ACTION_SEND).apply { + data = Uri.parse("mailto:$DEV_EMAIL") + putExtra(Intent.EXTRA_SUBJECT, "Feedback hydro app") + } + ) + } catch (ex: ActivityNotFoundException) { + Toast.makeText( + this, + "No email client installed.", + Toast.LENGTH_SHORT + ).show() + } +} diff --git a/app/src/main/java/at/florianschuster/hydro/MainActivity.kt b/app/src/main/java/at/florianschuster/hydro/MainActivity.kt new file mode 100644 index 0000000..476d122 --- /dev/null +++ b/app/src/main/java/at/florianschuster/hydro/MainActivity.kt @@ -0,0 +1,204 @@ +package at.florianschuster.hydro + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.BackHandler +import androidx.activity.compose.setContent +import androidx.compose.animation.ExperimentalAnimationApi +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutVertically +import androidx.compose.animation.with +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Surface +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.core.view.WindowCompat +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import at.florianschuster.hydro.model.Theme +import at.florianschuster.hydro.model.isDarkTheme +import at.florianschuster.hydro.ui.GoalOfTheDayBottomSheet +import at.florianschuster.hydro.ui.HistoryScreen +import at.florianschuster.hydro.ui.HistoryScreenToolbar +import at.florianschuster.hydro.ui.OnboardingScreen +import at.florianschuster.hydro.ui.SetIntervalBottomSheet +import at.florianschuster.hydro.ui.SetLiquidUnitBottomSheet +import at.florianschuster.hydro.ui.SettingsScreen +import at.florianschuster.hydro.ui.SettingsToolbar +import at.florianschuster.hydro.ui.ThemeBottomSheet +import at.florianschuster.hydro.ui.TodayScreen +import at.florianschuster.hydro.ui.TodayToolbar +import at.florianschuster.hydro.ui.base.HydrationBackground +import at.florianschuster.hydro.ui.base.HydroTheme +import dev.olshevski.navigation.reimagined.AnimatedNavHost +import dev.olshevski.navigation.reimagined.NavAction +import dev.olshevski.navigation.reimagined.NavBackHandler +import dev.olshevski.navigation.reimagined.material3.BottomSheetNavHost +import dev.olshevski.navigation.reimagined.navigate +import dev.olshevski.navigation.reimagined.pop +import dev.olshevski.navigation.reimagined.rememberNavController + +class MainActivity : ComponentActivity() { + + @OptIn(ExperimentalAnimationApi::class) + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + WindowCompat.setDecorFitsSystemWindows(window, false) + + val store = App.instance.store + + setContent { + val state by store.state.collectAsStateWithLifecycle() + + HydroTheme( + darkTheme = state.theme.isDarkTheme(), + dynamicColor = state.theme == Theme.Dynamic + ) { + Surface( + modifier = Modifier.fillMaxSize() + ) { + HydrationBackground( + isSystemInDarkTheme = state.theme.isDarkTheme(), + hydrationProgress = state.hydrationProgress + ) + + val navController = rememberNavController( + startDestination = if (state.onboardingShown) { + Screen.Today + } else { + Screen.Onboarding + } + ) + val sheetController = rememberNavController( + initialBackstack = emptyList() + ) + + NavBackHandler(navController) + AnimatedNavHost( + controller = navController, + transitionSpec = { action, _, _ -> + when (action) { + is NavAction.Pop -> { + fadeIn() with slideOutVertically( + targetOffsetY = { fullHeight -> fullHeight } + ) + fadeOut() + } + + is NavAction.Navigate -> { + slideInVertically( + initialOffsetY = { fullHeight -> fullHeight } + ) + fadeIn() with fadeOut() + } + + else -> error("no transition defined for $action") + } + } + ) { screen -> + Scaffold( + topBar = { + when (screen) { + is Screen.Onboarding -> { + // no toolbar + } + + is Screen.Today -> TodayToolbar( + isDebug = state.isDebug, + onGoToSettings = { + navController.navigate(Screen.Settings) + }, + onGoToHistory = { + navController.navigate(Screen.History) + } + ) + + is Screen.Settings -> SettingsToolbar( + onGoBack = navController::pop + ) + + is Screen.History -> HistoryScreenToolbar( + onGoBack = navController::pop + ) + } + }, + containerColor = Color.Transparent + ) { contentPadding -> + when (screen) { + is Screen.Onboarding -> OnboardingScreen( + contentPadding = contentPadding, + dispatch = store::dispatch, + onOnboardingFinished = { + navController.navigate( + listOf(Screen.Today, Screen.Settings) + ) + } + ) + + is Screen.Today -> TodayScreen( + contentPadding = contentPadding, + state = state, + dispatch = store::dispatch + ) + + is Screen.Settings -> SettingsScreen( + contentPadding = contentPadding, + state = state, + dispatch = store::dispatch, + onSetGoalOfTheDay = { sheetController.navigate(Sheet.GoalOfTheDay) }, + onSetLiquidUnit = { sheetController.navigate(Sheet.LiquidUnit) }, + onSetTheme = { sheetController.navigate(Sheet.SetTheme) }, + onShowDeveloperInfo = { openInfoCustomTab() }, + onWriteDeveloper = { openFeedback() }, + onSetInterval = { sheetController.navigate(Sheet.SetInterval) } + ) + + is Screen.History -> HistoryScreen( + contentPadding = contentPadding, + appState = state + ) + } + } + } + + BackHandler( + enabled = sheetController.backstack.entries.isNotEmpty(), + onBack = sheetController::pop + ) + BottomSheetNavHost( + controller = sheetController, + onDismissRequest = sheetController::pop, + scrimColor = Color.Black.copy(alpha = 0.75f) + ) { sheet -> + when (sheet) { + is Sheet.GoalOfTheDay -> GoalOfTheDayBottomSheet( + state = state, + dispatch = store::dispatch + ) + + is Sheet.SetTheme -> ThemeBottomSheet( + state = state, + dispatch = store::dispatch, + onClose = sheetController::pop + ) + + is Sheet.SetInterval -> SetIntervalBottomSheet( + state = state, + dispatch = store::dispatch, + onClose = sheetController::pop + ) + + is Sheet.LiquidUnit -> SetLiquidUnitBottomSheet( + state = state, + dispatch = store::dispatch, + onClose = sheetController::pop + ) + } + } + } + } + } + } +} diff --git a/app/src/main/java/at/florianschuster/hydro/Screen.kt b/app/src/main/java/at/florianschuster/hydro/Screen.kt new file mode 100644 index 0000000..24ba1af --- /dev/null +++ b/app/src/main/java/at/florianschuster/hydro/Screen.kt @@ -0,0 +1,33 @@ +package at.florianschuster.hydro + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +sealed interface Screen : Parcelable { + @Parcelize + data object Onboarding : Screen + + @Parcelize + data object Today : Screen + + @Parcelize + data object Settings : Screen + + @Parcelize + data object History : Screen +} + +sealed interface Sheet : Parcelable { + + @Parcelize + data object GoalOfTheDay : Sheet + + @Parcelize + data object LiquidUnit : Sheet + + @Parcelize + data object SetTheme : Sheet + + @Parcelize + data object SetInterval : Sheet +} diff --git a/app/src/main/java/at/florianschuster/hydro/model/Cup.kt b/app/src/main/java/at/florianschuster/hydro/model/Cup.kt new file mode 100644 index 0000000..615203c --- /dev/null +++ b/app/src/main/java/at/florianschuster/hydro/model/Cup.kt @@ -0,0 +1,43 @@ +package at.florianschuster.hydro.model + +import kotlinx.serialization.Serializable +import java.util.Locale + +@Serializable +data class Cup( + val milliliters: Milliliters +) : Comparable { + override fun compareTo(other: Cup): Int = milliliters compareTo other.milliliters +} + +fun defaultCups( + locale: Locale = Locale.getDefault() +): List = defaultSelectedCups(locale) + when (locale) { + Locale.US, + Locale.UK -> listOf( + Cup(Milliliters(355)), // 12 oz + Cup(Milliliters(591)), // 20 oz + Cup(Milliliters(946)), // 32 oz + Cup(Milliliters(1183)) // 40 oz + ) + + else -> listOf( + Cup(Milliliters(330)), + Cup(Milliliters(500)), + Cup(Milliliters(1_000)), + Cup(Milliliters(2_000)) + ) +}.sorted() + +fun defaultSelectedCups( + locale: Locale = Locale.getDefault() +) = when (locale) { + Locale.US, + Locale.UK -> listOf( + Cup(Milliliters(236)) // 8 oz + ) + + else -> listOf( + Cup(Milliliters(250)) + ) +} diff --git a/app/src/main/java/at/florianschuster/hydro/model/DateTime.kt b/app/src/main/java/at/florianschuster/hydro/model/DateTime.kt new file mode 100644 index 0000000..8b62352 --- /dev/null +++ b/app/src/main/java/at/florianschuster/hydro/model/DateTime.kt @@ -0,0 +1,143 @@ +package at.florianschuster.hydro.model + +import android.icu.text.MeasureFormat +import android.icu.util.MeasureUnit +import kotlinx.datetime.Clock +import kotlinx.datetime.Instant +import kotlinx.datetime.LocalDate +import kotlinx.datetime.LocalDateTime +import kotlinx.datetime.LocalTime +import kotlinx.datetime.TimeZone +import kotlinx.datetime.toJavaLocalDate +import kotlinx.datetime.toJavaLocalTime +import kotlinx.datetime.toLocalDateTime +import java.text.NumberFormat +import java.time.format.DateTimeFormatter +import java.util.Locale +import kotlin.time.Duration +import kotlin.time.Duration.Companion.days +import kotlin.time.Duration.Companion.hours +import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.Duration.Companion.minutes +import kotlin.time.Duration.Companion.seconds + +val UserTimeZone: TimeZone + get() = TimeZone.currentSystemDefault() + +val NowInstant: Instant + get() = Clock.System.now() + +val TodayNow: LocalDateTime + get() = NowInstant.toLocalDateTime(UserTimeZone) + +val Today: LocalDate + get() = TodayNow.date + +val Now: LocalTime + get() = TodayNow.time + +operator fun LocalTime.plus( + duration: Duration +): LocalTime { + return LocalTime.fromMillisecondOfDay( + toMillisecondOfDay() + duration.inWholeMilliseconds.toInt() + ) +} + +private val usDateFormatter = DateTimeFormatter.ofPattern("MM/dd/yyyy") +private val euDateFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy") + +fun LocalDate.format( + locale: Locale = Locale.getDefault() +): String = when (locale) { + Locale.US, + Locale.UK -> toJavaLocalDate().format(usDateFormatter) + + else -> toJavaLocalDate().format(euDateFormatter) +} + +private val usTimeFormatter = DateTimeFormatter.ofPattern("hh:mm a") +private val euTimeFormatter = DateTimeFormatter.ofPattern("HH:mm") +fun LocalTime.format( + locale: Locale = Locale.getDefault() +): String = when (locale) { + Locale.US, + Locale.UK -> toJavaLocalTime().format(usTimeFormatter) + + else -> toJavaLocalTime().format(euTimeFormatter) +} + +fun Duration.format( + locale: Locale = Locale.getDefault() +): String { + return DurationFormat(locale).format(this, DurationFormat.DurationUnit.MINUTE) +} + +// https://gist.github.com/Jeehut/78534c27b24d78f14a3cbd3eebead861 +class DurationFormat( + private val locale: Locale = Locale.getDefault() +) { + + enum class DurationUnit { + DAY, HOUR, MINUTE, SECOND, MILLISECOND + } + + fun format( + duration: Duration, + smallestUnit: DurationUnit = DurationUnit.SECOND + ): String { + val formattedStringComponents = mutableListOf() + var remainder = duration + + for (unit in DurationUnit.entries) { + val component = calculateComponent(unit, remainder) + + remainder = when (unit) { + DurationUnit.DAY -> remainder - component.days + DurationUnit.HOUR -> remainder - component.hours + DurationUnit.MINUTE -> remainder - component.minutes + DurationUnit.SECOND -> remainder - component.seconds + DurationUnit.MILLISECOND -> remainder - component.milliseconds + } + + val unitDisplayName = unitDisplayName(unit) + + if (component > 0) { + val formattedComponent = NumberFormat.getInstance(locale).format(component) + formattedStringComponents.add("$formattedComponent$unitDisplayName") + } + + if (unit == smallestUnit) { + val formattedZero = NumberFormat.getInstance(locale).format(0) + if (formattedStringComponents.isEmpty()) { + formattedStringComponents.add("$formattedZero$unitDisplayName") + } + break + } + } + + return formattedStringComponents.joinToString(" ") + } + + private fun calculateComponent(unit: DurationUnit, remainder: Duration) = when (unit) { + DurationUnit.DAY -> remainder.inWholeDays + DurationUnit.HOUR -> remainder.inWholeHours + DurationUnit.MINUTE -> remainder.inWholeMinutes + DurationUnit.SECOND -> remainder.inWholeSeconds + DurationUnit.MILLISECOND -> remainder.inWholeMilliseconds + } + + private fun unitDisplayName(unit: DurationUnit): String { + val measureFormat = MeasureFormat.getInstance( + locale, + MeasureFormat.FormatWidth.NARROW + ) + return when (unit) { + DurationUnit.DAY -> measureFormat.getUnitDisplayName(MeasureUnit.DAY) + DurationUnit.HOUR -> measureFormat.getUnitDisplayName(MeasureUnit.HOUR) + DurationUnit.MINUTE -> measureFormat.getUnitDisplayName(MeasureUnit.MINUTE) + DurationUnit.SECOND -> measureFormat.getUnitDisplayName(MeasureUnit.SECOND) + DurationUnit.MILLISECOND -> measureFormat.getUnitDisplayName(MeasureUnit.MILLISECOND) + } + } +} diff --git a/app/src/main/java/at/florianschuster/hydro/model/Day.kt b/app/src/main/java/at/florianschuster/hydro/model/Day.kt new file mode 100644 index 0000000..081480a --- /dev/null +++ b/app/src/main/java/at/florianschuster/hydro/model/Day.kt @@ -0,0 +1,31 @@ +package at.florianschuster.hydro.model + +import kotlinx.datetime.LocalDate +import kotlinx.datetime.LocalTime +import kotlinx.serialization.Serializable +import java.util.UUID + +data class Day( + val date: LocalDate, + val hydration: List, + val goal: Milliliters, + val id: String = UUID.randomUUID().toString() +) : Comparable { + + @Serializable + data class Hydration( + val milliliters: Milliliters, + val time: LocalTime, + val id: String = UUID.randomUUID().toString() + ) : Comparable { + override fun compareTo(other: Hydration): Int = time compareTo other.time + } + + override fun compareTo(other: Day): Int = date compareTo other.date +} + +fun List.sumOfMilliliters(): Milliliters { + return Milliliters(sumOf { it.milliliters.value }) +} + +fun Day.reachedGoal(): Boolean = hydration.sumOfMilliliters() >= goal diff --git a/app/src/main/java/at/florianschuster/hydro/model/LiquidUnit.kt b/app/src/main/java/at/florianschuster/hydro/model/LiquidUnit.kt new file mode 100644 index 0000000..2631cac --- /dev/null +++ b/app/src/main/java/at/florianschuster/hydro/model/LiquidUnit.kt @@ -0,0 +1,53 @@ +package at.florianschuster.hydro.model + +enum class LiquidUnit( + val baseUnitMultiplier: Double, + val serialized: String +) { + Milliliter( + baseUnitMultiplier = 1.0, + serialized = "ml" + ), + USFluidOunce( + baseUnitMultiplier = 0.033814, // 1 milliliter = 0.033814 oz + serialized = "oz_us" + ), + UKFluidOunce( + baseUnitMultiplier = 0.035195, // 1 milliliter = 0.035195 oz + serialized = "oz_uk" + ); + + fun convertValue( + milliliters: Milliliters + ): Double = milliliters.value * baseUnitMultiplier + + companion object { + fun of(serialized: String?): LiquidUnit = if (serialized == null) { + Milliliter + } else { + entries.firstOrNull { it.serialized == serialized } ?: Milliliter + } + } +} + +fun LiquidUnit.format() = when (this) { + LiquidUnit.Milliliter -> "Milliliters (ml)" + LiquidUnit.USFluidOunce -> "US Ounces (fl oz US)" + LiquidUnit.UKFluidOunce -> "UK Ounces (fl oz UK)" +} + +fun LiquidUnit.formatMoreInfo() = when (this) { + LiquidUnit.Milliliter -> + "Milliliters are a standard metric unit of liquid volume. " + + "They provide a precise way to measure fluids, making them ideal for accurate " + + "hydration monitoring." + + LiquidUnit.USFluidOunce -> + "US ounces serve as a customary unit for liquid volume " + + "predominantly in the United States." + + LiquidUnit.UKFluidOunce -> + "UK ounces, also referred to as Imperial ounces, are a " + + "volume measurement commonly utilized in the United Kingdom and certain other " + + "regions." +} diff --git a/app/src/main/java/at/florianschuster/hydro/model/Milliliters.kt b/app/src/main/java/at/florianschuster/hydro/model/Milliliters.kt new file mode 100644 index 0000000..1d8a86e --- /dev/null +++ b/app/src/main/java/at/florianschuster/hydro/model/Milliliters.kt @@ -0,0 +1,55 @@ +package at.florianschuster.hydro.model + +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.res.painterResource +import at.florianschuster.hydro.R +import kotlinx.serialization.Serializable +import kotlin.math.roundToInt + +@Serializable +@JvmInline +value class Milliliters(val value: Int) : Comparable { + override fun compareTo(other: Milliliters): Int = value compareTo other.value + + companion object { + val ZERO: Milliliters = Milliliters(0) + + val DAILY_GOAL_DEFAULT: Milliliters = Milliliters(2_000) + val DAILY_GOAL_MIN: Milliliters = Milliliters(500) + val DAILY_GOAL_MAX: Milliliters = Milliliters(5_000) + val DAILY_GOAL_STEPS: Milliliters = Milliliters(100) + } +} + +operator fun Milliliters?.plus( + other: Milliliters +): Milliliters = Milliliters((this?.value ?: 0) + other.value) + +operator fun Milliliters.minus( + other: Milliliters +): Milliliters = Milliliters(maxOf(0, value - other.value)) + +operator fun Milliliters.times( + other: Float +): Milliliters = Milliliters(maxOf(0f, value * other).roundToInt()) + +fun Milliliters.format( + unit: LiquidUnit +): String { + val actualValue = unit.convertValue(this).roundToInt() + return when (unit) { + LiquidUnit.Milliliter -> "$actualValue ml" + LiquidUnit.USFluidOunce -> "$actualValue oz." + LiquidUnit.UKFluidOunce -> "$actualValue oz." + } +} + +@Composable +fun Milliliters.icon(): Painter = when (value) { + in 0..250 -> painterResource(R.drawable.ic_water_less) + in 251..330 -> painterResource(R.drawable.ic_water_medium) + in 331..750 -> painterResource(R.drawable.ic_water_full) + in 751..1_000 -> painterResource(R.drawable.ic_water_bottle) + else -> painterResource(R.drawable.ic_water_bottle_large) +} diff --git a/app/src/main/java/at/florianschuster/hydro/model/Percent.kt b/app/src/main/java/at/florianschuster/hydro/model/Percent.kt new file mode 100644 index 0000000..1db0320 --- /dev/null +++ b/app/src/main/java/at/florianschuster/hydro/model/Percent.kt @@ -0,0 +1,16 @@ +package at.florianschuster.hydro.model + +import androidx.annotation.FloatRange +import kotlin.math.roundToInt + +@JvmInline +value class Percent( + // 0.0f = 0%, 1.0f = 100% + @FloatRange(from = 0.0, to = Double.MAX_VALUE) val value: Float +) { + init { + require(value >= 0f) + } +} + +fun Percent.format(): String = "${(value * 100).roundToInt()}%" diff --git a/app/src/main/java/at/florianschuster/hydro/model/Reminder.kt b/app/src/main/java/at/florianschuster/hydro/model/Reminder.kt new file mode 100644 index 0000000..496cfcc --- /dev/null +++ b/app/src/main/java/at/florianschuster/hydro/model/Reminder.kt @@ -0,0 +1,34 @@ +package at.florianschuster.hydro.model + +import kotlinx.datetime.LocalTime +import kotlinx.serialization.Serializable +import kotlin.time.Duration +import kotlin.time.Duration.Companion.minutes + +@Serializable +data class Reminder( + val start: LocalTime, + val end: LocalTime, + val interval: Duration +) { + init { + require(start < end) { "$start cannot be before $end" } + } + + companion object { + val DEFAULT = Reminder( + start = LocalTime(9, 0, 0, 0), + end = LocalTime(18, 0, 0, 0), + interval = 30.minutes + ) + } +} + +fun Reminder.calculateReminderTimes(): List = buildList { + var current = start + while (current <= end) { + add(current) + current += interval + } + if (!contains(end)) add(end) +} diff --git a/app/src/main/java/at/florianschuster/hydro/model/Theme.kt b/app/src/main/java/at/florianschuster/hydro/model/Theme.kt new file mode 100644 index 0000000..3a4f785 --- /dev/null +++ b/app/src/main/java/at/florianschuster/hydro/model/Theme.kt @@ -0,0 +1,56 @@ +package at.florianschuster.hydro.model + +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.ColorLens +import androidx.compose.material.icons.outlined.DarkMode +import androidx.compose.material.icons.outlined.InvertColors +import androidx.compose.material.icons.outlined.LightMode +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.vector.ImageVector + +enum class Theme( + val serialized: String +) { + System("system"), + Dynamic("dynamic"), + Dark("dark"), + Light("light"); + + companion object { + fun of(serialized: String?) = if (serialized == null) { + System + } else { + entries.firstOrNull { it.serialized == serialized } ?: System + } + } +} + +@Composable +fun Theme.isDarkTheme(): Boolean { + return when (this) { + Theme.Dark -> true + Theme.Light -> false + Theme.System, + Theme.Dynamic -> isSystemInDarkTheme() + } +} + +fun Theme.format(): String { + return when (this) { + Theme.Dark -> "Dark" + Theme.Light -> "Light" + Theme.System -> "System" + Theme.Dynamic -> "Dynamic" + } +} + +@Composable +fun Theme.icon(): ImageVector { + return when (this) { + Theme.Dark -> Icons.Outlined.DarkMode + Theme.Light -> Icons.Outlined.LightMode + Theme.System -> Icons.Outlined.InvertColors + Theme.Dynamic -> Icons.Outlined.ColorLens + } +} diff --git a/app/src/main/java/at/florianschuster/hydro/service/HydrationHistoryStore.kt b/app/src/main/java/at/florianschuster/hydro/service/HydrationHistoryStore.kt new file mode 100644 index 0000000..5b01ef8 --- /dev/null +++ b/app/src/main/java/at/florianschuster/hydro/service/HydrationHistoryStore.kt @@ -0,0 +1,195 @@ +package at.florianschuster.hydro.service + +import android.content.Context +import androidx.lifecycle.ProcessLifecycleOwner +import androidx.lifecycle.lifecycleScope +import androidx.room.Dao +import androidx.room.Database +import androidx.room.Delete +import androidx.room.Entity +import androidx.room.Index +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.PrimaryKey +import androidx.room.Query +import androidx.room.Room +import androidx.room.RoomDatabase +import androidx.room.TypeConverter +import androidx.room.TypeConverters +import at.florianschuster.hydro.model.Day +import at.florianschuster.hydro.model.Milliliters +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import kotlinx.datetime.DatePeriod +import kotlinx.datetime.LocalDate +import kotlinx.datetime.LocalTime +import kotlinx.datetime.minus +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json + +interface HydrationHistoryStore { + suspend fun setDay(day: Day) + fun day(date: LocalDate): Flow + suspend fun days( + startDateExclusive: LocalDate, + pageSize: Int = 20 + ): List + + suspend fun delete(date: LocalDate) + suspend fun clear() +} + +class SqliteHydrationHistoryStore( + context: Context, + isDebug: Boolean, + private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO +) : HydrationHistoryStore { + + private val database: DayDatabase = Room.databaseBuilder( + context = context.applicationContext, + klass = DayDatabase::class.java, + name = databaseName + ).apply { + if (isDebug) fallbackToDestructiveMigration() + }.build() + + init { + // insert test data + if (isDebug) { + ProcessLifecycleOwner.get().lifecycleScope.launch(ioDispatcher) { + if (day(debugStartDate).firstOrNull() == null) { + generateTestHydrationData().forEach { setDay(it) } + } + } + } + } + + override suspend fun setDay(day: Day) { + database.dayDao().upsert(day.toDTO()) + } + + override fun day(date: LocalDate): Flow { + return database.dayDao().get(date).map { it?.fromDTO() } + } + + override suspend fun days( + startDateExclusive: LocalDate, + pageSize: Int + ): List = withContext(ioDispatcher) { + database.dayDao().getDescending(startDateExclusive, pageSize).map(DayDTO::fromDTO) + } + + override suspend fun delete(date: LocalDate) = withContext(ioDispatcher) { + val day = database.dayDao().get(date).firstOrNull() ?: return@withContext + database.dayDao().delete(day) + } + + override suspend fun clear() = withContext(ioDispatcher) { + database.clearAllTables() + } + + companion object { + private const val databaseName = "day-database" + } +} + +@Entity( + tableName = "day", + indices = [Index("date", unique = true)] +) +data class DayDTO( + val date: LocalDate, + val hydration: List, + val goalMilliliters: Int, + @PrimaryKey val id: String +) : Comparable { + override fun compareTo(other: DayDTO): Int = date compareTo other.date +} + +private fun DayDTO.fromDTO(): Day = Day( + date = date, + hydration = hydration, + goal = Milliliters(goalMilliliters), + id = id +) + +private fun Day.toDTO(): DayDTO = DayDTO( + date = date, + hydration = hydration, + goalMilliliters = goal.value, + id = id +) + +@Dao +interface DayDao { + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun upsert(vararg days: DayDTO) + + @Query("SELECT * FROM day WHERE date = :date LIMIT 1") + fun get(date: LocalDate): Flow + + @Query( + "SELECT * FROM day WHERE date < :startDateExclusive ORDER BY date desc LIMIT :limit " + ) + fun getDescending(startDateExclusive: LocalDate, limit: Int): List + + @Delete + suspend fun delete(day: DayDTO) +} + +@Database(entities = [DayDTO::class], version = 1) +@TypeConverters(LocalDateConverter::class, DayHydrationConverter::class) +abstract class DayDatabase : RoomDatabase() { + abstract fun dayDao(): DayDao +} + +class LocalDateConverter { + @TypeConverter + fun fromLocalDate(date: LocalDate): Int = date.toEpochDays() + + @TypeConverter + fun toLocalDate(epochDays: Int): LocalDate = LocalDate.fromEpochDays(epochDays) +} + +class DayHydrationConverter { + @TypeConverter + fun fromDayHydration(dayHydration: List): String { + return json.encodeToString(dayHydration) + } + + @TypeConverter + fun toDayHydration(serialized: String): List { + return json.decodeFromString(serialized) + } + + companion object { + val json = Json + } +} + +private val debugStartDate = LocalDate.fromEpochDays(19000) + +private fun generateTestHydrationData(): List = buildList { + for (i in 1..100) { + val date = debugStartDate.minus(DatePeriod(days = i)) + val hydrationList = generateTestHydrationList() + val goal = Milliliters((1500..2500).random()) + add(Day(date, hydrationList, goal)) + } +} + +private fun generateTestHydrationList(): List = buildList { + // Random number of hydration instances (1 to 5) + val numHydrations = (1..5).random() + for (i in 0 until numHydrations) { + val milliliters = Milliliters((100..500).random()) + val time = LocalTime((0..23).random(), (0..59).random()) + add(Day.Hydration(milliliters, time)) + } +} diff --git a/app/src/main/java/at/florianschuster/hydro/service/LocaleChangedService.kt b/app/src/main/java/at/florianschuster/hydro/service/LocaleChangedService.kt new file mode 100644 index 0000000..7514585 --- /dev/null +++ b/app/src/main/java/at/florianschuster/hydro/service/LocaleChangedService.kt @@ -0,0 +1,40 @@ +package at.florianschuster.hydro.service + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import androidx.core.content.ContextCompat +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.callbackFlow +import java.util.Locale + +interface LocaleChangedService { + val onChanged: Flow +} + +class AndroidLocaleChangedService( + context: Context +) : LocaleChangedService { + + override val onChanged: Flow = callbackFlow { + trySend(context.getMainLocale()) + val receiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + if (intent.action == Intent.ACTION_LOCALE_CHANGED) { + trySend(context.getMainLocale()) + } + } + } + ContextCompat.registerReceiver( + context, + receiver, + IntentFilter(Intent.ACTION_LOCALE_CHANGED), + ContextCompat.RECEIVER_NOT_EXPORTED + ) + awaitClose { context.unregisterReceiver(receiver) } + } + + private fun Context.getMainLocale(): Locale = resources.configuration.locales.get(0) +} diff --git a/app/src/main/java/at/florianschuster/hydro/service/NotificationService.kt b/app/src/main/java/at/florianschuster/hydro/service/NotificationService.kt new file mode 100644 index 0000000..c59bffc --- /dev/null +++ b/app/src/main/java/at/florianschuster/hydro/service/NotificationService.kt @@ -0,0 +1,184 @@ +package at.florianschuster.hydro.service + +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.PendingIntent +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import androidx.annotation.IntRange +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.toArgb +import androidx.core.content.getSystemService +import at.florianschuster.hydro.App +import at.florianschuster.hydro.AppAction +import at.florianschuster.hydro.MainActivity +import at.florianschuster.hydro.R +import at.florianschuster.hydro.model.Cup +import at.florianschuster.hydro.model.LiquidUnit +import at.florianschuster.hydro.model.Milliliters +import at.florianschuster.hydro.model.Percent +import at.florianschuster.hydro.model.format + +class NotificationService( + private val context: Context +) { + + private val notificationManager: NotificationManager = checkNotNull(context.getSystemService()) + + private val openAppPendingIntent = PendingIntent.getActivity( + context, + 1, + Intent(context, MainActivity::class.java), + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE + ) + + fun showHydrationReminderNotification( + todayMilliliters: Milliliters, + todayProgress: Percent, + selectedCups: List, + liquidUnit: LiquidUnit + ) { + notificationManager.notify( + hydrationReminderId, + Notification.Builder( + context, + Channel.Hydrate.id + ).apply { + setSmallIcon(R.drawable.ic_notification) + setContentTitle("Hydration Reminder") + setContentText(todayMilliliters.asReminderMessage()) + setSubText("${todayMilliliters.format(liquidUnit)} (${todayProgress.format()})") + setColor(NOTIFICATION_COLOR) + setContentIntent(openAppPendingIntent) + selectedCups.forEach { cup -> + addAction( + Notification.Action.Builder( + null, + cup.milliliters.format(liquidUnit), + addCupPendingIntent(cup.milliliters) + ).build() + ) + } + setShowWhen(true) + setAutoCancel(true) + }.build() + ) + } + + private fun Milliliters.asReminderMessage(): String = when (value) { + in 0..199 -> "Time to Hydrate! Take a Sip of Water and Stay Refreshed." + in 200..299 -> "Stay Hydrated! Your Body Needs Water. Take a Break and Drink Up!" + in 300..399 -> "Hydration Alert! Grab a Glass of Water and Rehydrate." + in 400..499 -> "Don't Forget to Drink Water! Your Body Thanks You." + in 500..599 -> "Quench Your Thirst! It's Hydration O'Clock." + in 600..699 -> "Stay Healthy and Hydrated! Time for a Water Break." + in 700..799 -> "Water Time! Hydrate Yourself for Optimal Wellness." + in 800..899 -> "Hydration Check: Have You Had Your Glass of Water Yet?" + in 900..999 -> "A Little H2O Never Hurt! Stay Hydrated for a Productive Day." + in 1000..1099 -> "Refill Your Cup! Hydration Is the Key to Feeling Great." + in 1100..1199 -> "Stay Hydrated! Another Glass of Water Brings You Closer to Wellness." + in 1200..1299 -> "Hydration Alert! Keep Sipping Water for a Healthy You." + in 1300..1399 -> "Don't Forget to Stay Hydrated! Your Body Loves Water." + in 1400..1499 -> "Quench Your Thirst! It's Time for More Hydration." + in 1500..1599 -> "Stay Healthy and Hydrated! Keep Up the Water Intake." + in 1600..1699 -> "Water Time! Hydrate to Energize Your Body." + in 1700..1799 -> "Hydration Check: Keep the Water Coming for a Productive Day." + in 1800..1899 -> "Stay Hydrated! Your Body Will Thank You." + in 1900..1999 -> "A Little H2O Never Hurt! Keep Hydrating for Optimal Wellness." + else -> "Keep Hydrating! Your Body Will Thank You." + } + + private fun addCupPendingIntent( + milliliters: Milliliters + ) = PendingIntent.getBroadcast( + context, + milliliters.value, + Intent(context, AddCupFromNotification::class.java).apply { + putExtra(AddCupFromNotification.EXTRA_MILLILITERS_VALUE, milliliters.value) + }, + PendingIntent.FLAG_IMMUTABLE + ) + + fun cancelHydrationReminderNotification() { + notificationManager.cancel(hydrationReminderId) + } + + fun showDailyGoalReachedNotification() { + notificationManager.notify( + goalReachedId, + Notification.Builder( + context, + Channel.GoalReached.id + ).apply { + setSmallIcon(R.drawable.ic_notification) + setContentTitle("You reached your goal today!") + setContentText("\uD83D\uDCA7\uD83C\uDF89\uD83C\uDF8A\uD83E\uDD73") + setContentIntent(openAppPendingIntent) + setColor(NOTIFICATION_COLOR) + setShowWhen(true) + setAutoCancel(true) + }.build() + ) + } + + fun clear() { + notificationManager.cancelAll() + } + + companion object { + private const val hydrationReminderId = 1357 + private const val goalReachedId = 1358 + private val NOTIFICATION_COLOR = Color(0xFF00A3F8).toArgb() + } +} + +class AddCupFromNotification : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + val millilitersValue = intent.getIntExtra(EXTRA_MILLILITERS_VALUE, -1) + if (millilitersValue == -1) return + App.instance.store.dispatch( + AppAction.AddHydration(Milliliters(millilitersValue)) + ) + } + + companion object { + const val EXTRA_MILLILITERS_VALUE = "EXTRA_MILLILITERS_VALUE" + } +} + +enum class Channel( + val id: String, + private val displayName: String, + private val description: String, + @IntRange(from = 0, to = 4) private val importance: Int +) { + Hydrate( + id = "HydroReminder", + displayName = "Hydration Reminder", + description = "Notifications for hydration reminders set in the application.", + importance = NotificationManager.IMPORTANCE_HIGH + ), + GoalReached( + id = "GoalReached", + displayName = "Hydration Goal Reached", + description = "Notifications for when you reach your hydration goal for the day.", + importance = NotificationManager.IMPORTANCE_DEFAULT + ); + + companion object { + fun registerAll(context: Context) { + checkNotNull(context.getSystemService()) + .createNotificationChannels( + entries.map { channel -> + NotificationChannel( + channel.id, + channel.displayName, + channel.importance + ).also { it.description = channel.description } + } + ) + } + } +} diff --git a/app/src/main/java/at/florianschuster/hydro/service/PreferencesStore.kt b/app/src/main/java/at/florianschuster/hydro/service/PreferencesStore.kt new file mode 100644 index 0000000..4df6bf5 --- /dev/null +++ b/app/src/main/java/at/florianschuster/hydro/service/PreferencesStore.kt @@ -0,0 +1,128 @@ +package at.florianschuster.hydro.service + +import android.content.Context +import androidx.datastore.preferences.core.MutablePreferences +import androidx.datastore.preferences.core.booleanPreferencesKey +import androidx.datastore.preferences.core.edit +import androidx.datastore.preferences.core.intPreferencesKey +import androidx.datastore.preferences.core.longPreferencesKey +import androidx.datastore.preferences.core.stringPreferencesKey +import androidx.datastore.preferences.preferencesDataStore +import at.florianschuster.hydro.model.Cup +import at.florianschuster.hydro.model.LiquidUnit +import at.florianschuster.hydro.model.Milliliters +import at.florianschuster.hydro.model.Reminder +import at.florianschuster.hydro.model.Theme +import at.florianschuster.hydro.model.Today +import at.florianschuster.hydro.model.UserTimeZone +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.map +import kotlinx.datetime.Instant +import kotlinx.datetime.toLocalDateTime +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json + +class PreferencesStore( + private val context: Context +) { + + val dailyGoal: Flow = context.dataStore.data.map { preferences -> + val persisted = preferences[dailyTargetMillilitersKey] + if (persisted != null) Milliliters(persisted) else null + } + + suspend fun setDailyGoal(milliliters: Milliliters) { + context.dataStore.edit { it[dailyTargetMillilitersKey] = milliliters.value } + } + + val reminder: Flow = context.dataStore.data.map { preferences -> + val persisted = preferences[reminderKey] ?: return@map null + json.decodeFromString(persisted) + } + + suspend fun setReminder(reminder: Reminder?) { + context.dataStore.edit { preferences -> + if (reminder == null) { + preferences.remove(reminderKey) + } else { + preferences[reminderKey] = json.encodeToString(reminder) + } + } + } + + val theme: Flow = context.dataStore.data.map { preferences -> + val persisted = preferences[themeKey] + Theme.of(persisted) + } + + suspend fun setTheme(theme: Theme) { + context.dataStore.edit { preferences -> + preferences[themeKey] = theme.serialized + } + } + + val lastGoalCelebration: Flow = context.dataStore.data.map { preferences -> + val persisted = preferences[lastGoalCelebrationKey] ?: return@map Instant.DISTANT_PAST + Instant.fromEpochMilliseconds(persisted) + } + + suspend fun setLastGoalCelebration(instant: Instant) { + context.dataStore.edit { preferences -> + preferences[lastGoalCelebrationKey] = instant.toEpochMilliseconds() + } + } + + val selectedCups: Flow> = context.dataStore.data.map { preferences -> + val persisted = preferences[selectedCupsKey] ?: return@map emptyList() + json.decodeFromString(persisted) + } + + suspend fun setSelectedCups(cups: List) { + context.dataStore.edit { preferences -> + preferences[selectedCupsKey] = json.encodeToString(cups) + } + } + + val liquidUnit: Flow = context.dataStore.data.map { preferences -> + val persisted = preferences[liquidUnitKey] + LiquidUnit.of(persisted) + } + + suspend fun setLiquidUnit(unit: LiquidUnit) { + context.dataStore.edit { it[liquidUnitKey] = unit.serialized } + } + + val onboardingShown: Flow = context.dataStore.data.map { preferences -> + preferences[onboardingShownKey] ?: false + } + + suspend fun setOnboardingShown(shown: Boolean) { + context.dataStore.edit { it[onboardingShownKey] = shown } + } + + suspend fun clear() { + context.dataStore.edit(MutablePreferences::clear) + } + + companion object { + private val json = Json + + private val Context.dataStore by preferencesDataStore("user_preferences") + private val dailyTargetMillilitersKey = intPreferencesKey("dailyTargetMilliliters") + private val reminderKey = stringPreferencesKey("reminder") + private val themeKey = stringPreferencesKey("theme") + private val lastGoalCelebrationKey = longPreferencesKey("lastGoalCelebration") + private val selectedCupsKey = stringPreferencesKey("selectedCups") + private val liquidUnitKey = stringPreferencesKey("liquidUnit") + private val onboardingShownKey = booleanPreferencesKey("onboardingShown") + } +} + +suspend fun PreferencesStore.hasCelebratedGoalToday(): Boolean { + val lastCelebrationDate = lastGoalCelebration + .first() + .toLocalDateTime(UserTimeZone) + .date + return lastCelebrationDate == Today +} diff --git a/app/src/main/java/at/florianschuster/hydro/service/ReminderAlarmService.kt b/app/src/main/java/at/florianschuster/hydro/service/ReminderAlarmService.kt new file mode 100644 index 0000000..8baaa13 --- /dev/null +++ b/app/src/main/java/at/florianschuster/hydro/service/ReminderAlarmService.kt @@ -0,0 +1,110 @@ +package at.florianschuster.hydro.service + +import android.app.AlarmManager +import android.app.PendingIntent +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.os.Build +import androidx.core.content.getSystemService +import at.florianschuster.hydro.App +import at.florianschuster.hydro.AppAction +import at.florianschuster.hydro.model.Now +import at.florianschuster.hydro.model.Reminder +import at.florianschuster.hydro.model.Today +import at.florianschuster.hydro.model.UserTimeZone +import at.florianschuster.hydro.model.calculateReminderTimes +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.stateIn +import kotlinx.datetime.DatePeriod +import kotlinx.datetime.LocalDateTime +import kotlinx.datetime.LocalTime +import kotlinx.datetime.plus +import kotlinx.datetime.toInstant + +class ReminderAlarmService( + private val context: Context, + private val preferencesStore: PreferencesStore, + scope: CoroutineScope +) { + + private val alarmManager: AlarmManager = checkNotNull(context.getSystemService()) + + private val canScheduleAlarmsNow: Boolean + get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + alarmManager.canScheduleExactAlarms() + } else { + true + } + + val canScheduleAlarms: StateFlow = callbackFlow { + send(canScheduleAlarmsNow) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + val receiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + trySend(canScheduleAlarmsNow) + } + } + context.registerReceiver( + receiver, + IntentFilter(AlarmManager.ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED) + ) + awaitClose { context.unregisterReceiver(receiver) } + } else { + awaitClose() + } + }.stateIn(scope, SharingStarted.Eagerly, canScheduleAlarmsNow) + + suspend fun setAlarm(reminder: Reminder) { + clear() + check(canScheduleAlarms.value) { "cannot schedule exact reminders without permission." } + reminder.calculateReminderTimes().forEach { time -> + val alarmTime = if (time <= Now) { + LocalDateTime(Today + DatePeriod(days = 1), time) + } else { + LocalDateTime(Today, time) + }.toInstant(UserTimeZone) + alarmManager.setRepeating( + AlarmManager.RTC_WAKEUP, + alarmTime.toEpochMilliseconds(), + AlarmManager.INTERVAL_DAY, + alarmPendingIntent(time) + ) + } + } + + suspend fun clear() { + val reminder = preferencesStore.reminder.first() ?: return + reminder.calculateReminderTimes().forEach { time -> + alarmManager.cancel(alarmPendingIntent(time)) + } + } + + private fun alarmPendingIntent(time: LocalTime): PendingIntent { + return PendingIntent.getBroadcast( + context, + time.toMillisecondOfDay(), + Intent(context, ReminderShowNotificationReceiver::class.java), + PendingIntent.FLAG_IMMUTABLE + ) + } +} + +class ReminderShowNotificationReceiver : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + App.instance.store.dispatch(AppAction.ShowHydrationReminderNotification()) + } +} + +class ReminderBootCompleteReceiver : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + if (intent.action != Intent.ACTION_BOOT_COMPLETED) return + App.instance.store.dispatch(AppAction.RestartReminder) + } +} diff --git a/app/src/main/java/at/florianschuster/hydro/ui/CupCarouselSelection.kt b/app/src/main/java/at/florianschuster/hydro/ui/CupCarouselSelection.kt new file mode 100644 index 0000000..d08212a --- /dev/null +++ b/app/src/main/java/at/florianschuster/hydro/ui/CupCarouselSelection.kt @@ -0,0 +1,80 @@ +package at.florianschuster.hydro.ui + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Button +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import at.florianschuster.hydro.AppAction +import at.florianschuster.hydro.AppState +import at.florianschuster.hydro.model.Cup +import at.florianschuster.hydro.ui.base.HydrationCarousel + +@Composable +fun CupCarouselSelection( + modifier: Modifier = Modifier, + state: AppState, + dispatch: (AppAction) -> Unit +) { + Column(modifier = modifier) { + var showCanOnlySelectThreeAlert by remember { mutableStateOf(false) } + Text( + modifier = Modifier.padding(horizontal = 24.dp), + text = "Cups", + style = MaterialTheme.typography.titleLarge + ) + Text( + modifier = Modifier.padding(horizontal = 24.dp), + text = "Cups are displayed on your Main screen and in you Reminder notification", + style = MaterialTheme.typography.labelSmall, + color = MaterialTheme.colorScheme.primary + ) + Spacer(modifier = Modifier.height(16.dp)) + val allCupsAsMilliliters = remember(state.allCups) { + state.allCups.map(Cup::milliliters) + } + val selectedCupsAsMilliliters = remember(state.selectedCups) { + state.selectedCups.map(Cup::milliliters) + } + HydrationCarousel( + contentPadding = PaddingValues(horizontal = 36.dp), + milliliterItems = allCupsAsMilliliters, + liquidUnit = state.liquidUnit, + selected = selectedCupsAsMilliliters, + onClick = { index, _ -> + val cup = state.allCups[index] + if (cup in state.selectedCups) { + dispatch(AppAction.SetSelectedCups(state.selectedCups - cup)) + } else if (state.selectedCups.count() >= 3) { + showCanOnlySelectThreeAlert = true + } else { + dispatch(AppAction.SetSelectedCups(state.selectedCups + cup)) + } + } + ) + if (showCanOnlySelectThreeAlert) { + AlertDialog( + title = { Text(text = "Sorry") }, + text = { Text(text = "You can only select 3 different Cups.") }, + onDismissRequest = { showCanOnlySelectThreeAlert = false }, + confirmButton = { + Button( + onClick = { showCanOnlySelectThreeAlert = false }, + content = { Text(text = "Ok") } + ) + } + ) + } + } +} diff --git a/app/src/main/java/at/florianschuster/hydro/ui/GoalOfTheDayBottomSheet.kt b/app/src/main/java/at/florianschuster/hydro/ui/GoalOfTheDayBottomSheet.kt new file mode 100644 index 0000000..05e000c --- /dev/null +++ b/app/src/main/java/at/florianschuster/hydro/ui/GoalOfTheDayBottomSheet.kt @@ -0,0 +1,88 @@ +package at.florianschuster.hydro.ui + +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Slider +import androidx.compose.material3.SliderDefaults +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.tooling.preview.Preview +import at.florianschuster.hydro.AppAction +import at.florianschuster.hydro.AppState +import at.florianschuster.hydro.model.Milliliters +import at.florianschuster.hydro.model.format +import at.florianschuster.hydro.ui.base.BottomSheet +import at.florianschuster.hydro.ui.base.HydroTheme +import kotlin.math.roundToInt + +@Composable +fun GoalOfTheDayBottomSheet( + state: AppState, + dispatch: (AppAction) -> Unit +) { + BottomSheet( + title = "Goal of the Day" + ) { + MillilitersSlider( + range = remember { Milliliters.DAILY_GOAL_MIN..Milliliters.DAILY_GOAL_MAX }, + stepsSize = remember { Milliliters.DAILY_GOAL_STEPS }, + milliliters = state.dailyGoal, + onMillilitersChanged = { dispatch(AppAction.SetDailyGoal(it)) } + ) + Text(text = state.dailyGoal.format(state.liquidUnit)) + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +private fun MillilitersSlider( + range: ClosedRange, + stepsSize: Milliliters, + milliliters: Milliliters, + onMillilitersChanged: (Milliliters) -> Unit +) { + val valueRange = remember(range) { + range.start.value.toFloat()..range.endInclusive.value.toFloat() + } + val steps = remember(valueRange) { + val itemCount = (valueRange.endInclusive - valueRange.start).roundToInt() + if (stepsSize.value == 1) 0 else (itemCount / stepsSize.value - 1) + } + Slider( + value = milliliters.value.toFloat(), + valueRange = valueRange, + steps = steps, + onValueChange = { onMillilitersChanged(Milliliters(it.roundToInt())) }, + colors = SliderDefaults.colors( + activeTickColor = Color.Transparent, + inactiveTickColor = Color.Transparent + ) + ) +} + +@Preview +@Composable +fun MillilitersSliderPreviewDark() { + HydroTheme(darkTheme = true, dynamicColor = false) { + MillilitersSlider( + range = Milliliters(0)..Milliliters(2000), + stepsSize = Milliliters(100), + milliliters = Milliliters(1000), + onMillilitersChanged = {} + ) + } +} + +@Preview +@Composable +fun MillilitersSliderPreviewLight() { + HydroTheme(darkTheme = false, dynamicColor = false) { + MillilitersSlider( + range = Milliliters(0)..Milliliters(2000), + stepsSize = Milliliters(100), + milliliters = Milliliters(1000), + onMillilitersChanged = {} + ) + } +} diff --git a/app/src/main/java/at/florianschuster/hydro/ui/HistoryScreen.kt b/app/src/main/java/at/florianschuster/hydro/ui/HistoryScreen.kt new file mode 100644 index 0000000..035e713 --- /dev/null +++ b/app/src/main/java/at/florianschuster/hydro/ui/HistoryScreen.kt @@ -0,0 +1,227 @@ +package at.florianschuster.hydro.ui + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Close +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.CenterAlignedTopAppBar +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import at.florianschuster.hydro.App +import at.florianschuster.hydro.AppState +import at.florianschuster.hydro.model.Day +import at.florianschuster.hydro.model.LiquidUnit +import at.florianschuster.hydro.model.Today +import at.florianschuster.hydro.model.format +import at.florianschuster.hydro.model.reachedGoal +import at.florianschuster.hydro.model.sumOfMilliliters +import at.florianschuster.hydro.service.HydrationHistoryStore +import at.florianschuster.hydro.ui.HistoryScreenStore.Companion.LOAD_MORE_THRESHOLD +import at.florianschuster.hydro.ui.base.HydrationCarousel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.datetime.DatePeriod +import kotlinx.datetime.LocalDate +import kotlinx.datetime.plus + +@Composable +fun HistoryScreen( + contentPadding: PaddingValues, + appState: AppState, + historyScreenStore: HistoryScreenStore = remember(appState) { + HistoryScreenStore( + hydrationHistoryStore = App.instance.hydrationHistoryStore + ) + } +) { + Column( + modifier = Modifier.fillMaxSize() + ) { + val state by historyScreenStore.state.collectAsStateWithLifecycle() + + val lazyColumnState = rememberLazyListState() + val shouldLoadNext by remember { + derivedStateOf { + if (!historyScreenStore.state.value.canLoadNext) return@derivedStateOf false + val lastVisibleItemPosition = lazyColumnState.layoutInfo.visibleItemsInfo + .lastOrNull() + ?.index ?: 0 + val itemCount = lazyColumnState.layoutInfo.totalItemsCount + 1 // loading + (lastVisibleItemPosition + LOAD_MORE_THRESHOLD) > itemCount + } + } + LaunchedEffect(shouldLoadNext) { + if (shouldLoadNext) { + historyScreenStore.loadNext() + } + } + LazyColumn( + modifier = Modifier.fillMaxWidth(), + contentPadding = contentPadding, + state = lazyColumnState, + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + items(state.days, key = Day::id) { day -> + DayItem( + modifier = Modifier.padding(horizontal = 16.dp), + day = day, + unit = appState.liquidUnit + ) + } + item { + if (state.loading) { + CircularProgressIndicator( + modifier = Modifier.padding(24.dp) + ) + } + } + } + + Spacer(modifier = Modifier.height(16.dp)) + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun HistoryScreenToolbar( + containerColor: Color = Color.Transparent, + onGoBack: () -> Unit +) { + CenterAlignedTopAppBar( + modifier = Modifier + .fillMaxWidth(), + navigationIcon = { + IconButton( + onClick = onGoBack, + content = { + Icon( + imageVector = Icons.Outlined.Close, + contentDescription = "close history" + ) + } + ) + }, + title = { + Text( + text = "History", + style = MaterialTheme.typography.headlineSmall + ) + }, + colors = TopAppBarDefaults.centerAlignedTopAppBarColors( + containerColor = containerColor + ) + ) +} + +@Composable +private fun DayItem( + modifier: Modifier = Modifier, + day: Day, + unit: LiquidUnit +) { + Card( + modifier = modifier, + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.95f) + ) + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 24.dp), + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + Text( + modifier = Modifier.padding(horizontal = 24.dp), + text = day.date.format(), + style = MaterialTheme.typography.labelLarge + ) + + val milliliters = remember(day) { day.hydration.map { it.milliliters } } + HydrationCarousel( + contentPadding = PaddingValues(horizontal = 24.dp), + milliliterItems = milliliters, + selected = milliliters, + liquidUnit = unit, + contentBelowItem = { index -> + Text( + text = day.hydration[index].time.format(), + style = MaterialTheme.typography.labelMedium + ) + } + ) + + Text( + modifier = Modifier.padding(horizontal = 24.dp), + text = day.hydration.sumOfMilliliters().format(unit) + + " out of " + + day.goal.format(unit) + + if (day.reachedGoal()) " 🎉" else "", + style = MaterialTheme.typography.labelLarge + ) + } + } +} + +class HistoryScreenStore( + private val hydrationHistoryStore: HydrationHistoryStore +) { + + data class State( + val days: List = emptyList(), + val loading: Boolean = false, + val lastQueriedDate: LocalDate = Today + DatePeriod(days = 1), + val reachedStart: Boolean = false + ) { + val canLoadNext = !loading && !reachedStart + } + + private val _state = MutableStateFlow(State()) + val state: StateFlow = _state.asStateFlow() + + suspend fun loadNext() { + _state.update { it.copy(loading = true) } + val days = hydrationHistoryStore.days(_state.value.lastQueriedDate) + _state.update { + it.copy( + loading = false, + days = it.days + days, + lastQueriedDate = if (days.isNotEmpty()) days.last().date else it.lastQueriedDate, + reachedStart = days.isEmpty() + ) + } + } + + companion object { + const val LOAD_MORE_THRESHOLD = 10 + } +} diff --git a/app/src/main/java/at/florianschuster/hydro/ui/OnboardingScreen.kt b/app/src/main/java/at/florianschuster/hydro/ui/OnboardingScreen.kt new file mode 100644 index 0000000..d728324 --- /dev/null +++ b/app/src/main/java/at/florianschuster/hydro/ui/OnboardingScreen.kt @@ -0,0 +1,77 @@ +package at.florianschuster.hydro.ui + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.NavigateNext +import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import at.florianschuster.hydro.AppAction +import kotlinx.coroutines.delay +import kotlin.time.Duration.Companion.milliseconds + +@Composable +fun OnboardingScreen( + contentPadding: PaddingValues, + dispatch: (AppAction) -> Unit, + onOnboardingFinished: () -> Unit +) { + Box( + modifier = Modifier + .fillMaxSize() + .padding(contentPadding) + ) { + Column( + modifier = Modifier.padding(horizontal = 32.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Spacer(modifier = Modifier.fillMaxHeight(0.3f)) + Text( + text = "Welcome to hydro", + style = MaterialTheme.typography.headlineMedium, + fontWeight = FontWeight.Bold + ) + Text( + text = "a simple no-nonsense hydration tracking app", + style = MaterialTheme.typography.bodyLarge, + fontWeight = FontWeight.Light, + textAlign = TextAlign.Center + ) + } + + FloatingActionButton( + modifier = Modifier + .align(Alignment.BottomCenter) + .padding(24.dp), + onClick = { + dispatch(AppAction.SetOnboardingShown) + onOnboardingFinished() + }, + content = { + Icon( + Icons.Outlined.NavigateNext, + contentDescription = "go next" + ) + } + ) + } + + LaunchedEffect(Unit) { + delay(500.milliseconds) + dispatch(AppAction.SetHydrationForOnboarding) + } +} diff --git a/app/src/main/java/at/florianschuster/hydro/ui/SetIntervalBottomSheet.kt b/app/src/main/java/at/florianschuster/hydro/ui/SetIntervalBottomSheet.kt new file mode 100644 index 0000000..dbec264 --- /dev/null +++ b/app/src/main/java/at/florianschuster/hydro/ui/SetIntervalBottomSheet.kt @@ -0,0 +1,48 @@ +package at.florianschuster.hydro.ui + +import androidx.compose.material3.Slider +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import at.florianschuster.hydro.AppAction +import at.florianschuster.hydro.AppState +import at.florianschuster.hydro.model.format +import at.florianschuster.hydro.ui.base.BottomSheet +import kotlin.math.roundToInt +import kotlin.time.Duration.Companion.minutes + +@Composable +fun SetIntervalBottomSheet( + state: AppState, + dispatch: (AppAction) -> Unit, + onClose: () -> Unit +) { + check(state.reminder != null) + BottomSheet( + title = "Interval" + ) { + var value by remember(state.reminder) { + mutableStateOf(state.reminder.interval.inWholeMinutes.toFloat()) + } + val intervalValue by remember(value) { + derivedStateOf { value.roundToInt().minutes } + } + Slider( + value = value, + valueRange = 1f..60f, + steps = 58, + onValueChange = { minutesValue -> value = minutesValue }, + onValueChangeFinished = { + dispatch( + AppAction.SetReminder(state.reminder.copy(interval = intervalValue)) + ) + onClose() + } + ) + Text(text = intervalValue.format()) + } +} diff --git a/app/src/main/java/at/florianschuster/hydro/ui/SetLiquidUnitBottomSheet.kt b/app/src/main/java/at/florianschuster/hydro/ui/SetLiquidUnitBottomSheet.kt new file mode 100644 index 0000000..afb0778 --- /dev/null +++ b/app/src/main/java/at/florianschuster/hydro/ui/SetLiquidUnitBottomSheet.kt @@ -0,0 +1,46 @@ +package at.florianschuster.hydro.ui + +import androidx.compose.foundation.clickable +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Check +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import at.florianschuster.hydro.AppAction +import at.florianschuster.hydro.AppState +import at.florianschuster.hydro.model.LiquidUnit +import at.florianschuster.hydro.model.format +import at.florianschuster.hydro.model.formatMoreInfo +import at.florianschuster.hydro.ui.base.BottomSheet +import at.florianschuster.hydro.ui.base.HydroListItem + +@Composable +fun SetLiquidUnitBottomSheet( + state: AppState, + dispatch: (AppAction) -> Unit, + onClose: () -> Unit +) { + BottomSheet( + title = "Measurement Unit" + ) { + LiquidUnit.entries.forEach { liquidUnit -> + HydroListItem( + modifier = Modifier.clickable { + dispatch(AppAction.SetLiquidUnit(liquidUnit)) + onClose() + }, + headlineContent = { Text(text = liquidUnit.format()) }, + supportingContent = { Text(text = liquidUnit.formatMoreInfo()) }, + trailingContent = { + if (state.liquidUnit == liquidUnit) { + Icon( + imageVector = Icons.Outlined.Check, + contentDescription = "selected" + ) + } + } + ) + } + } +} diff --git a/app/src/main/java/at/florianschuster/hydro/ui/SettingsScreen.kt b/app/src/main/java/at/florianschuster/hydro/ui/SettingsScreen.kt new file mode 100644 index 0000000..7b503f6 --- /dev/null +++ b/app/src/main/java/at/florianschuster/hydro/ui/SettingsScreen.kt @@ -0,0 +1,568 @@ +package at.florianschuster.hydro.ui + +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.net.Uri +import android.os.Build +import android.provider.Settings +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.statusBarsPadding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Close +import androidx.compose.material.icons.outlined.Delete +import androidx.compose.material.icons.outlined.Email +import androidx.compose.material.icons.outlined.Info +import androidx.compose.material.icons.outlined.InvertColors +import androidx.compose.material.icons.outlined.Notifications +import androidx.compose.material.icons.outlined.NotificationsActive +import androidx.compose.material.icons.outlined.NotificationsOff +import androidx.compose.material.icons.outlined.RestartAlt +import androidx.compose.material.icons.outlined.Start +import androidx.compose.material.icons.outlined.WaterDrop +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Button +import androidx.compose.material3.CenterAlignedTopAppBar +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.scale +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp +import androidx.core.content.ContextCompat +import at.florianschuster.hydro.AppAction +import at.florianschuster.hydro.AppState +import at.florianschuster.hydro.R +import at.florianschuster.hydro.model.LiquidUnit +import at.florianschuster.hydro.model.Milliliters +import at.florianschuster.hydro.model.Reminder +import at.florianschuster.hydro.model.format +import at.florianschuster.hydro.ui.base.SettingItem +import at.florianschuster.hydro.ui.base.SettingsSection +import at.florianschuster.hydro.ui.base.TimePickerDialog +import at.florianschuster.hydro.ui.base.ToggleSettingItem +import kotlinx.datetime.LocalTime + +@Composable +fun SettingsScreen( + contentPadding: PaddingValues, + state: AppState, + dispatch: (AppAction) -> Unit, + onSetGoalOfTheDay: () -> Unit, + onSetLiquidUnit: () -> Unit, + onSetTheme: () -> Unit, + onShowDeveloperInfo: () -> Unit, + onWriteDeveloper: () -> Unit, + onSetInterval: () -> Unit +) { + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()) + ) { + SettingsSection( + modifier = Modifier + .padding(top = contentPadding.calculateTopPadding()) + .padding(horizontal = 16.dp), + title = "Hydration" + ) { + SettingItem( + fieldName = "Daily Goal", + value = state.dailyGoal.format(state.liquidUnit), + icon = { + Icon( + imageVector = Icons.Outlined.WaterDrop, + contentDescription = null + ) + }, + onClick = onSetGoalOfTheDay + ) + SettingItem( + fieldName = "Measurement Unit", + value = state.liquidUnit.format(), + icon = { + Icon( + modifier = Modifier.size(24.dp), + painter = painterResource(R.drawable.ic_unit), + contentDescription = null + ) + }, + onClick = onSetLiquidUnit + ) + CupCarouselSelection( + state = state, + dispatch = dispatch + ) + } + + Spacer(modifier = Modifier.height(16.dp)) + + ReminderSettingsSection( + canScheduleAlarms = state.canScheduleAlarms, + reminder = state.reminder, + onRemindersChanged = { dispatch(AppAction.SetReminder(it)) }, + onSetInterval = onSetInterval + ) + + Spacer(modifier = Modifier.height(16.dp)) + + SettingsSection( + modifier = Modifier + .padding(horizontal = 16.dp), + title = "App" + ) { + SettingItem( + fieldName = "Theme", + value = state.theme.format(), + icon = { + Icon( + imageVector = Icons.Outlined.InvertColors, + contentDescription = null + ) + }, + onClick = onSetTheme + ) + } + + Spacer(modifier = Modifier.height(16.dp)) + + DataSettingsSection( + onDeleteAll = { dispatch(AppAction.DeleteAll) } + ) + + Spacer(modifier = Modifier.height(16.dp)) + + SettingsSection( + modifier = Modifier + .padding(horizontal = 16.dp) + .apply { + if (!state.isDebug) { + padding(bottom = contentPadding.calculateBottomPadding()) + } + }, + title = "About" + ) { + SettingItem( + fieldName = "Developer Info", + icon = { + Icon( + imageVector = Icons.Outlined.Info, + contentDescription = null + ) + }, + onClick = onShowDeveloperInfo + ) + SettingItem( + fieldName = "Feedback", + icon = { + Icon( + imageVector = Icons.Outlined.Email, + contentDescription = null + ) + }, + onClick = onWriteDeveloper + ) + } + + Spacer(modifier = Modifier.height(16.dp)) + + if (state.isDebug) { + DebugSettingsSection( + modifier = Modifier.padding(bottom = contentPadding.calculateBottomPadding()), + todayHydration = state.todayHydration, + liquidUnit = state.liquidUnit, + onResetToday = { dispatch(AppAction.ResetToday) }, + onShowReminderNotification = { + dispatch(AppAction.ShowHydrationReminderNotification(forced = true)) + } + ) + + Spacer(modifier = Modifier.height(16.dp)) + } + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun SettingsToolbar( + containerColor: Color = Color.Transparent, + onGoBack: () -> Unit +) { + CenterAlignedTopAppBar( + modifier = Modifier + .fillMaxWidth() + .statusBarsPadding(), + navigationIcon = { + IconButton( + onClick = onGoBack, + content = { + Icon( + imageVector = Icons.Outlined.Close, + contentDescription = "close settings" + ) + } + ) + }, + title = { + Text( + text = "Settings", + style = MaterialTheme.typography.headlineSmall + ) + }, + colors = TopAppBarDefaults.centerAlignedTopAppBarColors( + containerColor = containerColor + ) + ) +} + +@Composable +private fun ReminderSettingsSection( + canScheduleAlarms: Boolean, + reminder: Reminder?, + onRemindersChanged: (Reminder?) -> Unit, + onSetInterval: () -> Unit +) { + var showPermissionRationaleDialog by remember { mutableStateOf(false) } + var showScheduleAlarmsDialog by remember { mutableStateOf(false) } + val launcher = rememberLauncherForActivityResult( + ActivityResultContracts.RequestPermission() + ) { granted -> + if (!granted) { + showPermissionRationaleDialog = true + } else if (!canScheduleAlarms) { + showScheduleAlarmsDialog = true + } else { + onRemindersChanged(Reminder.DEFAULT) + } + } + val context = LocalContext.current + SettingsSection( + modifier = Modifier.padding(horizontal = 16.dp), + title = "Reminders" + ) { + ToggleSettingItem( + fieldName = "Enabled", + icon = if (reminder != null) { + Icons.Outlined.Notifications + } else { + Icons.Outlined.NotificationsOff + }, + checked = reminder != null, + onCheckedChange = { checked -> + if (checked) { + if (!canScheduleAlarms) { + showScheduleAlarmsDialog = true + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + val permission = android.Manifest.permission.POST_NOTIFICATIONS + if (!context.isPermissionGranted(permission)) { + launcher.launch(permission) + } else { + onRemindersChanged(Reminder.DEFAULT) + } + } else { + onRemindersChanged(Reminder.DEFAULT) + } + } else { + onRemindersChanged(null) + } + } + ) + if (reminder != null) { + var showStartPicker by remember { mutableStateOf(false) } + var showEndPicker by remember { mutableStateOf(false) } + var showStartBeforeEndAlert by remember { mutableStateOf(false) } + SettingItem( + fieldName = "Start", + value = reminder.start.format(), + icon = { + Icon( + imageVector = Icons.Outlined.Start, + contentDescription = null + ) + }, + onClick = { showStartPicker = true } + ) + SettingItem( + fieldName = "End", + value = reminder.end.format(), + icon = { + Icon( + modifier = Modifier.scale(scaleX = -1f, scaleY = 1f), + imageVector = Icons.Outlined.Start, + contentDescription = null + ) + }, + onClick = { showEndPicker = true } + ) + SettingItem( + fieldName = "Interval", + value = reminder.interval.format(), + icon = { + Icon( + modifier = Modifier.size(24.dp), + painter = painterResource(R.drawable.ic_arrow_range), + contentDescription = null + ) + }, + onClick = onSetInterval + ) + if (showStartPicker) { + TimePickerDialog( + initialHour = reminder.start.hour, + initialMinute = reminder.start.minute, + onConfirm = { hour, minute -> + showStartPicker = false + val start = LocalTime(hour, minute, 0) + if (start > reminder.end) { + showStartBeforeEndAlert = true + } else { + onRemindersChanged(reminder.copy(start = start)) + } + }, + onCancel = { showStartPicker = false } + ) + } + if (showEndPicker) { + TimePickerDialog( + initialHour = reminder.end.hour, + initialMinute = reminder.end.minute, + onConfirm = { hour, minute -> + showEndPicker = false + val end = LocalTime(hour, minute, 0) + if (reminder.start > end) { + showStartBeforeEndAlert = true + } else { + onRemindersChanged(reminder.copy(end = end)) + } + }, + onCancel = { showEndPicker = false } + ) + } + if (showStartBeforeEndAlert) { + StartBeforeEndAlert( + onDismiss = { showStartBeforeEndAlert = false } + ) + } + } + if (showPermissionRationaleDialog) { + NotificationPermissionSettingsAlert( + onDismiss = { showPermissionRationaleDialog = false } + ) + } + if (showScheduleAlarmsDialog) { + AlarmSystemSettingsAlert( + onDismiss = { showScheduleAlarmsDialog = false } + ) + } + } +} + +private fun Context.isPermissionGranted(permission: String): Boolean { + return ContextCompat.checkSelfPermission( + this, + permission + ) == PackageManager.PERMISSION_GRANTED +} + +@Composable +private fun StartBeforeEndAlert(onDismiss: () -> Unit) { + AlertDialog( + onDismissRequest = { onDismiss() }, + title = { Text(text = "Time constraints") }, + text = { + Text( + text = "Start time of the Reminder must be before the end time." + ) + }, + confirmButton = { + Button( + onClick = { onDismiss() }, + content = { Text(text = "Ok") } + ) + } + ) +} + +@Composable +private fun NotificationPermissionSettingsAlert(onDismiss: () -> Unit) { + val context = LocalContext.current + AlertDialog( + onDismissRequest = { onDismiss() }, + title = { Text(text = "Notification permission denied") }, + text = { + Text( + text = "The app is unable to show Reminders " + + "without the notification permission." + ) + }, + confirmButton = { + Button( + onClick = { + context.goToNotificationPermissionSettings() + onDismiss() + }, + content = { Text(text = "Go To Settings") } + ) + }, + dismissButton = { + Button( + onClick = { onDismiss() }, + content = { Text(text = "Cancel") } + ) + } + ) +} + +private fun Context.goToNotificationPermissionSettings() { + startActivity( + Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply { + data = Uri.parse("package:$packageName") + } + ) +} + +@Composable +private fun AlarmSystemSettingsAlert(onDismiss: () -> Unit) { + val context = LocalContext.current + AlertDialog( + onDismissRequest = { onDismiss() }, + title = { Text(text = "Alarm permission denied") }, + text = { + Text( + text = "The app is unable to show Reminders " + + "without the alarm permission." + ) + }, + confirmButton = { + Button( + onClick = { + context.goToAlarmSystemSettings() + onDismiss() + }, + content = { Text(text = "Go To Settings") } + ) + }, + dismissButton = { + Button( + onClick = { onDismiss() }, + content = { Text(text = "Cancel") } + ) + } + ) +} + +private fun Context.goToAlarmSystemSettings() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return + startActivity( + Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM).apply { + data = Uri.parse("package:$packageName") + flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + } + ) +} + +@Composable +private fun DataSettingsSection( + modifier: Modifier = Modifier, + onDeleteAll: () -> Unit +) { + var showDeleteAllDialog by remember { mutableStateOf(false) } + SettingsSection( + modifier = modifier.padding(horizontal = 16.dp), + title = "Data" + ) { + SettingItem( + fieldName = "Delete all stored data", + value = "This includes all settings and the complete hydration history", + icon = { + Icon( + imageVector = Icons.Outlined.Delete, + contentDescription = null + ) + }, + onClick = { showDeleteAllDialog = true } + ) + } + if (showDeleteAllDialog) { + AlertDialog( + onDismissRequest = { showDeleteAllDialog = false }, + title = { Text(text = "Are you sure?") }, + text = { Text(text = "This cannot be reversed.") }, + confirmButton = { + Button( + onClick = { + showDeleteAllDialog = false + onDeleteAll() + }, + content = { Text(text = "Yes") } + ) + }, + dismissButton = { + Button( + onClick = { showDeleteAllDialog = false }, + content = { Text(text = "Cancel") } + ) + } + ) + } +} + +@Composable +private fun DebugSettingsSection( + modifier: Modifier = Modifier, + todayHydration: Milliliters, + liquidUnit: LiquidUnit, + onResetToday: () -> Unit, + onShowReminderNotification: () -> Unit +) { + SettingsSection( + modifier = modifier.padding(horizontal = 16.dp), + title = "Debug" + ) { + SettingItem( + fieldName = "Reset hydration today", + value = "This sets today's hydration from ${todayHydration.format(liquidUnit)} " + + "to ${Milliliters.ZERO.format(liquidUnit)}", + icon = { + Icon( + imageVector = Icons.Outlined.RestartAlt, + contentDescription = null + ) + }, + onClick = onResetToday + ) + + SettingItem( + fieldName = "Show Reminder Notification", + icon = { + Icon( + imageVector = Icons.Outlined.NotificationsActive, + contentDescription = null + ) + }, + onClick = onShowReminderNotification + ) + } +} diff --git a/app/src/main/java/at/florianschuster/hydro/ui/ThemeBottomSheet.kt b/app/src/main/java/at/florianschuster/hydro/ui/ThemeBottomSheet.kt new file mode 100644 index 0000000..62de914 --- /dev/null +++ b/app/src/main/java/at/florianschuster/hydro/ui/ThemeBottomSheet.kt @@ -0,0 +1,65 @@ +package at.florianschuster.hydro.ui + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Check +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import at.florianschuster.hydro.AppAction +import at.florianschuster.hydro.AppState +import at.florianschuster.hydro.model.Theme +import at.florianschuster.hydro.model.format +import at.florianschuster.hydro.model.icon +import at.florianschuster.hydro.ui.base.BottomSheet +import at.florianschuster.hydro.ui.base.HydroListItem + +@Composable +fun ThemeBottomSheet( + state: AppState, + dispatch: (AppAction) -> Unit, + onClose: () -> Unit +) { + BottomSheet( + title = "Theme" + ) { + Theme.entries.forEach { theme -> + HydroListItem( + modifier = Modifier.clickable { + dispatch(AppAction.SetTheme(theme)) + onClose() + }, + headlineContent = { Text(text = theme.format()) }, + supportingContent = { + if (theme == Theme.System) { + Text( + text = "Current system theme is " + + "${if (isSystemInDarkTheme()) "dark" else "light"}." + ) + } + if (theme == Theme.Dynamic) { + Text( + text = "Uses your phone's wallpaper to determine colors." + ) + } + }, + leadingContent = { + Icon( + imageVector = theme.icon(), + contentDescription = "system theme" + ) + }, + trailingContent = { + if (state.theme == theme) { + Icon( + imageVector = Icons.Outlined.Check, + contentDescription = "selected" + ) + } + } + ) + } + } +} diff --git a/app/src/main/java/at/florianschuster/hydro/ui/TodayScreen.kt b/app/src/main/java/at/florianschuster/hydro/ui/TodayScreen.kt new file mode 100644 index 0000000..8027092 --- /dev/null +++ b/app/src/main/java/at/florianschuster/hydro/ui/TodayScreen.kt @@ -0,0 +1,140 @@ +package at.florianschuster.hydro.ui + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.statusBarsPadding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.QueryStats +import androidx.compose.material.icons.outlined.Settings +import androidx.compose.material3.CenterAlignedTopAppBar +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import at.florianschuster.hydro.AppAction +import at.florianschuster.hydro.AppState +import at.florianschuster.hydro.R +import at.florianschuster.hydro.model.format +import at.florianschuster.hydro.model.icon +import com.airbnb.lottie.compose.LottieAnimation +import com.airbnb.lottie.compose.LottieCompositionSpec +import com.airbnb.lottie.compose.rememberLottieComposition + +@Composable +internal fun TodayScreen( + contentPadding: PaddingValues, + state: AppState, + dispatch: (AppAction) -> Unit +) { + Box( + modifier = Modifier + .fillMaxSize() + .padding(contentPadding) + ) { + Column( + modifier = Modifier.align(Alignment.Center), + horizontalAlignment = Alignment.CenterHorizontally + ) { + AnimatedVisibility(visible = state.dailyGoalReached) { + LottieAnimation( + modifier = Modifier.size(148.dp), + composition = rememberLottieComposition( + // https://lottiefiles.com/animations/trophy-yEGPe40FVr + spec = LottieCompositionSpec.RawRes(R.raw.winner) + ).value + ) + } + Text( + text = state.todayHydration.format(state.liquidUnit), + style = MaterialTheme.typography.displayLarge + ) + Text( + text = state.hydrationProgress.format(), + style = MaterialTheme.typography.titleMedium + ) + } + + Row( + modifier = Modifier + .align(Alignment.BottomCenter) + .padding(24.dp), + horizontalArrangement = Arrangement.spacedBy(16.dp) + ) { + state.selectedCups.forEach { cup -> + FloatingActionButton( + onClick = { dispatch(AppAction.AddHydration(cup.milliliters)) }, + content = { + Column(horizontalAlignment = Alignment.CenterHorizontally) { + Icon( + modifier = Modifier.size(20.dp), + painter = cup.milliliters.icon(), + contentDescription = "add one cup" + ) + Text( + text = cup.milliliters.format(state.liquidUnit), + style = MaterialTheme.typography.labelSmall + ) + } + } + ) + } + } + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun TodayToolbar( + containerColor: Color = Color.Transparent, + isDebug: Boolean, + onGoToHistory: () -> Unit, + onGoToSettings: () -> Unit +) { + CenterAlignedTopAppBar( + modifier = Modifier + .fillMaxWidth() + .statusBarsPadding(), + actions = { + if (isDebug) { + IconButton( + onClick = onGoToHistory, + content = { + Icon( + imageVector = Icons.Outlined.QueryStats, + contentDescription = "go to history" + ) + } + ) + } + IconButton( + onClick = onGoToSettings, + content = { + Icon( + imageVector = Icons.Outlined.Settings, + contentDescription = "go to settings" + ) + } + ) + }, + title = {}, + colors = TopAppBarDefaults.centerAlignedTopAppBarColors( + containerColor = containerColor + ) + ) +} diff --git a/app/src/main/java/at/florianschuster/hydro/ui/base/BottomSheet.kt b/app/src/main/java/at/florianschuster/hydro/ui/base/BottomSheet.kt new file mode 100644 index 0000000..30486e4 --- /dev/null +++ b/app/src/main/java/at/florianschuster/hydro/ui/base/BottomSheet.kt @@ -0,0 +1,66 @@ +package at.florianschuster.hydro.ui.base + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.navigationBarsPadding +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CornerSize +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.unit.dp + +@Composable +fun BottomSheet( + title: String, + content: @Composable ColumnScope.() -> Unit +) { + Card( + modifier = Modifier + .fillMaxWidth(), + shape = MaterialTheme.shapes.extraLarge.copy( + bottomStart = CornerSize(0.dp), + bottomEnd = CornerSize(0.dp) + ), + colors = CardDefaults.cardColors( + MaterialTheme.colorScheme.primaryContainer + ) + ) { + Column( + modifier = Modifier + .padding(horizontal = 24.dp) + .padding(bottom = 24.dp) + .navigationBarsPadding(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Spacer(modifier = Modifier.height(12.dp)) + Spacer( + modifier = Modifier + .size(width = 50.dp, height = 4.dp) + .clip(MaterialTheme.shapes.medium) + .background(MaterialTheme.colorScheme.onSurface) + ) + Spacer(modifier = Modifier.height(24.dp)) + + Text( + text = title, + style = MaterialTheme.typography.headlineMedium, + color = MaterialTheme.colorScheme.primary + ) + + Spacer(modifier = Modifier.height(32.dp)) + + content() + } + } +} diff --git a/app/src/main/java/at/florianschuster/hydro/ui/base/HydrationBackground.kt b/app/src/main/java/at/florianschuster/hydro/ui/base/HydrationBackground.kt new file mode 100644 index 0000000..4e1a433 --- /dev/null +++ b/app/src/main/java/at/florianschuster/hydro/ui/base/HydrationBackground.kt @@ -0,0 +1,46 @@ +package at.florianschuster.hydro.ui.base + +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.ContentScale +import at.florianschuster.hydro.R +import at.florianschuster.hydro.model.Percent +import com.airbnb.lottie.compose.LottieAnimation +import com.airbnb.lottie.compose.LottieClipSpec +import com.airbnb.lottie.compose.LottieCompositionSpec +import com.airbnb.lottie.compose.rememberLottieComposition + +@Composable +internal fun HydrationBackground( + modifier: Modifier = Modifier, + isSystemInDarkTheme: Boolean, + hydrationProgress: Percent +) { + val composition by rememberLottieComposition( + LottieCompositionSpec.RawRes( + // https://lottiefiles.com/animations/water-fills-square-progress-bar-mGPK3uyxIP + if (isSystemInDarkTheme) R.raw.water_dark else R.raw.water_light + ) + ) + val clipSpec by remember(hydrationProgress) { + // 196 == last frame we want to show + val maximumFrame = (minOf(1.0f, hydrationProgress.value) * 210).toInt() + mutableStateOf( + LottieClipSpec.Frame( + min = 0, + // if max frame = 0 we glitch, so we set it to 1 + max = maxOf(maximumFrame, 1) + ) + ) + } + LottieAnimation( + modifier = modifier.fillMaxSize(), + composition = composition, + clipSpec = clipSpec, + contentScale = ContentScale.FillBounds + ) +} diff --git a/app/src/main/java/at/florianschuster/hydro/ui/base/HydrationCarousel.kt b/app/src/main/java/at/florianschuster/hydro/ui/base/HydrationCarousel.kt new file mode 100644 index 0000000..fbf2d1a --- /dev/null +++ b/app/src/main/java/at/florianschuster/hydro/ui/base/HydrationCarousel.kt @@ -0,0 +1,105 @@ +package at.florianschuster.hydro.ui.base + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedCard +import androidx.compose.material3.Text +import androidx.compose.material3.surfaceColorAtElevation +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.unit.dp +import at.florianschuster.hydro.model.LiquidUnit +import at.florianschuster.hydro.model.Milliliters +import at.florianschuster.hydro.model.format +import at.florianschuster.hydro.model.icon + +@Composable +fun HydrationCarousel( + modifier: Modifier = Modifier, + contentPadding: PaddingValues = PaddingValues(0.dp), + milliliterItems: List, + liquidUnit: LiquidUnit, + selected: List = emptyList(), + onClick: (index: Int, Milliliters) -> Unit = { _, _ -> }, + contentBelowItem: @Composable (index: Int) -> Unit = {} +) { + LazyRow( + modifier = modifier, + horizontalArrangement = Arrangement.spacedBy(12.dp), + contentPadding = contentPadding + ) { + itemsIndexed(items = milliliterItems) { index, item -> + CupItem( + item = item, + index = index, + liquidUnit = liquidUnit, + selected = item in selected, + onClick = onClick, + contentBelowItem = contentBelowItem + ) + } + } +} + +@Composable +private fun CupItem( + modifier: Modifier = Modifier, + item: Milliliters, + index: Int, + liquidUnit: LiquidUnit, + selected: Boolean, + onClick: (index: Int, Milliliters) -> Unit, + contentBelowItem: @Composable (index: Int) -> Unit +) { + Column( + horizontalAlignment = Alignment.CenterHorizontally + ) { + OutlinedCard( + modifier = modifier, + colors = CardDefaults.outlinedCardColors( + containerColor = if (selected) { + MaterialTheme.colorScheme.surfaceColorAtElevation(24.dp) + } else { + MaterialTheme.colorScheme.surface + } + ), + border = if (selected) { + CardDefaults + .outlinedCardBorder() + .copy(brush = SolidColor(MaterialTheme.colorScheme.primary)) + } else { + CardDefaults.outlinedCardBorder(false) + } + ) { + Column( + modifier = Modifier + .clickable { onClick(index, item) } + .padding(16.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Icon( + modifier = Modifier.size(28.dp), + painter = item.icon(), + contentDescription = null + ) + Text( + text = item.format(liquidUnit), + style = MaterialTheme.typography.labelLarge + ) + } + } + + contentBelowItem(index) + } +} diff --git a/app/src/main/java/at/florianschuster/hydro/ui/base/HydroListItem.kt b/app/src/main/java/at/florianschuster/hydro/ui/base/HydroListItem.kt new file mode 100644 index 0000000..56268f1 --- /dev/null +++ b/app/src/main/java/at/florianschuster/hydro/ui/base/HydroListItem.kt @@ -0,0 +1,27 @@ +package at.florianschuster.hydro.ui.base + +import androidx.compose.material3.ListItem +import androidx.compose.material3.ListItemDefaults +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color + +@Composable +internal fun HydroListItem( + modifier: Modifier = Modifier, + headlineContent: @Composable () -> Unit, + overlineContent: @Composable (() -> Unit)? = null, + leadingContent: @Composable (() -> Unit)? = null, + trailingContent: @Composable (() -> Unit)? = null, + supportingContent: @Composable (() -> Unit)? = null +) { + ListItem( + modifier = modifier, + colors = ListItemDefaults.colors(containerColor = Color.Transparent), + overlineContent = overlineContent, + headlineContent = headlineContent, + leadingContent = leadingContent, + trailingContent = trailingContent, + supportingContent = supportingContent + ) +} diff --git a/app/src/main/java/at/florianschuster/hydro/ui/base/HydroTheme.kt b/app/src/main/java/at/florianschuster/hydro/ui/base/HydroTheme.kt new file mode 100644 index 0000000..a9e0ad5 --- /dev/null +++ b/app/src/main/java/at/florianschuster/hydro/ui/base/HydroTheme.kt @@ -0,0 +1,216 @@ +@file:Suppress("SpellCheckingInspection", "PrivatePropertyName") + +package at.florianschuster.hydro.ui.base + +import android.app.Activity +import android.os.Build +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Typography +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.runtime.SideEffect +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalView +import androidx.compose.ui.text.font.Font +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.core.view.WindowCompat +import at.florianschuster.hydro.R + +private val md_theme_light_primary = Color(0xFF006683) +private val md_theme_light_onPrimary = Color(0xFFFFFFFF) +private val md_theme_light_primaryContainer = Color(0xFFBDE9FF) +private val md_theme_light_onPrimaryContainer = Color(0xFF001F2A) +private val md_theme_light_secondary = Color(0xFF006689) +private val md_theme_light_onSecondary = Color(0xFFFFFFFF) +private val md_theme_light_secondaryContainer = Color(0xFFC3E8FF) +private val md_theme_light_onSecondaryContainer = Color(0xFF001E2C) +private val md_theme_light_tertiary = Color(0xFF00629F) +private val md_theme_light_onTertiary = Color(0xFFFFFFFF) +private val md_theme_light_tertiaryContainer = Color(0xFFD0E4FF) +private val md_theme_light_onTertiaryContainer = Color(0xFF001D34) +private val md_theme_light_error = Color(0xFFBA1A1A) +private val md_theme_light_errorContainer = Color(0xFFFFDAD6) +private val md_theme_light_onError = Color(0xFFFFFFFF) +private val md_theme_light_onErrorContainer = Color(0xFF410002) +private val md_theme_light_background = Color(0xFFFAFCFF) +private val md_theme_light_onBackground = Color(0xFF001F2A) +private val md_theme_light_surface = Color(0xFFFAFCFF) +private val md_theme_light_onSurface = Color(0xFF001F2A) +private val md_theme_light_surfaceVariant = Color(0xFFDCE4E9) +private val md_theme_light_onSurfaceVariant = Color(0xFF40484C) +private val md_theme_light_outline = Color(0xFF70787D) +private val md_theme_light_inverseOnSurface = Color(0xFFE1F4FF) +private val md_theme_light_inverseSurface = Color(0xFF003547) +private val md_theme_light_inversePrimary = Color(0xFF65D3FF) +private val md_theme_light_shadow = Color(0xFF000000) +private val md_theme_light_surfaceTint = Color(0xFF006683) +private val md_theme_light_outlineVariant = Color(0xFFC0C8CD) +private val md_theme_light_scrim = Color(0xFF000000) + +private val md_theme_dark_primary = Color(0xFF65D3FF) +private val md_theme_dark_onPrimary = Color(0xFF003546) +private val md_theme_dark_primaryContainer = Color(0xFF004D64) +private val md_theme_dark_onPrimaryContainer = Color(0xFFBDE9FF) +private val md_theme_dark_secondary = Color(0xFF7AD1FF) +private val md_theme_dark_onSecondary = Color(0xFF003549) +private val md_theme_dark_secondaryContainer = Color(0xFF004C68) +private val md_theme_dark_onSecondaryContainer = Color(0xFFC3E8FF) +private val md_theme_dark_tertiary = Color(0xFF9BCBFF) +private val md_theme_dark_onTertiary = Color(0xFF003256) +private val md_theme_dark_tertiaryContainer = Color(0xFF004A79) +private val md_theme_dark_onTertiaryContainer = Color(0xFFD0E4FF) +private val md_theme_dark_error = Color(0xFFFFB4AB) +private val md_theme_dark_errorContainer = Color(0xFF93000A) +private val md_theme_dark_onError = Color(0xFF690005) +private val md_theme_dark_onErrorContainer = Color(0xFFFFDAD6) +private val md_theme_dark_background = Color(0xFF001F2A) +private val md_theme_dark_onBackground = Color(0xFFBFE9FF) +private val md_theme_dark_surface = Color(0xFF001F2A) +private val md_theme_dark_onSurface = Color(0xFFBFE9FF) +private val md_theme_dark_surfaceVariant = Color(0xFF40484C) +private val md_theme_dark_onSurfaceVariant = Color(0xFFC0C8CD) +private val md_theme_dark_outline = Color(0xFF8A9297) +private val md_theme_dark_inverseOnSurface = Color(0xFF001F2A) +private val md_theme_dark_inverseSurface = Color(0xFFBFE9FF) +private val md_theme_dark_inversePrimary = Color(0xFF006683) +private val md_theme_dark_shadow = Color(0xFF000000) +private val md_theme_dark_surfaceTint = Color(0xFF65D3FF) +private val md_theme_dark_outlineVariant = Color(0xFF40484C) +private val md_theme_dark_scrim = Color(0xFF000000) + +private val seed = Color(0xFFBBE5FA) + +private val LightColors = lightColorScheme( + primary = md_theme_light_primary, + onPrimary = md_theme_light_onPrimary, + primaryContainer = md_theme_light_primaryContainer, + onPrimaryContainer = md_theme_light_onPrimaryContainer, + secondary = md_theme_light_secondary, + onSecondary = md_theme_light_onSecondary, + secondaryContainer = md_theme_light_secondaryContainer, + onSecondaryContainer = md_theme_light_onSecondaryContainer, + tertiary = md_theme_light_tertiary, + onTertiary = md_theme_light_onTertiary, + tertiaryContainer = md_theme_light_tertiaryContainer, + onTertiaryContainer = md_theme_light_onTertiaryContainer, + error = md_theme_light_error, + errorContainer = md_theme_light_errorContainer, + onError = md_theme_light_onError, + onErrorContainer = md_theme_light_onErrorContainer, + background = md_theme_light_background, + onBackground = md_theme_light_onBackground, + surface = md_theme_light_surface, + onSurface = md_theme_light_onSurface, + surfaceVariant = md_theme_light_surfaceVariant, + onSurfaceVariant = md_theme_light_onSurfaceVariant, + outline = md_theme_light_outline, + inverseOnSurface = md_theme_light_inverseOnSurface, + inverseSurface = md_theme_light_inverseSurface, + inversePrimary = md_theme_light_inversePrimary, + surfaceTint = md_theme_light_surfaceTint, + outlineVariant = md_theme_light_outlineVariant, + scrim = md_theme_light_scrim +) + +private val DarkColors = darkColorScheme( + primary = md_theme_dark_primary, + onPrimary = md_theme_dark_onPrimary, + primaryContainer = md_theme_dark_primaryContainer, + onPrimaryContainer = md_theme_dark_onPrimaryContainer, + secondary = md_theme_dark_secondary, + onSecondary = md_theme_dark_onSecondary, + secondaryContainer = md_theme_dark_secondaryContainer, + onSecondaryContainer = md_theme_dark_onSecondaryContainer, + tertiary = md_theme_dark_tertiary, + onTertiary = md_theme_dark_onTertiary, + tertiaryContainer = md_theme_dark_tertiaryContainer, + onTertiaryContainer = md_theme_dark_onTertiaryContainer, + error = md_theme_dark_error, + errorContainer = md_theme_dark_errorContainer, + onError = md_theme_dark_onError, + onErrorContainer = md_theme_dark_onErrorContainer, + background = md_theme_dark_background, + onBackground = md_theme_dark_onBackground, + surface = md_theme_dark_surface, + onSurface = md_theme_dark_onSurface, + surfaceVariant = md_theme_dark_surfaceVariant, + onSurfaceVariant = md_theme_dark_onSurfaceVariant, + outline = md_theme_dark_outline, + inverseOnSurface = md_theme_dark_inverseOnSurface, + inverseSurface = md_theme_dark_inverseSurface, + inversePrimary = md_theme_dark_inversePrimary, + surfaceTint = md_theme_dark_surfaceTint, + outlineVariant = md_theme_dark_outlineVariant, + scrim = md_theme_dark_scrim +) + +private val NotoSansFontFamily = FontFamily( + Font(R.font.notosans_black, FontWeight.Black), + Font(R.font.notosans_bold, FontWeight.Bold), + Font(R.font.notosans_extrabold, FontWeight.ExtraBold), + Font(R.font.notosans_extralight, FontWeight.ExtraLight), + Font(R.font.notosans_light, FontWeight.Light), + Font(R.font.notosans_medium, FontWeight.Medium), + Font(R.font.notosans_regular, FontWeight.Normal), + Font(R.font.notosans_semibold, FontWeight.SemiBold), + Font(R.font.notosans_thin, FontWeight.Thin) +) + +// Set of Material typography styles to start with +private val typography = with(Typography()) { + copy( + displayLarge = displayLarge.copy(fontFamily = NotoSansFontFamily), + displayMedium = displayMedium.copy(fontFamily = NotoSansFontFamily), + displaySmall = displaySmall.copy(fontFamily = NotoSansFontFamily), + headlineLarge = headlineLarge.copy(fontFamily = NotoSansFontFamily), + headlineMedium = headlineMedium.copy(fontFamily = NotoSansFontFamily), + headlineSmall = headlineSmall.copy(fontFamily = NotoSansFontFamily), + titleLarge = titleLarge.copy(fontFamily = NotoSansFontFamily), + titleMedium = titleMedium.copy(fontFamily = NotoSansFontFamily), + titleSmall = titleSmall.copy(fontFamily = NotoSansFontFamily), + bodyLarge = bodyLarge.copy(fontFamily = NotoSansFontFamily), + bodyMedium = bodyMedium.copy(fontFamily = NotoSansFontFamily), + bodySmall = bodySmall.copy(fontFamily = NotoSansFontFamily), + labelLarge = labelLarge.copy(fontFamily = NotoSansFontFamily), + labelMedium = labelMedium.copy(fontFamily = NotoSansFontFamily), + labelSmall = labelSmall.copy(fontFamily = NotoSansFontFamily) + ) +} + +@Composable +fun HydroTheme( + darkTheme: Boolean, + dynamicColor: Boolean, + content: @Composable () -> Unit +) { + val colorScheme = when { + dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { + val context = LocalContext.current + if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + } + + darkTheme -> DarkColors + else -> LightColors + } + val view = LocalView.current + if (!view.isInEditMode) { + SideEffect { + val window = (view.context as Activity).window + with(WindowCompat.getInsetsController(window, view)) { + isAppearanceLightStatusBars = !darkTheme + isAppearanceLightNavigationBars = !darkTheme + } + } + } + + MaterialTheme( + colorScheme = colorScheme, + typography = typography, + content = content + ) +} diff --git a/app/src/main/java/at/florianschuster/hydro/ui/base/Settings.kt b/app/src/main/java/at/florianschuster/hydro/ui/base/Settings.kt new file mode 100644 index 0000000..fa34ec9 --- /dev/null +++ b/app/src/main/java/at/florianschuster/hydro/ui/base/Settings.kt @@ -0,0 +1,109 @@ +package at.florianschuster.hydro.ui.base + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Switch +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.unit.dp + +@Composable +fun SettingsSection( + modifier: Modifier = Modifier, + title: String? = null, + content: @Composable ColumnScope.() -> Unit +) { + Card( + modifier = modifier, + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.95f) + ) + ) { + Column( + modifier = Modifier.padding(vertical = 20.dp), + verticalArrangement = Arrangement.spacedBy(4.dp) + ) { + if (title != null) { + SettingTitle( + modifier = Modifier.padding(horizontal = 24.dp), + name = title + ) + } + content() + } + } +} + +@Composable +private fun SettingTitle( + modifier: Modifier = Modifier, + name: String +) { + Text( + modifier = modifier, + text = name, + style = MaterialTheme.typography.titleLarge + ) +} + +@Composable +fun SettingItem( + modifier: Modifier = Modifier, + icon: @Composable () -> Unit, + fieldName: String, + value: String? = null, + onClick: () -> Unit +) { + HydroListItem( + modifier = modifier + .clickable(onClick = onClick) + .padding(horizontal = 20.dp), + headlineContent = { + Text(text = fieldName, style = MaterialTheme.typography.titleSmall) + }, + supportingContent = { + if (value != null) { + Text( + text = value, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.primary + ) + } + }, + leadingContent = { icon() } + ) +} + +@Composable +fun ToggleSettingItem( + modifier: Modifier = Modifier, + icon: ImageVector, + fieldName: String, + checked: Boolean, + onCheckedChange: (Boolean) -> Unit +) { + HydroListItem( + modifier = modifier.padding(horizontal = 20.dp), + headlineContent = { + Text(text = fieldName, style = MaterialTheme.typography.titleSmall) + }, + leadingContent = { + Icon( + imageVector = icon, + contentDescription = "set $fieldName" + ) + }, + trailingContent = { + Switch(checked = checked, onCheckedChange = onCheckedChange) + } + ) +} diff --git a/app/src/main/java/at/florianschuster/hydro/ui/base/TimePickerDialog.kt b/app/src/main/java/at/florianschuster/hydro/ui/base/TimePickerDialog.kt new file mode 100644 index 0000000..710b8a9 --- /dev/null +++ b/app/src/main/java/at/florianschuster/hydro/ui/base/TimePickerDialog.kt @@ -0,0 +1,85 @@ +package at.florianschuster.hydro.ui.base + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.IntrinsicSize +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.material3.TimePicker +import androidx.compose.material3.rememberTimePickerState +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Dialog +import androidx.compose.ui.window.DialogProperties + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun TimePickerDialog( + title: String = "Select Time", + initialHour: Int = 0, + initialMinute: Int = 0, + onConfirm: (hour: Int, minute: Int) -> Unit, + onCancel: () -> Unit = {} +) { + val state = rememberTimePickerState( + initialHour = initialHour, + initialMinute = initialMinute + ) + Dialog( + onDismissRequest = onCancel, + properties = DialogProperties( + usePlatformDefaultWidth = false + ) + ) { + Surface( + shape = MaterialTheme.shapes.extraLarge, + tonalElevation = 6.dp, + modifier = Modifier + .width(IntrinsicSize.Min) + .height(IntrinsicSize.Min) + .background( + shape = MaterialTheme.shapes.extraLarge, + color = MaterialTheme.colorScheme.surface + ) + ) { + Column( + modifier = Modifier.padding(24.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 20.dp), + text = title, + style = MaterialTheme.typography.labelMedium + ) + + TimePicker(state = state) + Row( + modifier = Modifier + .height(40.dp) + .fillMaxWidth() + ) { + Spacer(modifier = Modifier.weight(1f)) + TextButton( + onClick = onCancel + ) { Text("Cancel") } + TextButton( + onClick = { onConfirm(state.hour, state.minute) } + ) { Text("OK") } + } + } + } + } +} diff --git a/app/src/main/res/drawable/brand_image.webp b/app/src/main/res/drawable/brand_image.webp new file mode 100644 index 0000000..cdaaa8b Binary files /dev/null and b/app/src/main/res/drawable/brand_image.webp differ diff --git a/app/src/main/res/drawable/ic_arrow_range.xml b/app/src/main/res/drawable/ic_arrow_range.xml new file mode 100644 index 0000000..fae2313 --- /dev/null +++ b/app/src/main/res/drawable/ic_arrow_range.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_notification.xml b/app/src/main/res/drawable/ic_notification.xml new file mode 100644 index 0000000..93d3dde --- /dev/null +++ b/app/src/main/res/drawable/ic_notification.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_unit.xml b/app/src/main/res/drawable/ic_unit.xml new file mode 100644 index 0000000..52b53b5 --- /dev/null +++ b/app/src/main/res/drawable/ic_unit.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_water_bottle.xml b/app/src/main/res/drawable/ic_water_bottle.xml new file mode 100644 index 0000000..df8d568 --- /dev/null +++ b/app/src/main/res/drawable/ic_water_bottle.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_water_bottle_large.xml b/app/src/main/res/drawable/ic_water_bottle_large.xml new file mode 100644 index 0000000..0a3f9e1 --- /dev/null +++ b/app/src/main/res/drawable/ic_water_bottle_large.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_water_full.xml b/app/src/main/res/drawable/ic_water_full.xml new file mode 100644 index 0000000..908a6df --- /dev/null +++ b/app/src/main/res/drawable/ic_water_full.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_water_less.xml b/app/src/main/res/drawable/ic_water_less.xml new file mode 100644 index 0000000..a044efb --- /dev/null +++ b/app/src/main/res/drawable/ic_water_less.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_water_medium.xml b/app/src/main/res/drawable/ic_water_medium.xml new file mode 100644 index 0000000..f3f5578 --- /dev/null +++ b/app/src/main/res/drawable/ic_water_medium.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/font/notosans_black.ttf b/app/src/main/res/font/notosans_black.ttf new file mode 100644 index 0000000..05771e5 Binary files /dev/null and b/app/src/main/res/font/notosans_black.ttf differ diff --git a/app/src/main/res/font/notosans_bold.ttf b/app/src/main/res/font/notosans_bold.ttf new file mode 100644 index 0000000..0d19068 Binary files /dev/null and b/app/src/main/res/font/notosans_bold.ttf differ diff --git a/app/src/main/res/font/notosans_extrabold.ttf b/app/src/main/res/font/notosans_extrabold.ttf new file mode 100644 index 0000000..a74c69a Binary files /dev/null and b/app/src/main/res/font/notosans_extrabold.ttf differ diff --git a/app/src/main/res/font/notosans_extralight.ttf b/app/src/main/res/font/notosans_extralight.ttf new file mode 100644 index 0000000..13c4bbc Binary files /dev/null and b/app/src/main/res/font/notosans_extralight.ttf differ diff --git a/app/src/main/res/font/notosans_light.ttf b/app/src/main/res/font/notosans_light.ttf new file mode 100644 index 0000000..8fde662 Binary files /dev/null and b/app/src/main/res/font/notosans_light.ttf differ diff --git a/app/src/main/res/font/notosans_medium.ttf b/app/src/main/res/font/notosans_medium.ttf new file mode 100644 index 0000000..faf167c Binary files /dev/null and b/app/src/main/res/font/notosans_medium.ttf differ diff --git a/app/src/main/res/font/notosans_regular.ttf b/app/src/main/res/font/notosans_regular.ttf new file mode 100644 index 0000000..7552fbe Binary files /dev/null and b/app/src/main/res/font/notosans_regular.ttf differ diff --git a/app/src/main/res/font/notosans_semibold.ttf b/app/src/main/res/font/notosans_semibold.ttf new file mode 100644 index 0000000..b460754 Binary files /dev/null and b/app/src/main/res/font/notosans_semibold.ttf differ diff --git a/app/src/main/res/font/notosans_thin.ttf b/app/src/main/res/font/notosans_thin.ttf new file mode 100644 index 0000000..b5af044 Binary files /dev/null and b/app/src/main/res/font/notosans_thin.ttf differ diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..c9ad5f9 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000..37534d7 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000..0ba583e Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000..79bf8b2 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000..44166d2 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000..6e7df2c Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp differ diff --git a/app/src/main/res/raw/water_dark.json b/app/src/main/res/raw/water_dark.json new file mode 100644 index 0000000..1aae39d --- /dev/null +++ b/app/src/main/res/raw/water_dark.json @@ -0,0 +1 @@ +{"nm":"MC","ddd":0,"h":1080,"w":1080,"meta":{"g":"@lottiefiles/toolkit-js 0.26.1"},"layers":[{"ty":0,"nm":"Wave Animation","sr":1,"st":0,"op":424,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[960,540,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[540,540,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"w":1920,"h":1080,"refId":"comp_0","ind":1}],"v":"5.7.3","fr":60,"op":301,"ip":32,"assets":[{"nm":"","id":"comp_0","layers":[{"ty":3,"nm":"Null 1","sr":1,"st":0,"op":424,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[169,169,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[960,540,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":0,"ix":11}},"ef":[],"ind":1},{"ty":0,"nm":"wave02","sr":1,"st":0,"op":424,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[960,540,0],"ix":1},"s":{"a":0,"k":[116,116,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.833,"y":0.833},"s":[960,1620,0],"t":0,"ti":[0,193.333,0],"to":[0,-193.333,0]},{"s":[960,460,0],"t":227}],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"w":1920,"h":1080,"refId":"comp_1","ind":2},{"ty":0,"nm":"wave04","sr":1,"st":0,"op":424,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[960,540,0],"ix":1},"s":{"a":0,"k":[116,116,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.833,"y":0.833},"s":[960,1620,0],"t":0,"ti":[0,193.333,0],"to":[0,-193.333,0]},{"s":[960,460,0],"t":227}],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[{"ty":0,"mn":"ADBE Ramp","nm":"Gradient Ramp","ix":1,"en":1,"ef":[{"ty":3,"mn":"ADBE Ramp-0001","nm":"Start of Ramp","ix":1,"v":{"a":0,"k":[960,0],"ix":1}},{"ty":2,"mn":"ADBE Ramp-0002","nm":"Start Color","ix":2,"v":{"a":0,"k":[0.8706,0.5725,0.1294],"ix":2}},{"ty":3,"mn":"ADBE Ramp-0003","nm":"End of Ramp","ix":3,"v":{"a":0,"k":[960,1080],"ix":3}},{"ty":2,"mn":"ADBE Ramp-0004","nm":"End Color","ix":4,"v":{"a":0,"k":[1,0.8824,0.7059],"ix":4}},{"ty":7,"mn":"ADBE Ramp-0005","nm":"Ramp Shape","ix":5,"v":{"a":0,"k":1,"ix":5}},{"ty":0,"mn":"ADBE Ramp-0006","nm":"Ramp Scatter","ix":6,"v":{"a":0,"k":0,"ix":6}},{"ty":0,"mn":"ADBE Ramp-0007","nm":"Blend With Original","ix":7,"v":{"a":0,"k":0,"ix":7}},{"ty":6,"mn":"ADBE Ramp-0008","nm":"","ix":8,"v":0}]}],"w":1920,"h":1080,"refId":"comp_2","ind":3},{"ty":0,"nm":"wave03","sr":1,"st":0,"op":424,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[960,540,0],"ix":1},"s":{"a":0,"k":[116,116,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.833,"y":0.833},"s":[960,1620,0],"t":0,"ti":[0,193.333,0],"to":[0,-193.333,0]},{"s":[960,460,0],"t":227}],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"w":1920,"h":1080,"refId":"comp_3","ind":4},{"ty":1,"nm":"bg","sr":1,"st":0,"op":424,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[960,540,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[960,540,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"sc":"#000000","sh":1080,"sw":1920,"ind":5}]},{"nm":"","id":"comp_1","layers":[{"ty":4,"nm":"water","sr":1,"st":0,"op":424,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[181,181,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.833,"y":0.833},"s":[-3109.6,705.07,0],"t":0,"ti":[-1752,40,0],"to":[1752,-40,0]},{"s":[7402.4,465.07,0],"t":227}],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":63,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Rectangle 1","ix":1,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[-267.334,13.961],[-386.024,2.075],[-387.882,-60.547],[-561.726,14.756],[-439.996,11.5],[-654.107,25.214],[-426.676,-4.654]],"o":[[0,0],[0,0],[0,0],[0,0],[231.29,-12.079],[720.469,-3.872],[287.741,44.916],[513.957,-13.501],[196.864,-5.145],[474.188,-18.279],[312.228,3.406]],"v":[[3122.657,-403.154],[3032.719,342.805],[-4858.624,366.136],[-4890.188,-284.814],[-4484.46,-263.518],[-3532.053,-171.856],[-2202.288,-282.386],[-1005.658,-163.965],[156.221,-295.452],[1307.92,-312.023],[2380.712,-276.665]]},"ix":2}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"c":{"a":0,"k":[0,0.6627,1],"ix":3}},{"ty":"gf","bm":0,"hd":false,"mn":"ADBE Vector Graphic - G-Fill","nm":"Gradient Fill 1","e":{"a":0,"k":[-1710,-142],"ix":6},"g":{"p":3,"k":{"a":0,"k":[0,0.3411764705882353,0.8901960784313725,1,0.495,0.21176470588235294,0.48627450980392156,0.7294117647058823,0.99,0.17254901960784313,0.6784313725490196,0.9607843137254902],"ix":9}},"t":1,"a":{"a":0,"k":0},"h":{"a":0,"k":0},"s":{"a":0,"k":[-1706,-427],"ix":5},"r":1,"o":{"a":0,"k":100,"ix":10}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[3.5,-11.5],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":1}]},{"nm":"","id":"comp_2","layers":[{"ty":4,"nm":"water","sr":1,"st":0,"op":424,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[169,169,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.833,"y":0.833},"s":[-3109.6,665.07,0],"t":0,"ti":[-1512,13.333,0],"to":[1512,-13.333,0]},{"s":[5962.4,585.07,0],"t":227}],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":63,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Rectangle 1","ix":1,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[-267.334,13.961],[-514.373,15.366],[-276.818,-2.951],[-356.676,20.338],[-192.117,-4.298],[-435.317,65.022],[-444.201,36.5],[-421.576,-65.955]],"o":[[0,0],[0,0],[0,0],[0,0],[119.969,-6.265],[147.306,-4.4],[308.969,3.294],[316.551,-18.05],[514.005,11.5],[641.417,-95.807],[472.947,-38.862],[778.87,121.852]],"v":[[3191.299,-483.627],[3032.719,342.805],[-4858.624,366.136],[-4888.624,-488.364],[-4895.032,-465.76],[-3631.042,-236.271],[-2730.72,-321.387],[-1656.778,-235.801],[-826.579,-298.876],[240.625,-262.316],[1317.387,-174.745],[2394.911,-177.257]]},"ix":2}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"c":{"a":0,"k":[0,0.6627,1],"ix":3}},{"ty":"gf","bm":0,"hd":false,"mn":"ADBE Vector Graphic - G-Fill","nm":"Gradient Fill 1","e":{"a":0,"k":[-1710,-142],"ix":6},"g":{"p":3,"k":{"a":0,"k":[0,0.21568627450980393,0.5686274509803921,0.8313725490196079,0.5,0,0.4588235294117647,0.7058823529411765,1,0.3411764705882353,0.8901960784313725,1],"ix":9}},"t":1,"a":{"a":0,"k":0},"h":{"a":0,"k":0},"s":{"a":0,"k":[-1706,-427],"ix":5},"r":1,"o":{"a":0,"k":100,"ix":10}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[3.5,-11.5],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":1}]},{"nm":"","id":"comp_3","layers":[{"ty":4,"nm":"water","sr":1,"st":0,"op":424,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[169,169,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.833,"y":0.833},"s":[-3109.6,1345.07,0],"t":0,"ti":[-1512,20,0],"to":[1512,-20,0]},{"s":[5962.4,1225.07,0],"t":227}],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":63,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Rectangle 1","ix":1,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[-267.334,13.961],[-385.992,5.334],[-387.882,-60.547],[-321.535,-7.194],[-439.996,11.5],[-654.107,25.214],[-426.676,-4.654]],"o":[[0,0],[0,0],[0,0],[0,0],[231.29,-12.079],[245.646,-3.395],[570.504,89.054],[514.006,11.5],[196.864,-5.145],[474.188,-18.279],[312.228,3.406]],"v":[[3120.29,-341.615],[3032.719,342.805],[-4858.624,366.136],[-4888.624,-374.755],[-4445.327,-238.541],[-3546.743,-195.277],[-2208.755,-238.645],[-1013.562,-156.864],[221.69,-269.417],[1307.92,-312.023],[2293.137,-222.228]]},"ix":2}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"c":{"a":0,"k":[0,0.6627,1],"ix":3}},{"ty":"gf","bm":0,"hd":false,"mn":"ADBE Vector Graphic - G-Fill","nm":"Gradient Fill 1","e":{"a":0,"k":[-1710,-142],"ix":6},"g":{"p":3,"k":{"a":0,"k":[0,0.03529411764705882,0.7372549019607844,0.9215686274509803,0.5,0.16862745098039217,0.6,0.8392156862745098,1,0.3411764705882353,0.8901960784313725,1],"ix":9}},"t":1,"a":{"a":0,"k":0},"h":{"a":0,"k":0},"s":{"a":0,"k":[-1706,-427],"ix":5},"r":1,"o":{"a":0,"k":100,"ix":10}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[3.5,-451.5],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":1}]}]} \ No newline at end of file diff --git a/app/src/main/res/raw/water_light.json b/app/src/main/res/raw/water_light.json new file mode 100644 index 0000000..842d68a --- /dev/null +++ b/app/src/main/res/raw/water_light.json @@ -0,0 +1 @@ +{"nm":"MC","ddd":0,"h":1080,"w":1080,"meta":{"g":"@lottiefiles/toolkit-js 0.26.1"},"layers":[{"ty":0,"nm":"Wave Animation","sr":1,"st":0,"op":424,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[960,540,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[540,540,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"w":1920,"h":1080,"refId":"comp_0","ind":1}],"v":"5.7.3","fr":60,"op":301,"ip":32,"assets":[{"nm":"","id":"comp_0","layers":[{"ty":3,"nm":"Null 1","sr":1,"st":0,"op":424,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[169,169,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[960,540,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":0,"ix":11}},"ef":[],"ind":1},{"ty":0,"nm":"wave02","sr":1,"st":0,"op":424,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[960,540,0],"ix":1},"s":{"a":0,"k":[116,116,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.833,"y":0.833},"s":[960,1620,0],"t":0,"ti":[0,193.333,0],"to":[0,-193.333,0]},{"s":[960,460,0],"t":227}],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"w":1920,"h":1080,"refId":"comp_1","ind":2},{"ty":0,"nm":"wave04","sr":1,"st":0,"op":424,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[960,540,0],"ix":1},"s":{"a":0,"k":[116,116,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.833,"y":0.833},"s":[960,1620,0],"t":0,"ti":[0,193.333,0],"to":[0,-193.333,0]},{"s":[960,460,0],"t":227}],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[{"ty":0,"mn":"ADBE Ramp","nm":"Gradient Ramp","ix":1,"en":1,"ef":[{"ty":3,"mn":"ADBE Ramp-0001","nm":"Start of Ramp","ix":1,"v":{"a":0,"k":[960,0],"ix":1}},{"ty":2,"mn":"ADBE Ramp-0002","nm":"Start Color","ix":2,"v":{"a":0,"k":[0.8706,0.5725,0.1294],"ix":2}},{"ty":3,"mn":"ADBE Ramp-0003","nm":"End of Ramp","ix":3,"v":{"a":0,"k":[960,1080],"ix":3}},{"ty":2,"mn":"ADBE Ramp-0004","nm":"End Color","ix":4,"v":{"a":0,"k":[1,0.8824,0.7059],"ix":4}},{"ty":7,"mn":"ADBE Ramp-0005","nm":"Ramp Shape","ix":5,"v":{"a":0,"k":1,"ix":5}},{"ty":0,"mn":"ADBE Ramp-0006","nm":"Ramp Scatter","ix":6,"v":{"a":0,"k":0,"ix":6}},{"ty":0,"mn":"ADBE Ramp-0007","nm":"Blend With Original","ix":7,"v":{"a":0,"k":0,"ix":7}},{"ty":6,"mn":"ADBE Ramp-0008","nm":"","ix":8,"v":0}]}],"w":1920,"h":1080,"refId":"comp_2","ind":3},{"ty":0,"nm":"wave03","sr":1,"st":0,"op":424,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[960,540,0],"ix":1},"s":{"a":0,"k":[116,116,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.833,"y":0.833},"s":[960,1620,0],"t":0,"ti":[0,193.333,0],"to":[0,-193.333,0]},{"s":[960,460,0],"t":227}],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"w":1920,"h":1080,"refId":"comp_3","ind":4},{"ty":1,"nm":"bg","sr":1,"st":0,"op":424,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[960,540,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[960,540,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"sc":"#ffffff","sh":1080,"sw":1920,"ind":5}]},{"nm":"","id":"comp_1","layers":[{"ty":4,"nm":"water","sr":1,"st":0,"op":424,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[181,181,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.833,"y":0.833},"s":[-3109.6,705.07,0],"t":0,"ti":[-1752,40,0],"to":[1752,-40,0]},{"s":[7402.4,465.07,0],"t":227}],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":63,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Rectangle 1","ix":1,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[-267.334,13.961],[-386.024,2.075],[-387.882,-60.547],[-561.726,14.756],[-439.996,11.5],[-654.107,25.214],[-426.676,-4.654]],"o":[[0,0],[0,0],[0,0],[0,0],[231.29,-12.079],[720.469,-3.872],[287.741,44.916],[513.957,-13.501],[196.864,-5.145],[474.188,-18.279],[312.228,3.406]],"v":[[3122.657,-403.154],[3032.719,342.805],[-4858.624,366.136],[-4890.188,-284.814],[-4484.46,-263.518],[-3532.053,-171.856],[-2202.288,-282.386],[-1005.658,-163.965],[156.221,-295.452],[1307.92,-312.023],[2380.712,-276.665]]},"ix":2}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"c":{"a":0,"k":[0,0.6627,1],"ix":3}},{"ty":"gf","bm":0,"hd":false,"mn":"ADBE Vector Graphic - G-Fill","nm":"Gradient Fill 1","e":{"a":0,"k":[-1710,-142],"ix":6},"g":{"p":3,"k":{"a":0,"k":[0,0.3411764705882353,0.8901960784313725,1,0.495,0.21176470588235294,0.48627450980392156,0.7294117647058823,0.99,0.17254901960784313,0.6784313725490196,0.9607843137254902],"ix":9}},"t":1,"a":{"a":0,"k":0},"h":{"a":0,"k":0},"s":{"a":0,"k":[-1706,-427],"ix":5},"r":1,"o":{"a":0,"k":100,"ix":10}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[3.5,-11.5],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":1}]},{"nm":"","id":"comp_2","layers":[{"ty":4,"nm":"water","sr":1,"st":0,"op":424,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[169,169,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.833,"y":0.833},"s":[-3109.6,665.07,0],"t":0,"ti":[-1512,13.333,0],"to":[1512,-13.333,0]},{"s":[5962.4,585.07,0],"t":227}],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":63,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Rectangle 1","ix":1,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[-267.334,13.961],[-514.373,15.366],[-276.818,-2.951],[-356.676,20.338],[-192.117,-4.298],[-435.317,65.022],[-444.201,36.5],[-421.576,-65.955]],"o":[[0,0],[0,0],[0,0],[0,0],[119.969,-6.265],[147.306,-4.4],[308.969,3.294],[316.551,-18.05],[514.005,11.5],[641.417,-95.807],[472.947,-38.862],[778.87,121.852]],"v":[[3191.299,-483.627],[3032.719,342.805],[-4858.624,366.136],[-4888.624,-488.364],[-4895.032,-465.76],[-3631.042,-236.271],[-2730.72,-321.387],[-1656.778,-235.801],[-826.579,-298.876],[240.625,-262.316],[1317.387,-174.745],[2394.911,-177.257]]},"ix":2}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"c":{"a":0,"k":[0,0.6627,1],"ix":3}},{"ty":"gf","bm":0,"hd":false,"mn":"ADBE Vector Graphic - G-Fill","nm":"Gradient Fill 1","e":{"a":0,"k":[-1710,-142],"ix":6},"g":{"p":3,"k":{"a":0,"k":[0,0.21568627450980393,0.5686274509803921,0.8313725490196079,0.5,0,0.4588235294117647,0.7058823529411765,1,0.3411764705882353,0.8901960784313725,1],"ix":9}},"t":1,"a":{"a":0,"k":0},"h":{"a":0,"k":0},"s":{"a":0,"k":[-1706,-427],"ix":5},"r":1,"o":{"a":0,"k":100,"ix":10}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[3.5,-11.5],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":1}]},{"nm":"","id":"comp_3","layers":[{"ty":4,"nm":"water","sr":1,"st":0,"op":424,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[169,169,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.833,"y":0.833},"s":[-3109.6,1345.07,0],"t":0,"ti":[-1512,20,0],"to":[1512,-20,0]},{"s":[5962.4,1225.07,0],"t":227}],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":63,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Rectangle 1","ix":1,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[-267.334,13.961],[-385.992,5.334],[-387.882,-60.547],[-321.535,-7.194],[-439.996,11.5],[-654.107,25.214],[-426.676,-4.654]],"o":[[0,0],[0,0],[0,0],[0,0],[231.29,-12.079],[245.646,-3.395],[570.504,89.054],[514.006,11.5],[196.864,-5.145],[474.188,-18.279],[312.228,3.406]],"v":[[3120.29,-341.615],[3032.719,342.805],[-4858.624,366.136],[-4888.624,-374.755],[-4445.327,-238.541],[-3546.743,-195.277],[-2208.755,-238.645],[-1013.562,-156.864],[221.69,-269.417],[1307.92,-312.023],[2293.137,-222.228]]},"ix":2}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"c":{"a":0,"k":[0,0.6627,1],"ix":3}},{"ty":"gf","bm":0,"hd":false,"mn":"ADBE Vector Graphic - G-Fill","nm":"Gradient Fill 1","e":{"a":0,"k":[-1710,-142],"ix":6},"g":{"p":3,"k":{"a":0,"k":[0,0.03529411764705882,0.7372549019607844,0.9215686274509803,0.5,0.16862745098039217,0.6,0.8392156862745098,1,0.3411764705882353,0.8901960784313725,1],"ix":9}},"t":1,"a":{"a":0,"k":0},"h":{"a":0,"k":0},"s":{"a":0,"k":[-1706,-427],"ix":5},"r":1,"o":{"a":0,"k":100,"ix":10}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[3.5,-451.5],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":1}]}]} \ No newline at end of file diff --git a/app/src/main/res/raw/winner.json b/app/src/main/res/raw/winner.json new file mode 100644 index 0000000..a4f54d8 --- /dev/null +++ b/app/src/main/res/raw/winner.json @@ -0,0 +1 @@ +{"v":"5.8.1","fr":30,"ip":0,"op":71,"w":500,"h":500,"nm":"Trophy","ddd":0,"assets":[{"id":"comp_0","nm":"Pre-comp 3","fr":30,"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Pre-comp 2","refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[391.176,345.588,0],"ix":2,"l":2},"a":{"a":0,"k":[50,48.5,0],"ix":1,"l":2},"s":{"a":0,"k":[30,30,100],"ix":6,"l":2}},"ao":0,"w":100,"h":97,"ip":2,"op":17,"st":2,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"Pre-comp 2","refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[344.118,294.118,0],"ix":2,"l":2},"a":{"a":0,"k":[50,48.5,0],"ix":1,"l":2},"s":{"a":0,"k":[50,50,100],"ix":6,"l":2}},"ao":0,"w":100,"h":97,"ip":1,"op":16,"st":1,"bm":0},{"ddd":0,"ind":3,"ty":0,"nm":"Pre-comp 2","refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[151.471,317.647,0],"ix":2,"l":2},"a":{"a":0,"k":[50,48.5,0],"ix":1,"l":2},"s":{"a":0,"k":[30,30,100],"ix":6,"l":2}},"ao":0,"w":100,"h":97,"ip":7,"op":22,"st":7,"bm":0},{"ddd":0,"ind":4,"ty":0,"nm":"Pre-comp 2","refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[104.412,266.176,0],"ix":2,"l":2},"a":{"a":0,"k":[50,48.5,0],"ix":1,"l":2},"s":{"a":0,"k":[50,50,100],"ix":6,"l":2}},"ao":0,"w":100,"h":97,"ip":6,"op":21,"st":6,"bm":0},{"ddd":0,"ind":5,"ty":0,"nm":"Pre-comp 2","refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[342.647,145.588,0],"ix":2,"l":2},"a":{"a":0,"k":[50,48.5,0],"ix":1,"l":2},"s":{"a":0,"k":[30,30,100],"ix":6,"l":2}},"ao":0,"w":100,"h":97,"ip":4,"op":19,"st":4,"bm":0},{"ddd":0,"ind":6,"ty":0,"nm":"Pre-comp 2","refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[295.588,94.118,0],"ix":2,"l":2},"a":{"a":0,"k":[50,48.5,0],"ix":1,"l":2},"s":{"a":0,"k":[50,50,100],"ix":6,"l":2}},"ao":0,"w":100,"h":97,"ip":3,"op":18,"st":3,"bm":0},{"ddd":0,"ind":7,"ty":0,"nm":"Pre-comp 2","refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[133.824,122.059,0],"ix":2,"l":2},"a":{"a":0,"k":[50,48.5,0],"ix":1,"l":2},"s":{"a":0,"k":[30,30,100],"ix":6,"l":2}},"ao":0,"w":100,"h":97,"ip":1,"op":16,"st":1,"bm":0},{"ddd":0,"ind":8,"ty":0,"nm":"Pre-comp 2","refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[179.412,82.353,0],"ix":2,"l":2},"a":{"a":0,"k":[50,48.5,0],"ix":1,"l":2},"s":{"a":0,"k":[50,50,100],"ix":6,"l":2}},"ao":0,"w":100,"h":97,"ip":0,"op":15,"st":0,"bm":0}]},{"id":"comp_1","nm":"Pre-comp 2","fr":30,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 12","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[50.5,47,0],"ix":2,"l":2},"a":{"a":0,"k":[-142.5,-154,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-142.5,-154],[-101.5,-154]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.705882352941,0.247058838489,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":4,"s":[0]},{"t":14,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[0]},{"t":10,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":15,"st":-11,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 11","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":180,"ix":10},"p":{"a":0,"k":[50.5,47,0],"ix":2,"l":2},"a":{"a":0,"k":[-142.5,-154,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-142.5,-154],[-101.5,-154]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.705882352941,0.247058838489,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":4,"s":[0]},{"t":14,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[0]},{"t":10,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":15,"st":-11,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 10","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[50.5,47,0],"ix":2,"l":2},"a":{"a":0,"k":[-142.5,-154,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-142.5,-154],[-101.5,-154]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.705882352941,0.247058838489,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":4,"s":[0]},{"t":14,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[0]},{"t":10,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":15,"st":-11,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 9","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50.5,47,0],"ix":2,"l":2},"a":{"a":0,"k":[-142.5,-154,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-142.5,-154],[-101.5,-154]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.705882352941,0.247058838489,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":4,"s":[0]},{"t":14,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[0]},{"t":10,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":15,"st":-11,"bm":0}]},{"id":"comp_2","nm":"Pre-comp 1","fr":30,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 10","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[178,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.705882352941,0.247058838489,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":4,"s":[60]},{"t":14,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[60]},{"t":10,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 11","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":30,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[178,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.705882352941,0.247058838489,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":4,"s":[60]},{"t":14,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[60]},{"t":10,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 12","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":60,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[178,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.705882352941,0.247058838489,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":4,"s":[60]},{"t":14,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[60]},{"t":10,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 13","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[178,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.705882352941,0.247058838489,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":4,"s":[60]},{"t":14,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[60]},{"t":10,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Shape Layer 14","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":120,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[178,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.705882352941,0.247058838489,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":4,"s":[60]},{"t":14,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[60]},{"t":10,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Shape Layer 15","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":150,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[178,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.705882352941,0.247058838489,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":4,"s":[60]},{"t":14,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[60]},{"t":10,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Shape Layer 16","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":180,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[178,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.705882352941,0.247058838489,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":4,"s":[60]},{"t":14,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[60]},{"t":10,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Shape Layer 17","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":210,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[178,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.705882352941,0.247058838489,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":4,"s":[60]},{"t":14,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[60]},{"t":10,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"Shape Layer 18","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":240,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[178,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.705882352941,0.247058838489,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":4,"s":[60]},{"t":14,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[60]},{"t":10,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Shape Layer 19","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":270,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[178,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.705882352941,0.247058838489,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":4,"s":[60]},{"t":14,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[60]},{"t":10,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"Shape Layer 21","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":300,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[178,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.705882352941,0.247058838489,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":3,"s":[60]},{"t":13,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[60]},{"t":10,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":"Shape Layer 20","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":330,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[178,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.705882352941,0.247058838489,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":3,"s":[60]},{"t":13,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[60]},{"t":10,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":300,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Pre-comp 3","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2,"l":2},"a":{"a":0,"k":[250,250,0],"ix":1,"l":2},"s":{"a":0,"k":[-100,100,100],"ix":6,"l":2}},"ao":0,"w":500,"h":500,"ip":39,"op":61,"st":39,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"Pre-comp 3","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2,"l":2},"a":{"a":0,"k":[250,250,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":500,"h":500,"ip":24,"op":46,"st":24,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Cup 3","parent":14,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0.371,-98.838,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.8,"y":1},"o":{"x":0.333,"y":0},"t":10,"s":[{"i":[[-11.815,0],[0,0],[1.176,-11.756],[0,0],[5.492,54.916],[0,0]],"o":[[0,0],[11.815,0],[0,0],[-5.492,54.916],[0,0],[-1.176,-11.756]],"v":[[-49.8,-128.285],[49.3,-128.285],[70.626,-106.958],[62.096,-21.652],[-62.596,-21.652],[-71.126,-106.958]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.2,"y":0},"t":14,"s":[{"i":[[0,6.785],[0,0],[0,-11.667],[0,0],[0,55.777],[0,0]],"o":[[0,0],[0,8.035],[0,0],[0,54.652],[0,0],[0,-12.042]],"v":[[-0.25,-128.285],[-0.25,-128.285],[-0.25,-106.958],[-0.25,-21.652],[-0.25,-21.652],[-0.25,-106.958]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":18,"s":[{"i":[[-11.815,0],[0,0],[1.176,-11.756],[0,0],[5.492,54.916],[0,0]],"o":[[0,0],[11.815,0],[0,0],[-5.492,54.916],[0,0],[-1.176,-11.756]],"v":[[-49.8,-128.285],[49.3,-128.285],[70.626,-106.958],[62.096,-21.652],[-62.596,-21.652],[-71.126,-106.958]],"c":true}]},{"i":{"x":1,"y":1},"o":{"x":0.333,"y":0},"t":24,"s":[{"i":[[-11.815,0],[0,0],[1.176,-11.756],[0,0],[5.492,54.916],[0,0]],"o":[[0,0],[11.815,0],[0,0],[-5.492,54.916],[0,0],[-1.176,-11.756]],"v":[[-49.8,-128.285],[49.3,-128.285],[70.626,-106.958],[62.096,-21.652],[-62.596,-21.652],[-71.126,-106.958]],"c":true}]},{"i":{"x":0.223,"y":1},"o":{"x":0.2,"y":0},"t":31,"s":[{"i":[[0,6.785],[0,0],[0,-11.667],[0,0],[0,55.777],[0,0]],"o":[[0,0],[0,8.035],[0,0],[0,54.652],[0,0],[0,-12.042]],"v":[[-0.25,-128.285],[-0.25,-128.285],[-0.25,-106.958],[-0.25,-21.652],[-0.25,-21.652],[-0.25,-106.958]],"c":true}]},{"t":50,"s":[{"i":[[-11.815,0],[0,0],[1.176,-11.756],[0,0],[5.492,54.916],[0,0]],"o":[[0,0],[11.815,0],[0,0],[-5.492,54.916],[0,0],[-1.176,-11.756]],"v":[[-49.8,-128.285],[49.3,-128.285],[70.626,-106.958],[62.096,-21.652],[-62.596,-21.652],[-71.126,-106.958]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.705882370472,0.247058823705,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Cup","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":31,"op":310,"st":10,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 7","tt":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"t":24,"s":[{"i":[[3.191,-0.395],[2.304,-0.927],[2.095,-1.709],[1.788,-1.877],[0.908,-1.912],[-1.334,-6.312],[-2.779,-4.188],[-3.401,-3.602],[-3.548,-3.297],[-2.312,-2.352],[-2.506,-2.506],[-2.476,-2.535],[-0.232,-1.997],[0.723,-0.831],[0.267,-1.304],[-2.88,-0.857],[1.3,9.712],[4.203,4.76],[9.453,16.328],[-0.295,3.28],[-3.343,1.249],[-4.023,-0.951],[-1.8,-0.768],[-8.286,2.069],[-0.398,3.182],[3.129,3.445],[1.614,1.176],[1.189,0.657],[2.306,0.956],[2.086,0.582]],"o":[[-4.689,0.581],[-2.304,0.927],[-1.938,1.582],[-1.788,1.877],[-3.116,6.566],[1.334,6.312],[2.849,4.294],[3.401,3.602],[2.244,2.084],[2.312,2.352],[2.864,2.864],[2.476,2.535],[0.16,1.372],[-0.723,0.831],[-1.339,6.557],[13.183,3.921],[-1.018,-7.607],[-12.335,-13.97],[-1.509,-2.606],[0.413,-4.602],[3.955,-1.477],[2.275,0.538],[8.878,3.789],[3.458,-0.863],[0.467,-3.729],[-1.703,-1.875],[-1.654,-1.205],[-1.861,-1.028],[-2.371,-0.983],[-7.691,-2.147]],"v":[[-93,-111],[-102.945,-108.846],[-109,-105],[-114.773,-99.748],[-119,-94],[-120.921,-74.216],[-114,-58],[-104.525,-46.252],[-94,-36],[-87.197,-29.316],[-80,-22],[-71.526,-13.849],[-67,-7],[-68.18,-3.949],[-70,-1],[-64,12],[-47,-11],[-59,-30],[-99,-72],[-102,-81],[-94,-91],[-82,-91],[-75,-89],[-55,-79],[-48,-87],[-53,-98],[-58,-101],[-62,-105],[-69,-107],[-75,-110]],"c":true}],"h":1},{"t":25,"s":[{"i":[[6.363,-1.468],[2.979,-2.095],[1.84,-2.639],[0.408,-1.067],[0.408,-1.35],[0.465,-0.387],[0.082,-0.263],[-3.965,-7.542],[-4.029,-4.555],[-0.766,-0.479],[-0.438,-0.523],[-0.104,-0.568],[-0.27,-0.353],[-0.859,-0.529],[-0.842,-0.709],[-4.878,-5.799],[-0.092,-0.71],[0.419,-2.677],[-6.464,-0.238],[-1.53,2.112],[6.189,7.171],[5.82,5.82],[5.515,7.127],[-5.296,5.528],[-9.204,-2.345],[-3.834,-2.514],[-5.231,0.751],[-0.822,3.258],[5.25,2.566],[1.551,0.608]],"o":[[-4.243,0.979],[-2.98,2.096],[-0.962,1.38],[-0.408,1.067],[-0.057,0.189],[-0.465,0.387],[-2.889,9.276],[3.965,7.542],[0.497,0.561],[0.766,0.479],[0.313,0.374],[0.104,0.568],[1.053,1.378],[1.068,0.657],[6.494,5.469],[1.271,1.511],[0.356,2.738],[-1.191,7.598],[4.588,0.169],[8.605,-11.877],[-4.677,-5.419],[-6.151,-6.151],[-4.119,-5.322],[4.622,-4.825],[3.701,0.943],[4.525,2.967],[3.142,-0.451],[2.691,-10.661],[-1.826,-0.892],[-7.754,-3.037]],"v":[[-95,-111],[-105.802,-106.245],[-113,-99],[-114.915,-95.478],[-116,-92],[-116.981,-91.056],[-118,-90],[-114.689,-64.459],[-101,-46],[-98.956,-44.471],[-97,-43],[-96.468,-41.485],[-96,-40],[-92,-37],[-90,-35],[-71,-17],[-65,-8],[-68,-1],[-59,12],[-49,6],[-55,-29],[-72,-45],[-91,-66],[-97,-87],[-77,-91],[-66,-85],[-54,-79],[-46,-86],[-62,-106],[-67,-109]],"c":true}],"h":1},{"t":26,"s":[{"i":[[1.111,-0.113],[2.585,-1.009],[1.674,-1.559],[0.573,-0.084],[0.356,-0.336],[0.475,-0.926],[0.564,-0.79],[0.36,-0.281],[0.285,-0.493],[0.871,-2.718],[0.063,-2.618],[-3.733,-5.588],[-2.015,-2.521],[-0.872,-1.07],[-1.274,-1.411],[-7.648,-7.648],[-0.543,-5.007],[0.55,-2.542],[-2.362,-2.153],[-1.562,7.645],[2.913,4.566],[2.86,3.478],[9.12,11.123],[-1.11,7.282],[-2.157,0.726],[-4.835,-3.467],[-4.962,0.362],[-0.24,6.416],[7.573,3.176],[2.407,0.544]],"o":[[-3.987,0.405],[-2.585,1.009],[-0.352,0.328],[-0.573,0.084],[-0.536,0.507],[-0.475,0.926],[-0.273,0.382],[-0.36,0.281],[-1.452,2.508],[-0.871,2.718],[-0.253,10.508],[1.754,2.625],[0.961,1.203],[1.009,1.238],[6.895,7.635],[5.268,5.268],[0.253,2.337],[-0.918,4.241],[8.175,7.452],[1.806,-8.842],[-2.806,-4.398],[-8.695,-10.574],[-5.23,-6.378],[0.863,-5.666],[7.845,-2.641],[3.765,2.699],[4.671,-0.341],[0.327,-8.737],[-3.3,-1.384],[-5.338,-1.207]],"v":[[-85,-112],[-94.735,-109.865],[-101,-106],[-102.498,-105.506],[-104,-105],[-105.479,-102.712],[-107,-100],[-107.991,-99.083],[-109,-98],[-112.542,-90.083],[-114,-82],[-106,-58],[-100,-51],[-98,-47],[-94,-44],[-75,-23],[-63,-8],[-65,-1],[-63,9],[-43,-1],[-48,-23],[-58,-35],[-84,-62],[-94,-83],[-86,-92],[-64,-86],[-52,-79],[-43,-89],[-63,-108],[-72,-112]],"c":true}],"h":1},{"t":27,"s":[{"i":[[2.317,-0.535],[3.86,-4.04],[1.242,-4.613],[-2.12,-5.938],[-2.373,-3.37],[-0.492,-0.639],[-0.459,-0.632],[-0.8,-1.421],[-0.923,-1.114],[-0.951,-0.655],[-0.472,-0.507],[-0.081,-0.566],[-0.34,-0.374],[-1.019,-0.973],[-0.936,-1.02],[-0.997,-1.18],[-0.954,-1.127],[-0.458,-4.534],[0.591,-1.92],[-7.875,-0.144],[-0.943,9.517],[4.79,6.294],[7.791,10.593],[-2.16,8.125],[-1.994,0.531],[-4.781,-3.242],[-0.608,-0.61],[-0.949,-0.628],[-0.752,8.522],[12.133,3.114]],"o":[[-5.317,1.227],[-3.86,4.04],[-1.851,6.879],[2.12,5.938],[0.571,0.811],[0.492,0.639],[0.875,1.203],[0.8,1.421],[0.723,0.873],[0.951,0.655],[0.337,0.361],[0.081,0.566],[0.99,1.09],[1.018,0.973],[1.058,1.152],[0.997,1.18],[3.481,4.111],[0.271,2.68],[-2.255,7.329],[7.212,0.132],[1.112,-11.222],[-8.19,-10.762],[-4.476,-6.085],[0.814,-3.063],[5.149,-1.372],[0.641,0.434],[1.172,1.175],[7.025,4.649],[0.85,-9.635],[-5.404,-1.387]],"v":[[-82,-111],[-96.057,-102.54],[-104,-89],[-102.668,-69.368],[-95,-55],[-93.416,-52.866],[-92,-51],[-89.536,-46.934],[-87,-43],[-84.312,-40.725],[-82,-39],[-81.502,-37.509],[-81,-36],[-77.959,-32.947],[-75,-30],[-71.923,-26.481],[-69,-23],[-58,-8],[-60,-2],[-51,12],[-38,-5],[-48,-30],[-76,-62],[-84,-85],[-77,-92],[-60,-87],[-57,-85],[-55,-81],[-38,-88],[-65,-111]],"c":true}],"h":1},{"t":28,"s":[{"i":[[9.692,-1.405],[1.755,-0.653],[1.212,-0.989],[0.612,-0.799],[1.064,-1.588],[0.956,-1.834],[0.829,-2.651],[-1.471,-5.599],[-2.124,-4.18],[-2.534,-3.99],[-2.617,-3.613],[-1.064,-1.14],[-0.856,-1.216],[-1.667,-2.958],[-0.212,-2.155],[0.684,-2.633],[-1.441,-2.449],[-1.846,-1.035],[-3.412,0.49],[-1.764,4.555],[0.994,6.213],[1.271,2.573],[1.579,2.614],[5.214,6.995],[2.346,5.732],[-9.454,1.216],[-1.712,-1.097],[-10.861,3.613],[-0.352,2.926],[4.889,4.64]],"o":[[-2.767,0.401],[-1.755,0.653],[-1.489,1.215],[-0.612,0.799],[-1.348,2.013],[-0.956,1.834],[-2.008,6.423],[1.471,5.599],[2.249,4.426],[2.534,3.99],[0.888,1.226],[1.064,1.14],[2.12,3.012],[1.667,2.958],[0.297,3.021],[-0.684,2.633],[0.206,0.35],[1.846,1.035],[3.939,-0.566],[1.764,-4.555],[-0.335,-2.095],[-1.271,-2.573],[-3.799,-6.286],[-5.214,-6.995],[-3.248,-7.936],[1.752,-0.225],[5.75,3.685],[2.926,-0.973],[0.526,-4.371],[-6.865,-6.516]],"v":[[-63,-111],[-69.666,-109.441],[-74,-107],[-76.82,-104.28],[-79,-101],[-82.389,-95.478],[-85,-89],[-85.099,-70.817],[-79,-56],[-71.776,-43.39],[-64,-32],[-60.976,-28.493],[-58,-25],[-52.068,-15.857],[-49,-8],[-50.358,0.429],[-50,8],[-46.904,10.63],[-39,12],[-30.301,3.736],[-29,-13],[-31.567,-20.11],[-36,-28],[-50.589,-48.416],[-63,-68],[-59,-92],[-53,-89],[-34,-79],[-28,-87],[-36,-101]],"c":true}],"h":1},{"t":29,"s":[{"i":[[-2.683,7.317],[-0.869,-1.25],[-0.8,-0.658],[-1.031,-0.204],[-1.563,0.111],[-1.735,1.264],[-0.45,1.828],[3.728,4.807],[2.323,0.76],[3.865,-2.557],[1.585,-3.719],[-0.715,-8.537],[-2.363,-6.29],[-1.877,-4.04],[-1.844,-4.454],[-1.186,-3.102],[-0.134,-2.887],[0.496,-1.449],[0.053,-1.394],[-1.552,-2.05],[-2.93,-0.213],[-1.523,0.346],[-0.883,0.689],[-0.327,1.358],[-0.442,1.593],[1.215,6.113],[1.85,4.387],[0.684,1.501],[0.533,1.264],[2.41,7.866]],"o":[[1.239,1.98],[0.869,1.25],[0.8,0.658],[1.031,0.204],[1.61,-0.114],[1.735,-1.264],[1.234,-5.011],[-3.728,-4.807],[-5.956,-1.948],[-3.865,2.557],[-3.224,7.564],[0.715,8.537],[1.648,4.387],[1.877,4.04],[1.12,2.706],[1.186,3.102],[0.038,0.811],[-0.496,1.449],[-0.136,3.585],[1.552,2.05],[1.025,0.075],[1.523,-0.346],[1.249,-0.974],[0.327,-1.358],[1.71,-6.16],[-1.215,-6.113],[-0.73,-1.733],[-0.684,-1.501],[-3.044,-7.221],[-2.41,-7.866]],"v":[[-33,-87],[-29.914,-82.19],[-27.486,-79.364],[-24.816,-78.105],[-21,-78],[-15.63,-80.215],[-12,-85],[-17.332,-100.689],[-28,-110],[-42.778,-108.25],[-51,-98],[-54.191,-73.044],[-49,-50],[-43.647,-37.55],[-38,-25],[-34.26,-16.136],[-32,-7],[-32.932,-3.437],[-34,1],[-31.799,9.529],[-25,13],[-20.893,12.572],[-17,11],[-14.895,7.464],[-14,3],[-13.83,-15.829],[-19,-32],[-21.148,-36.851],[-23,-41],[-32.295,-63.928]],"c":true}],"h":1},{"t":30,"s":[{"i":[[3.333,-0.976],[1.133,-1.074],[0.545,-1.572],[0.264,-2.087],[0.291,-2.619],[-0.238,-6.751],[-0.732,-6.624],[-0.753,-6.186],[-0.301,-5.438],[0.011,-2.415],[-0.042,-2.238],[-0.282,-1.704],[-0.709,-0.814],[-1.027,-0.436],[-1.371,-0.226],[-1.409,0.198],[-1.139,0.835],[0.122,7.953],[0.958,8.662],[0.318,3.74],[0.375,3.844],[0.571,4.011],[-0.7,2.69],[-0.723,0.668],[-0.748,0.937],[-0.514,0.926],[-0.22,1.386],[0.663,2.516],[0.717,1.6],[2.195,1.193]],"o":[[-2.029,0.594],[-1.133,1.074],[-0.545,1.572],[-0.264,2.087],[-0.729,6.568],[0.238,6.751],[0.732,6.624],[0.753,6.186],[0.124,2.236],[-0.011,2.415],[0.042,2.238],[0.282,1.704],[0.377,0.433],[1.027,0.436],[1.371,0.226],[1.409,-0.198],[3.447,-2.528],[-0.122,-7.953],[-0.353,-3.197],[-0.318,-3.74],[-0.416,-4.266],[-0.571,-4.011],[0.361,-1.39],[0.723,-0.668],[0.726,-0.91],[0.514,-0.926],[0.37,-2.338],[-0.663,-2.516],[-1.885,-4.206],[-2.195,-1.193]],"v":[[-11,-109],[-15.667,-106.502],[-18.108,-102.537],[-19.245,-97.054],[-20,-90],[-20.619,-69.944],[-19.046,-49.805],[-16.7,-30.513],[-15,-13],[-14.877,-5.936],[-14.877,1.132],[-14.438,7.133],[-13,11],[-10.818,12.357],[-7.145,13.403],[-2.899,13.497],[1,12],[5.304,-4.9],[3,-31],[2.016,-41.514],[1,-53],[-0.837,-65.682],[-1,-76],[0.711,-78.84],[3,-81],[4.88,-83.643],[6,-87],[5.316,-94.554],[3,-101],[-2.914,-108.886]],"c":true}],"h":1},{"t":31,"s":[{"i":[[3.441,-0.647],[1.283,-0.953],[0.833,-1.422],[0.508,-1.758],[0.31,-1.96],[-0.087,-1.457],[-0.408,-1.039],[-0.617,-0.938],[-0.714,-1.153],[-0.141,-1.047],[0.131,-1.095],[0.206,-1.125],[0.086,-1.137],[0.415,-3.836],[0.451,-3.58],[0.436,-3.579],[0.369,-3.834],[0.163,-4.005],[-0.565,-3.399],[-1.82,-1.944],[-3.604,0.359],[-1.357,1.89],[-0.311,2.89],[0.076,3.305],[-0.195,3.135],[-0.864,6.178],[-0.881,7.07],[-0.327,7.107],[0.798,6.288],[2.455,2.453]],"o":[[-1.859,0.35],[-1.283,0.953],[-0.833,1.422],[-0.508,1.758],[-0.346,2.191],[0.087,1.457],[0.408,1.039],[0.617,0.938],[0.608,0.981],[0.141,1.047],[-0.131,1.095],[-0.206,1.125],[-0.328,4.346],[-0.415,3.836],[-0.451,3.58],[-0.436,3.579],[-0.363,3.764],[-0.163,4.005],[0.565,3.399],[1.82,1.944],[3.06,-0.305],[1.357,-1.89],[0.311,-2.89],[-0.076,-3.305],[0.275,-4.432],[0.864,-6.178],[0.881,-7.07],[0.327,-7.107],[-0.741,-5.836],[-2.455,-2.453]],"v":[[11,-109],[6.318,-107.013],[3.176,-103.416],[1.196,-98.612],[0,-93],[-0.361,-87.608],[0.409,-83.943],[1.975,-81.057],[4,-78],[5.074,-74.953],[5.041,-71.735],[4.487,-68.399],[4,-65],[2.874,-52.791],[1.563,-41.731],[0.22,-31.056],[-1,-20],[-1.921,-8.134],[-1.45,3.184],[1.995,11.41],[10,14],[16.461,10.561],[18.798,3.245],[18.986,-6.194],[19,-16],[20.851,-32.129],[23.61,-52.216],[25.564,-73.694],[25,-94],[20.025,-106.362]],"c":true}],"h":1},{"t":32,"s":[{"i":[[10.012,-0.857],[0.663,-0.066],[0.696,-0.133],[0.685,-0.25],[0.629,-0.419],[1.161,-1.785],[0.997,-2.24],[0.588,-2.086],[-0.068,-1.321],[-1.507,-1.219],[-0.877,-0.316],[-1.658,-0.093],[-0.11,-0.173],[0.768,-3.552],[0.803,-3.414],[1.144,-4.168],[1.192,-4.1],[1.234,-4.969],[-0.056,-4.277],[-2.293,-3.72],[-5.767,1.698],[-1.257,0.847],[-0.419,0.742],[0.306,2.867],[-0.16,2.647],[-0.757,2.689],[-0.84,3.026],[-1.59,5.162],[-1.1,4.811],[1.605,11.555]],"o":[[-0.585,0.05],[-0.663,0.066],[-0.696,0.133],[-0.685,0.25],[-1.079,0.718],[-1.161,1.785],[-0.997,2.24],[-0.588,2.086],[0.093,1.806],[1.507,1.219],[1.258,0.453],[1.658,0.093],[0.876,1.379],[-0.768,3.552],[-1.23,5.23],[-1.144,4.168],[-1.119,3.847],[-1.234,4.969],[0.062,4.817],[2.293,3.72],[-0.445,0.131],[1.257,-0.847],[1.063,-1.884],[-0.306,-2.867],[0.134,-2.219],[0.757,-2.689],[1.538,-5.541],[1.59,-5.162],[2.433,-10.639],[-1.605,-11.555]],"v":[[27,-109],[25.116,-108.839],[23.066,-108.553],[20.982,-107.991],[19,-107],[15.579,-103.093],[12.28,-96.903],[9.841,-90.262],[9,-85],[11.912,-80.383],[16,-78],[20.861,-77.29],[24,-77],[23.76,-69.026],[21,-58],[17.471,-44.153],[14,-32],[10.119,-18.323],[8,-4],[11.222,9.887],[23,14],[24.852,12.655],[28,10],[28.678,2.572],[28,-6],[29.471,-13.395],[32,-22],[36.828,-38.047],[41,-53],[43.334,-89.622]],"c":true}],"h":1},{"t":33,"s":[{"i":[[-1.692,-0.766],[1.471,-6.219],[2.553,-5.943],[1.166,-2.647],[1.155,-2.664],[1.283,-2.686],[0.627,-2.395],[-0.895,-5.6],[-2.8,-1.331],[-2.452,0.856],[-0.736,1.221],[0.438,2.954],[-0.248,2.689],[-1.182,2.599],[-0.96,2.214],[-0.473,1.359],[-0.575,1.331],[-0.379,0.478],[-0.227,0.489],[-0.098,0.91],[-0.285,0.66],[-1.26,2.459],[-0.88,2.66],[-0.699,3.206],[-0.248,3.282],[3.214,7.139],[7.793,-0.124],[1.212,-0.836],[-16.354,-1.393],[-1.724,3.235]],"o":[[1.434,6.183],[-1.471,6.218],[-1.18,2.747],[-1.166,2.647],[-1.24,2.86],[-1.283,2.686],[-1.379,5.271],[0.895,5.6],[3.456,1.643],[2.452,-0.856],[1.506,-2.497],[-0.438,-2.954],[0.312,-3.39],[1.182,-2.599],[0.535,-1.233],[0.473,-1.359],[0.258,-0.599],[0.379,-0.478],[0.325,-0.701],[0.098,-0.91],[1.079,-2.504],[1.26,-2.459],[0.958,-2.895],[0.698,-3.206],[0.647,-8.57],[-3.214,-7.139],[-2.471,0.04],[-7.887,5.438],[7.4,0.63],[0.183,-0.343]],"v":[[38,-85],[37.49,-66.32],[31,-48],[27.481,-39.938],[24,-32],[20.04,-23.651],[17,-16],[16.366,1.455],[22,13],[31.04,13.648],[36,10],[36.944,1.644],[36,-7],[38.514,-15.882],[42,-23],[43.47,-26.927],[45,-31],[46.023,-32.582],[47,-34],[47.53,-36.53],[48,-39],[51.649,-46.383],[55,-54],[57.532,-63.209],[59,-73],[55.33,-98.02],[39,-110],[28,-106],[26,-77],[36,-83]],"c":true}],"h":1},{"t":34,"s":[{"i":[[14.095,-1.272],[0.937,-0.322],[0.989,-0.507],[0.677,-0.062],[0.614,-0.406],[1.107,-1.421],[0.922,-1.224],[0.408,-0.14],[0.201,-0.216],[0.756,-2.55],[-0.329,-1.4],[-2.06,-1.454],[-3.378,0.441],[-2.23,3.005],[-1.843,-1.444],[-0.405,-1.932],[-0.025,-1.995],[0.392,-1.914],[0.456,-1.6],[2.132,-3.599],[2.472,-4.571],[0.616,-1.544],[0.771,-1.406],[0.656,-1.147],[0.679,-1.473],[-14.013,-1.683],[-0.906,4.713],[-0.286,3.517],[-3.246,5.959],[-1.098,19.932]],"o":[[-2.264,0.204],[-0.937,0.322],[-0.678,0.347],[-0.677,0.062],[-1.651,1.092],[-1.107,1.421],[-0.169,0.224],[-0.407,0.14],[-1.305,1.401],[-0.756,2.55],[0.382,1.624],[2.06,1.454],[3.235,-0.423],[2.23,-3.005],[0.353,0.276],[0.405,1.932],[0.022,1.745],[-0.392,1.914],[-2.002,7.031],[-2.132,3.6],[-0.767,1.416],[-0.616,1.544],[-0.631,1.151],[-0.757,1.324],[-5.008,10.858],[6.28,0.754],[0.78,-4.06],[0.405,-4.988],[8.39,-15.404],[1.187,-21.552]],"v":[[47,-109],[42.544,-108.226],[40,-107],[37.952,-106.544],[36,-106],[31.953,-102.099],[29,-98],[28.025,-97.494],[27,-97],[23.774,-90.499],[23,-84],[26.753,-78.951],[35,-77],[43.044,-83.9],[49,-88],[50.246,-84.288],[51,-78],[50.358,-72.392],[49,-67],[42.853,-52.155],[36,-41],[34.003,-36.492],[32,-32],[29,-29],[27,-24],[32,14],[44,6],[42,-6],[49,-23],[71,-75]],"c":true}],"h":1},{"t":35,"s":[{"i":[[8.043,-0.886],[1.416,-0.578],[2.278,-1.352],[1.236,-0.627],[0.831,-0.763],[0.311,-0.547],[0.347,-0.438],[0.627,-2.982],[-3.135,-2.767],[-3.115,2.252],[-2.844,2.136],[-1.146,-0.796],[-0.7,-3.858],[2.241,-5.103],[2.163,-3.381],[0.336,-0.745],[0.319,-0.495],[4.168,-7.075],[0.392,-6.585],[-1.84,-4.18],[-4.893,0],[-1.309,1.377],[-0.485,1.491],[0.519,2.006],[-0.269,2.498],[-1.51,2.797],[-1.745,2.835],[-3.084,4.604],[-2.081,10.716],[5.726,6.344]],"o":[[-3.056,0.337],[-1.416,0.578],[-1.225,0.727],[-1.236,0.627],[-0.408,0.374],[-0.311,0.547],[-2.626,3.307],[-0.627,2.981],[4.694,4.144],[3.115,-2.252],[2.716,-2.041],[1.146,0.796],[1.126,6.206],[-2.241,5.103],[-0.337,0.526],[-0.336,0.745],[-3.936,6.116],[-4.168,7.075],[-0.255,4.279],[1.841,4.18],[3.279,0],[1.309,-1.377],[0.806,-2.478],[-0.519,-2.006],[0.213,-1.978],[1.511,-2.797],[3.564,-5.791],[5.859,-8.748],[2.529,-13.025],[-5.149,-5.706]],"v":[[54,-109],[47.916,-107.762],[43,-105],[39.204,-103.026],[36,-101],[34.955,-99.548],[34,-98],[28.68,-88.595],[32,-80],[43.387,-78.79],[52,-87],[57.513,-88.924],[60,-82],[57.466,-64.882],[50,-52],[48.986,-49.976],[48,-48],[34.842,-27.852],[27,-7],[29.139,6.709],[39,14],[45.596,11.618],[48,7],[47.903,0.515],[47,-6],[49.851,-13.357],[55,-22],[67,-40],[80,-70],[72,-102]],"c":true}],"h":1},{"t":36,"s":[{"i":[[6.95,-0.74],[2.905,-1.098],[1.338,-1.282],[2.561,-3.354],[-1.377,-4.54],[-1.524,-1.099],[-2.451,-0.109],[-1.063,0.784],[-0.925,0.989],[-2.672,1.205],[-0.427,0.231],[-1.115,-0.377],[-0.221,-2.138],[2.655,-4.292],[0.852,-1.278],[0.353,-0.47],[0.554,-0.76],[0.376,-0.501],[0.552,-0.759],[1.638,-2.261],[2.119,-8.747],[-11.571,0.349],[0.102,-0.212],[-0.6,5.533],[-3.865,5.614],[-3.36,5.017],[-2.959,7.022],[-0.68,2.933],[8.858,3.993],[0.432,0.229]],"o":[[-3.041,0.324],[-2.905,1.098],[-2.132,2.042],[-2.561,3.354],[0.454,1.496],[1.524,1.099],[2.821,0.126],[1.064,-0.784],[2.484,-2.655],[0.357,-0.161],[1.608,-0.869],[1.776,0.601],[0.807,7.818],[-0.965,1.561],[-0.298,0.447],[-0.636,0.848],[-0.344,0.472],[-0.635,0.846],[-1.758,2.419],[-5.704,7.876],[-2.125,8.771],[5.64,-0.17],[2.416,-5.024],[0.401,-3.692],[4.493,-6.525],[4.477,-6.685],[1.175,-2.788],[3.523,-15.198],[-0.36,-0.162],[-3.543,-1.875]],"v":[[60,-109],[50.723,-106.719],[44,-103],[35.868,-94.873],[33,-83],[36.002,-78.96],[42,-77],[47.422,-78.164],[50,-81],[56,-86],[57,-88],[64,-90],[69,-80],[60,-57],[58,-52],[56,-51],[55,-48],[53,-47],[52,-44],[46,-37],[32,-11],[44,14],[53,9],[51,-6],[60,-21],[73,-39],[84,-59],[88,-68],[76,-105],[75,-107]],"c":true}],"h":1},{"t":37,"s":[{"i":[[6.233,-0.744],[3.673,-1.795],[2.536,-2.251],[1.463,-2.357],[-0.21,-2.367],[-1.895,-1.574],[-2.184,-0.066],[-3.996,3.091],[-3.467,-1.045],[-0.259,-4.087],[3.554,-5.557],[3.616,-4.531],[1.087,-12.139],[-9.903,1.954],[-0.682,3.684],[-0.71,4.488],[-0.964,1.734],[-0.939,1.446],[-0.345,0.461],[-0.554,0.76],[-0.376,0.501],[-0.596,0.775],[-1.455,2.193],[-0.623,0.94],[-0.623,0.94],[-0.506,0.737],[-0.962,1.445],[-0.981,1.538],[0.282,9.538],[7.628,4.07]],"o":[[-4.773,0.57],[-3.673,1.795],[-1.358,1.206],[-1.463,2.357],[0.234,2.637],[1.896,1.574],[6.258,0.189],[4.147,-3.208],[2.255,0.68],[0.397,6.272],[-3.899,6.097],[-9.421,11.805],[-1.145,12.792],[3.806,-0.751],[0.803,-4.341],[0.086,-0.546],[0.913,-1.643],[0.285,-0.438],[0.636,-0.848],[0.344,-0.472],[0.653,-0.87],[2.166,-2.819],[1.103,-1.662],[1.103,-1.662],[0.595,-0.897],[0.809,-1.177],[0.873,-1.312],[4.282,-6.717],[-0.374,-12.63],[-4.306,-2.297]],"v":[[65,-109],[52.323,-105.261],[43,-99],[38.324,-93.371],[36,-86],[39.537,-79.572],[46,-77],[57,-84],[70,-90],[76,-79],[68,-59],[56,-43],[35,-8],[50,14],[57,6],[55,-7],[58,-11],[60,-16],[62,-17],[63,-20],[65,-21],[66,-24],[74,-33],[77,-37],[80,-41],[82,-43],[84,-48],[87,-52],[96,-79],[81,-107]],"c":true}],"h":1},{"t":38,"s":[{"i":[[1.647,-0.211],[3.205,-1.278],[3.453,-2.54],[2.196,-2.853],[-1.45,-4.627],[-1.536,-1.121],[-2.416,-0.112],[-2.608,2.496],[-2.576,1.201],[-2.905,-1.487],[-0.312,-4.948],[1.855,-3.591],[1.77,-2.606],[2.135,-2.77],[2.131,-2.486],[4.115,-5.865],[0,-7.076],[-9.381,0.969],[0.945,4.244],[-0.331,2.895],[-2.557,3.213],[-1.725,2.071],[-2.393,3.051],[-0.908,1.137],[-1.532,2.219],[-0.672,1.075],[-1.198,3.937],[3.55,7.75],[6.098,3.388],[1.056,0.329]],"o":[[-2.727,0.349],[-3.205,1.278],[-2.665,1.96],[-2.196,2.853],[0.439,1.403],[1.536,1.121],[3.993,0.185],[2.608,-2.496],[4.069,-1.897],[2.905,1.487],[0.191,3.028],[-1.855,3.591],[-2.331,3.432],[-2.135,2.77],[-5.541,6.463],[-4.115,5.865],[0,10.245],[8.784,-0.907],[-0.459,-2.063],[0.63,-5.51],[1.77,-2.224],[3.326,-3.993],[0.909,-1.159],[1.718,-2.153],[0.596,-0.863],[2.908,-4.652],[3.053,-10.033],[-3.703,-8.083],[-0.968,-0.538],[-4.187,-1.303]],"v":[[69,-109],[60.044,-106.643],[50,-101],[41.914,-94.001],[40,-83],[43.017,-79.032],[49,-77],[58.562,-81.461],[66,-88],[76.817,-88.634],[82,-79],[78.971,-68.684],[73,-59],[66.35,-49.791],[60,-42],[44.844,-23.959],[38,-5],[52,14],[60,1],[58,-6],[68,-20],[73,-27],[82,-37],[84,-41],[90,-47],[92,-51],[100,-66],[98,-93],[86,-106],[82,-109]],"c":true}],"h":1},{"t":39,"s":[{"i":[[6.89,-0.815],[3.45,-1.257],[3.629,-2.669],[2.123,-2.867],[-1.331,-4.445],[-1.321,-1.204],[-2.45,-0.294],[-2.437,1.703],[-1.607,-13.824],[3.698,-5.29],[0.365,-0.487],[0.611,-0.78],[0.417,-0.448],[0.686,-0.803],[2.043,-2.273],[2.527,-10.638],[-10.055,1.039],[-0.351,0.734],[-0.86,5.47],[-1.778,2.471],[-3.623,4.033],[-1.799,1.999],[-1.727,2.054],[-0.83,1.103],[-0.403,0.433],[-0.679,0.936],[-0.711,1.062],[0.53,11.664],[4.486,4.612],[1.94,1.026]],"o":[[-2.573,0.304],[-3.45,1.257],[-2.84,2.088],[-2.123,2.867],[0.267,0.889],[1.321,1.204],[6.346,0.762],[8.352,-5.838],[0.651,5.598],[-0.322,0.461],[-0.659,0.878],[-0.361,0.46],[-0.697,0.749],[-2.476,2.9],[-10.39,11.564],[-3.517,14.807],[4.645,-0.48],[2.721,-5.69],[0.214,-1.358],[3.702,-5.144],[2.269,-2.525],[1.844,-2.05],[0.988,-1.175],[0.337,-0.448],[0.745,-0.8],[0.829,-1.142],[4.953,-7.395],[-0.454,-9.995],[-2.485,-2.555],[-5.035,-2.663]],"v":[[73,-109],[63.792,-106.773],[53,-101],[44.872,-93.767],[43,-83],[45.362,-79.554],[51,-77],[64,-84],[87,-80],[79,-60],[77,-59],[76,-56],[74,-55],[73,-52],[66,-45],[42,-12],[55,14],[63,9],[61,-7],[66,-14],[77,-27],[83,-34],[89,-40],[91,-44],[93,-45],[94,-48],[97,-51],[107,-80],[97,-101],[91,-107]],"c":true}],"h":1},{"t":40,"s":[{"i":[[4.56,-0.479],[2.024,-0.262],[1.95,-0.748],[0.607,-0.565],[0.799,-0.444],[1.906,-1.337],[1.585,-2.101],[0.779,-1.504],[-0.362,-2.331],[-4.248,-0.316],[-3.926,2.486],[-1.573,-12.837],[4.023,-5.229],[6.173,-6.924],[2.693,-4.652],[0.019,-5.058],[-8.53,1.272],[-0.963,3.04],[0.206,2.491],[-0.396,3.016],[-1.573,2.228],[-3.55,3.963],[-1.701,1.835],[-3.405,4.389],[-0.712,0.971],[-1.534,2.568],[-1.537,4.72],[-0.22,2.206],[6.333,4.201],[0.665,0.404]],"o":[[-1.954,0.206],[-2.024,0.262],[-0.772,0.296],[-0.607,0.565],[-2.79,1.549],[-1.906,1.337],[-1.247,1.653],[-0.779,1.504],[0.802,5.157],[6.652,0.495],[9.988,-6.325],[0.794,6.479],[-5.783,7.516],[-4.146,4.65],[-2.608,4.505],[-0.039,10.342],[5.007,-0.747],[0.615,-1.941],[-0.203,-2.447],[0.19,-1.452],[3.567,-5.052],[2.286,-2.552],[3.581,-3.864],[0.608,-0.783],[2.013,-2.745],[2.329,-3.899],[0.731,-2.245],[1.388,-13.905],[-0.823,-0.546],[-5.657,-3.436]],"v":[[77,-109],[70.996,-108.407],[65,-107],[63.02,-105.611],[61,-104],[54.097,-99.914],[49,-95],[45.793,-90.508],[45,-85],[54,-77],[67,-85],[91,-80],[82,-59],[60,-35],[48,-20],[43,-5],[58,14],[65,7],[66,2],[63,-6],[68,-13],[79,-26],[85,-33],[97,-45],[99,-49],[104,-56],[109,-67],[111,-74],[99,-104],[97,-106]],"c":true}],"h":1},{"t":41,"s":[{"i":[[5.891,-0.619],[7.054,-4.41],[-0.705,-7.194],[-5.031,-0.092],[-2.824,1.828],[-0.657,0.394],[-2.013,0.636],[-2.125,-0.552],[-0.418,-5.349],[2.56,-3.348],[0.67,-0.82],[2.544,-2.78],[1.253,-1.437],[1.207,-1.257],[0,-14.229],[-8.933,0.442],[-0.698,1.155],[0.579,3.606],[-0.339,2.536],[-0.446,0.479],[-0.686,0.877],[-0.919,0.987],[-0.682,0.766],[-5.785,6.618],[-0.808,0.985],[-0.432,0.464],[-0.687,0.872],[-1.654,2.521],[-1.364,6.519],[8.91,4.888]],"o":[[-6.357,0.668],[-5.62,3.514],[0.498,5.084],[5.931,0.109],[0.694,-0.449],[1.764,-1.058],[2.338,-0.739],[2.344,0.608],[0.506,6.467],[-0.612,0.801],[-2.183,2.67],[-1.231,1.345],[-1.246,1.429],[-12.272,12.774],[0,8.527],[2.795,-0.138],[0.685,-1.133],[-0.352,-2.197],[0.226,-1.696],[0.726,-0.779],[1.372,-1.753],[0.681,-0.732],[7.305,-8.207],[0.724,-0.829],[0.388,-0.473],[0.724,-0.777],[1.893,-2.404],[3.54,-5.395],[3.454,-16.502],[-6.286,-3.448]],"v":[[80,-109],[59,-102],[47,-86],[57,-77],[68,-84],[70,-85],[77,-89],[87,-90],[95,-79],[86,-61],[85,-58],[77,-51],[74,-46],[70,-42],[45,-5],[59,14],[67,10],[68,1],[65,-6],[69,-11],[70,-14],[75,-18],[76,-21],[96,-40],[98,-44],[100,-45],[101,-48],[107,-55],[114,-71],[99,-106]],"c":true}],"h":1},{"t":42,"s":[{"i":[[5.581,-0.632],[7.165,-4.053],[-1.142,-7.166],[-1.553,-1.381],[-2.421,-0.18],[-1.8,1.565],[-1.521,0.984],[-3.539,1.312],[-2.811,-0.562],[-1.738,-1.872],[-0.263,-3.155],[2.61,-4.713],[9.48,-10.878],[1.233,-1.504],[1.8,-3.22],[0.368,-5.272],[-1.843,-2.869],[-6.806,1.534],[-0.796,2.513],[0.246,2.439],[-0.489,3.636],[-0.368,0.49],[-0.662,0.792],[-3.438,3.791],[-5.613,8.462],[-1.561,2.724],[-1.17,6.131],[7.022,5.403],[0.481,0.217],[0.411,0.238]],"o":[[-5.48,0.621],[-7.166,4.053],[0.366,2.296],[1.553,1.381],[3.279,0.244],[1.8,-1.565],[2.031,-1.314],[3.54,-1.312],[1.785,0.357],[1.738,1.872],[0.371,4.452],[-8.341,15.06],[-1.324,1.52],[-2.438,2.974],[-1.597,2.857],[-0.327,4.68],[1.338,2.084],[3.671,-0.828],[0.639,-2.018],[-0.224,-2.216],[0.192,-1.43],[0.677,-0.903],[3.676,-4.395],[7.576,-8.354],[1.656,-2.497],[2.724,-4.753],[2.594,-13.588],[-0.41,-0.315],[-0.349,-0.157],[-5.679,-3.284]],"v":[[82,-109],[60.534,-101.908],[49,-85],[51.959,-79.413],[58,-77],[65.318,-79.579],[70,-84],[78.915,-88.407],[89,-90],[94.642,-86.599],[98,-79],[94,-68],[61,-32],[58,-27],[51,-19],[47,-7],[50,8],[63,14],[69,7],[70,2],[67,-6],[71,-11],[72,-14],[85,-27],[108,-52],[112,-59],[117,-72],[106,-104],[104,-104],[103,-106]],"c":true}],"h":1},{"t":43,"s":[{"i":[[4.927,-0.558],[3.542,-1.123],[3.644,-2.146],[2.59,-2.62],[-0.351,-3.759],[-1.728,-1.527],[-1.838,-0.203],[-1.693,1.27],[-1.334,1.242],[-1.264,0.661],[-0.923,0.407],[-1.021,0.334],[-1.016,0.265],[-2.77,-1.549],[-1.102,-4.061],[2.077,-3.671],[2.572,-3.066],[2.696,-2.589],[1.977,-1.977],[2.091,-10.911],[-10.779,1.307],[-1.057,1.787],[-1.051,5.198],[-2.459,2.941],[-0.93,1.141],[-3.209,2.975],[-2.158,2.158],[-2.093,2.308],[-2.789,7.123],[11.877,6.689]],"o":[[-3.189,0.361],[-3.542,1.123],[-2.991,1.761],[-2.59,2.62],[0.251,2.69],[1.728,1.527],[2.895,0.32],[1.693,-1.27],[1.02,-0.95],[1.264,-0.661],[0.9,-0.398],[1.021,-0.334],[4.816,-1.257],[2.77,1.549],[0.882,3.25],[-2.077,3.671],[-2.239,2.668],[-2.696,2.589],[-9.81,9.81],[-2.071,10.81],[1.08,-0.131],[3.745,-6.328],[0.488,-2.412],[0.944,-1.129],[2.981,-3.657],[2.545,-2.359],[2.037,-2.037],[5.832,-6.43],[6.585,-16.816],[-5.88,-3.312]],"v":[[84,-109],[73.841,-106.839],[63,-102],[53.993,-95.498],[50,-86],[53.309,-79.635],[59,-77],[65.671,-78.829],[70,-83],[73.573,-85.407],[77,-87],[79.913,-88.099],[83,-89],[94.285,-88.489],[100,-80],[97.591,-69.362],[90,-59],[82.303,-50.981],[75,-44],[49,-10],[63,14],[70,10],[69,-7],[75,-14],[77,-18],[87,-28],[95,-34],[101,-41],[118,-64],[105,-106]],"c":true}],"h":1},{"t":44,"s":[{"i":[[5.115,-0.566],[0.943,-0.154],[1.191,-0.267],[0.914,-0.327],[1.32,-0.463],[1.941,-0.604],[1.595,-1.01],[0.14,-0.424],[0.226,-0.169],[0.793,-0.671],[0.484,-0.704],[-9.232,-0.443],[-2.197,1.257],[-5.522,-1.014],[-0.547,-6.508],[2.724,-4.107],[3.049,-2.882],[3.51,-5.292],[1.328,-2.294],[0.363,-1.894],[-1.376,-2.868],[-5.947,0.179],[-0.63,1.987],[-1.123,6.386],[-1.55,1.785],[-9.427,10.506],[-1.063,1.345],[-1.763,2.75],[0.099,8.822],[7.131,4.016]],"o":[[-1.038,0.115],[-0.943,0.154],[-1.026,0.231],[-0.914,0.327],[-1.639,0.575],[-1.941,0.604],[-0.215,0.136],[-0.14,0.424],[-0.907,0.68],[-1.156,0.978],[-5.414,7.879],[4.964,0.238],[4.374,-2.503],[3.374,0.62],[0.679,8.082],[-3.097,4.669],[-5.604,5.298],[-2.105,3.173],[-1.447,2.499],[-1.606,8.373],[2.312,4.819],[6.404,-0.193],[2.077,-6.55],[0.135,-0.768],[10.333,-11.897],[0.89,-0.991],[2.309,-2.921],[3.774,-5.887],[-0.135,-11.968],[-6.041,-3.402]],"v":[[86,-109],[83.114,-108.614],[80,-108],[77.22,-107.174],[74,-106],[68.467,-104.326],[63,-102],[62.508,-101.025],[62,-100],[59,-99],[55,-95],[61,-77],[75,-85],[93,-90],[103,-79],[91,-58],[78,-44],[61,-28],[54,-19],[50,-11],[52,6],[63,14],[72,7],[70,-7],[75,-14],[107,-45],[109,-49],[115,-56],[123,-79],[107,-106]],"c":true}],"h":1},{"t":45,"s":[{"i":[[0.261,-0.024],[8.017,-4.31],[-2.441,-8.274],[-1.328,-1.18],[-2.397,-0.288],[-2.528,1.81],[-1.946,1.114],[-3.131,1.036],[-3.078,-0.516],[-1.994,-1.612],[-0.453,-3.289],[2.299,-3.506],[2.393,-2.681],[2.722,-2.635],[2.206,-2.206],[2.769,-2.663],[2.362,-2.786],[2.18,-3.757],[0.046,-4.341],[-2.666,-3.677],[-4.385,0.463],[1.056,5.63],[-0.76,4],[-2.669,2.987],[-6.78,6.435],[-3.89,11.92],[3.755,5.161],[2.881,1.299],[0.428,0.231],[7.572,0.345]],"o":[[-5.371,0.485],[-8.018,4.31],[0.293,0.992],[1.328,1.18],[2.942,0.353],[2.528,-1.81],[2.397,-1.371],[3.131,-1.036],[1.571,0.263],[1.994,1.612],[0.59,4.296],[-2.299,3.506],[-2.904,3.254],[-2.722,2.635],[-2.564,2.564],[-2.769,2.663],[-2.232,2.632],[-2.181,3.757],[-0.05,4.755],[2.666,3.677],[7.589,-0.801],[-0.441,-2.352],[0.115,-0.608],[6.683,-7.478],[10.619,-10.079],[3.595,-11.016],[-2.432,-3.343],[-0.358,-0.161],[-4.269,-2.305],[-0.985,-0.045]],"v":[[88,-109],[64.641,-101.842],[53,-83],[55.422,-79.472],[61,-77],[69.247,-79.9],[76,-85],[84.489,-88.916],[94,-90],[99.839,-87.269],[104,-80],[100.738,-68.289],[93,-59],[84.476,-50.214],[77,-43],[68.849,-35.167],[61,-27],[53.861,-17.282],[50,-5],[54.174,8.414],[65,14],[73,1],[71,-7],[77,-15],[99,-36],[123,-69],[118,-97],[110,-104],[109,-106],[90,-110]],"c":true}],"h":1},{"t":46,"s":[{"i":[[0.261,-0.024],[3.609,-1.108],[3.998,-2.245],[-0.756,-8.148],[-4.586,-0.172],[-3.519,2.276],[-3.334,0.953],[-4.742,-2.039],[-0.147,-6.015],[1.516,-2.355],[5.212,-4.928],[2.644,-2.52],[2.302,-2.807],[1.252,-1.683],[0.027,-7.326],[-9.201,1.116],[1.156,7.206],[-0.386,2.527],[-2.006,2.278],[-4.659,4.126],[-1.031,0.939],[-4.081,4.977],[-0.432,0.464],[-0.685,0.891],[-1.167,2.216],[0.124,6.681],[1.678,2.899],[2.982,2.713],[0.823,0.444],[6.03,0.275]],"o":[[-3.566,0.322],[-3.609,1.108],[-6.202,3.483],[0.531,5.729],[7.273,0.272],[2.833,-1.832],[3.97,-1.135],[1.682,0.723],[0.086,3.512],[-5.448,8.46],[-2.964,2.802],[-3.241,3.089],[-1.422,1.734],[-3.514,4.726],[-0.035,9.359],[6.251,-0.758],[-0.396,-2.471],[0.249,-1.627],[4.982,-5.658],[1.278,-1.132],[5.173,-4.711],[0.388,-0.473],[0.73,-0.785],[2.117,-2.754],[2.635,-5.004],[-0.117,-6.315],[-2.072,-3.579],[-2.029,-1.847],[-4.47,-2.413],[-0.985,-0.045]],"v":[[89,-109],[78.324,-106.942],[67,-102],[53,-86],[63,-77],[73,-83],[84,-88],[99,-89],[106,-78],[101,-67],[78,-42],[69,-35],[62,-26],[58,-22],[51,-5],[66,14],[74,1],[71,-6],[77,-13],[93,-29],[97,-32],[112,-49],[114,-50],[115,-53],[121,-60],[126,-79],[121,-94],[115,-101],[110,-106],[91,-110]],"c":true}],"h":1},{"t":47,"s":[{"i":[[0.256,-0.022],[2.254,-0.585],[3.206,-1.332],[1.791,-0.633],[1.243,-0.803],[0.459,-0.344],[0.796,-0.69],[0.578,-2.536],[-5.412,-0.37],[-5.389,2.961],[-5.612,-7.141],[4.373,-4.9],[5.997,-4.909],[3.55,-3.74],[1.212,-6.251],[-3.531,-3.523],[-4.555,1.079],[-0.436,0.887],[-0.723,4.885],[-1.207,1.819],[-1.929,1.28],[-0.83,0.751],[-7.632,9.27],[-2.211,5.316],[-0.595,2.568],[2.654,4.923],[0.466,0.486],[3.364,1.516],[0.428,0.231],[8.55,0.39]],"o":[[-3.78,0.33],[-2.254,0.585],[-1.595,0.663],[-1.791,0.633],[-0.437,0.283],[-0.915,0.687],[-2.477,2.147],[-1.81,7.935],[5.485,0.375],[9.229,-5.071],[7.221,9.187],[-7.227,8.097],[-4.679,3.831],[-5.316,5.6],[-1.874,9.669],[2.82,2.814],[1.501,-0.355],[3.521,-7.157],[0.248,-1.678],[1.85,-2.789],[1.033,-0.685],[9.235,-8.355],[4.474,-5.434],[0.69,-1.658],[2.05,-8.853],[-0.787,-1.461],[-2.878,-3.004],[-0.358,-0.161],[-4.426,-2.389],[-0.981,-0.045]],"v":[[90,-109],[81.569,-107.752],[74,-105],[68.736,-103.105],[64,-101],[63,-99],[60,-98],[54,-90],[63,-77],[78,-85],[104,-85],[96,-60],[77,-41],[65,-29],[52,-11],[57,10],[68,14],[74,9],[72,-6],[79,-15],[86,-22],[88,-24],[112,-48],[124,-64],[126,-71],[123,-92],[121,-96],[112,-104],[111,-106],[92,-110]],"c":true}],"h":1},{"t":48,"s":[{"i":[[5.707,-0.548],[3.607,-1.102],[4.005,-2.249],[2.819,-2.546],[-0.378,-4.074],[-1.662,-1.648],[-2.151,-0.238],[-1.707,1.271],[-1.321,1.23],[-4.305,1.431],[-2.995,-0.388],[-2.217,-1.571],[-0.439,-4.495],[2.592,-3.463],[2.102,-2.328],[3.975,-3.638],[4.112,-3.924],[2.697,-3.058],[1.5,-3.18],[0.57,-2.285],[-0.267,-2.771],[-10.363,1.257],[1.29,7.424],[-0.455,2.952],[-2.67,2.951],[-4.807,4.372],[-3.133,3.133],[-1.813,14.684],[3.516,5.047],[5.309,2.786]],"o":[[-3.566,0.342],[-3.607,1.102],[-3.101,1.742],[-2.819,2.546],[0.201,2.169],[1.662,1.648],[2.852,0.316],[1.707,-1.271],[1.786,-1.663],[4.305,-1.431],[1.695,0.22],[2.216,1.571],[0.471,4.82],[-2.592,3.463],[-4.989,5.524],[-3.975,3.638],[-2.714,2.59],[-2.697,3.058],[-0.985,2.088],[-0.571,2.285],[0.413,4.288],[6.161,-0.747],[-0.406,-2.339],[0.384,-2.489],[5.442,-6.015],[2.741,-2.493],[10.566,-10.566],[1.415,-11.462],[-3.734,-5.361],[-5.857,-3.073]],"v":[[90,-109],[79.329,-106.93],[68,-102],[58.391,-95.749],[54,-86],[57.038,-80.051],[63,-77],[69.648,-78.841],[74,-83],[84.094,-88.039],[96,-90],[102.442,-87.706],[107,-79],[102.93,-66.631],[95,-58],[81.841,-44.8],[70,-34],[61.59,-25.442],[55,-16],[52.561,-9.512],[52,-2],[67,14],[75,1],[72,-6],[79,-14],[94,-29],[102,-37],[127,-73],[121,-96],[111,-106]],"c":true}],"h":1},{"t":49,"s":[{"i":[[3.726,-0.269],[3.866,-1.088],[4.144,-2.403],[2.823,-2.612],[-0.276,-3.822],[-1.586,-1.63],[-2.499,-0.282],[-1.548,1.293],[-1.59,1.192],[-0.564,0.12],[-0.354,0.225],[-4.842,0.875],[-3.172,-2.701],[-0.118,-3.707],[1.76,-2.908],[6.757,-6.211],[3.75,-10.485],[-0.208,-3.713],[-1.091,-1.383],[2.067,12.881],[-0.361,2.289],[-1.78,2.037],[-2.428,2.305],[-7.457,6.16],[-3.098,4.195],[3.88,12.637],[0.571,0.761],[0.508,0.74],[2.927,1.694],[0.556,0.251]],"o":[[-3.391,0.245],[-3.866,1.088],[-2.984,1.73],[-2.823,2.612],[0.159,2.197],[1.586,1.63],[3.218,0.363],[1.548,-1.293],[0.392,-0.294],[0.564,-0.12],[3.46,-2.202],[4.842,-0.875],[2.825,2.405],[0.108,3.373],[-5.399,8.92],[-11.244,10.335],[-2.003,5.601],[0.162,2.889],[6.774,8.581],[-0.434,-2.706],[0.207,-1.314],[2.331,-2.667],[8.916,-8.463],[4.571,-3.776],[5.652,-7.653],[-0.646,-2.104],[-0.616,-0.821],[-3.524,-5.128],[-0.784,-0.454],[-4.8,-2.162]],"v":[[91,-109],[80.065,-107.118],[68,-102],[58.555,-95.569],[54,-86],[56.744,-80.063],[63,-77],[69.721,-78.833],[74,-83],[75.529,-83.551],[77,-84],[90.216,-89.177],[103,-87],[108,-78],[104,-68],[82,-45],[54,-13],[52,-3],[56,9],[75,1],[72,-6],[78,-13],[85,-20],[107,-41],[118,-54],[127,-87],[123,-93],[122,-96],[112,-105],[110,-107]],"c":true}],"h":1},{"t":50,"s":[{"i":[[10.014,-0.756],[0.334,0.01],[0.332,-0.013],[7.34,-4.098],[-0.047,-5.375],[-1.561,-1.849],[-2.735,-0.328],[-1.553,1.304],[-1.559,1.17],[-0.564,0.12],[-0.354,0.225],[-4.846,0.868],[-3.148,-2.679],[-0.605,-0.782],[-0.453,-0.912],[2.39,-3.712],[5.022,-4.916],[1.428,-1.092],[0.716,-0.65],[4.753,-5.254],[0.052,-8.184],[-9.639,1.902],[0.967,6.027],[-0.365,2.287],[-0.108,0.144],[-0.519,0.745],[-3.935,3.656],[-4.823,4.433],[-3.771,12.303],[9.393,5.781]],"o":[[-0.331,0.025],[-0.334,-0.01],[-5.69,0.232],[-7.339,4.098],[0.02,2.276],[1.561,1.849],[3.227,0.388],[1.553,-1.304],[0.392,-0.294],[0.564,-0.12],[3.47,-2.209],[4.846,-0.868],[1.127,0.96],[0.605,0.782],[2.799,5.636],[-4.487,6.967],[-2.004,1.962],[-0.743,0.568],[-5.103,4.635],[-4.541,5.021],[-0.059,9.36],[6.182,-1.22],[-0.434,-2.705],[0.005,-0.029],[0.621,-0.828],[3.433,-4.925],[6.122,-5.688],[10.354,-9.517],[4.825,-15.744],[-6.583,-4.051]],"v":[[91,-109],[90.001,-108.991],[89,-109],[67.197,-101.857],[54,-87],[56.464,-80.539],[63,-77],[69.751,-78.832],[74,-83],[75.529,-83.551],[77,-84],[90.242,-89.166],[103,-87],[105.506,-84.464],[107,-82],[103,-67],[88,-50],[82,-44],[79,-43],[63,-27],[52,-5],[68,14],[75,1],[72,-6],[74,-7],[75,-10],[86,-21],[101,-35],[126,-68],[114,-104]],"c":true}],"h":1},{"t":51,"s":[{"i":[[10.014,-0.756],[0.334,0.01],[0.332,-0.013],[7.385,-4.171],[-0.434,-6.076],[-1.652,-1.692],[-2.25,-0.27],[-3.046,2.238],[-3.59,1.436],[-3.744,0.083],[-2.191,-1.865],[-0.881,-1.501],[-0.057,-1.79],[1.061,-1.93],[0.82,-1.273],[2.519,-2.714],[2.61,-2.555],[1.071,-1.118],[0.714,-0.546],[0.716,-0.65],[4.921,-5.441],[0,-8.425],[-9.948,1.963],[0.967,6.027],[-0.361,2.289],[-1.893,2.135],[-2.46,2.285],[-4.891,4.496],[-3.77,12.301],[9.393,5.781]],"o":[[-0.331,0.025],[-0.334,-0.01],[-5.894,0.24],[-7.385,4.171],[0.141,1.96],[1.652,1.692],[3.227,0.388],[3.046,-2.238],[3.834,-1.534],[3.744,-0.083],[1.418,1.208],[0.881,1.501],[0.064,2.005],[-1.061,1.93],[-2.312,3.591],[-2.519,2.714],[-1.002,0.981],[-1.071,1.118],[-0.743,0.568],[-5.167,4.692],[-4.504,4.98],[0,9.142],[6.182,-1.22],[-0.434,-2.706],[0.105,-0.668],[2.217,-2.501],[6.163,-5.726],[10.351,-9.515],[4.825,-15.744],[-6.583,-4.051]],"v":[[91,-109],[90.001,-108.991],[89,-109],[66.754,-101.877],[54,-86],[56.918,-80.233],[63,-77],[72.227,-80.632],[82,-87],[93.732,-89.55],[103,-87],[106.521,-82.937],[108,-78],[106.163,-71.951],[103,-67],[95.723,-57.723],[88,-50],[84.784,-46.674],[82,-44],[79,-43],[63,-27],[52,-5],[68,14],[75,1],[72,-6],[78,-14],[86,-21],[101,-35],[126,-68],[114,-104]],"c":true}],"h":1},{"t":52,"s":[{"i":[[12.38,-0.935],[0.334,0.01],[0.332,-0.013],[7.395,-4.231],[-0.401,-5.84],[-1.589,-1.631],[-2.495,-0.281],[-1.556,1.296],[-1.569,1.177],[-0.708,0.451],[-6.344,-5.401],[-0.118,-3.707],[1.553,-2.412],[2.109,-2.261],[3.479,-3.032],[1.015,-0.925],[4.884,-5.399],[0.457,-0.597],[0.675,-0.881],[0.965,-1.73],[-4.653,-6.739],[-6.717,1.325],[0.967,6.027],[-0.361,2.289],[-1.893,2.135],[-2.46,2.285],[-4.813,4.442],[-3.763,12.278],[3.547,5.784],[3.366,3.062]],"o":[[-0.331,0.025],[-0.334,-0.01],[-5.822,0.237],[-7.395,4.231],[0.151,2.196],[1.589,1.631],[3.209,0.362],[1.556,-1.296],[0.784,-0.588],[6.92,-4.404],[2.825,2.405],[0.127,3.97],[-1.794,2.785],[-3.802,4.076],[-1.461,1.273],[-5.345,4.868],[-0.563,0.622],[-0.636,0.832],[-1.303,1.701],[-4.265,7.65],[2.215,3.208],[6.182,-1.22],[-0.434,-2.706],[0.105,-0.668],[2.217,-2.501],[6.121,-5.687],[10.358,-9.559],[2.752,-8.978],[-2.654,-4.329],[-7.422,-6.753]],"v":[[91,-109],[90.001,-108.991],[89,-109],[66.832,-101.702],[54,-86],[56.742,-80.064],[63,-77],[69.73,-78.846],[74,-83],[77,-84],[103,-87],[108,-78],[103,-67],[96,-58],[84,-46],[79,-43],[63,-27],[61,-25],[60,-22],[56,-18],[56,8],[68,14],[75,1],[72,-6],[78,-14],[86,-21],[101,-35],[126,-68],[124,-93],[117,-101]],"c":true}],"h":1},{"t":53,"s":[{"i":[[12.304,-0.929],[0.334,0.01],[0.332,-0.013],[7.404,-4.223],[-0.424,-5.87],[-1.586,-1.63],[-2.499,-0.282],[-1.556,1.296],[-1.569,1.177],[-0.564,0.12],[-0.354,0.225],[-4.842,0.875],[-3.172,-2.701],[-0.882,-1.486],[-0.059,-1.853],[1.066,-1.956],[0.799,-1.242],[3.42,-3.683],[2.926,-2.55],[1.015,-0.925],[4.644,-5.08],[-8.281,-11.995],[-6.717,1.325],[0.967,6.027],[-0.361,2.289],[-1.837,2.073],[-2.369,2.222],[-4.859,4.485],[13.718,22.37],[3.366,3.062]],"o":[[-0.331,0.025],[-0.334,-0.01],[-5.806,0.237],[-7.404,4.223],[0.159,2.197],[1.586,1.63],[3.209,0.362],[1.556,-1.296],[0.392,-0.294],[0.564,-0.12],[3.46,-2.202],[4.842,-0.875],[1.412,1.203],[0.882,1.486],[0.061,1.934],[-1.066,1.956],[-2.394,3.718],[-3.42,3.683],[-1.461,1.273],[-5.408,4.924],[-7.65,8.367],[2.215,3.208],[6.182,-1.22],[-0.434,-2.706],[0.097,-0.617],[2.18,-2.459],[6.391,-5.993],[13.758,-12.697],[-2.654,-4.329],[-7.482,-6.808]],"v":[[91,-109],[90.001,-108.991],[89,-109],[66.828,-101.724],[54,-86],[56.744,-80.063],[63,-77],[69.73,-78.846],[74,-83],[75.529,-83.551],[77,-84],[90.216,-89.177],[103,-87],[106.515,-82.988],[108,-78],[106.146,-71.981],[103,-67],[93.899,-55.624],[84,-46],[79,-43],[63,-27],[56,8],[68,14],[75,1],[72,-6],[78,-14],[86,-21],[101,-35],[124,-93],[117,-101]],"c":true}],"h":1},{"t":54,"s":[{"i":[[12.304,-0.929],[0.334,0.01],[0.332,-0.013],[7.404,-4.223],[-0.424,-5.87],[-1.586,-1.63],[-2.499,-0.282],[-1.556,1.296],[-1.569,1.177],[-0.564,0.12],[-0.354,0.225],[-4.842,0.875],[-3.172,-2.701],[-0.882,-1.486],[-0.059,-1.853],[1.066,-1.956],[0.799,-1.242],[3.42,-3.683],[2.926,-2.55],[1.015,-0.925],[4.644,-5.08],[-8.281,-11.995],[-6.717,1.325],[0.967,6.027],[-0.361,2.289],[-1.837,2.073],[-2.369,2.222],[-4.843,4.499],[13.699,22.339],[3.366,3.062]],"o":[[-0.331,0.025],[-0.334,-0.01],[-5.806,0.237],[-7.404,4.223],[0.159,2.197],[1.586,1.63],[3.209,0.362],[1.556,-1.296],[0.392,-0.294],[0.564,-0.12],[3.46,-2.202],[4.842,-0.875],[1.412,1.203],[0.882,1.486],[0.061,1.934],[-1.066,1.956],[-2.394,3.718],[-3.42,3.683],[-1.461,1.273],[-5.408,4.924],[-7.65,8.367],[2.215,3.208],[6.182,-1.22],[-0.434,-2.706],[0.097,-0.617],[2.18,-2.459],[6.39,-5.993],[13.777,-12.8],[-2.654,-4.329],[-7.482,-6.808]],"v":[[91,-109],[90.001,-108.991],[89,-109],[66.828,-101.724],[54,-86],[56.744,-80.063],[63,-77],[69.73,-78.846],[74,-83],[75.529,-83.551],[77,-84],[90.216,-89.177],[103,-87],[106.515,-82.988],[108,-78],[106.146,-71.981],[103,-67],[93.899,-55.624],[84,-46],[79,-43],[63,-27],[56,8],[68,14],[75,1],[72,-6],[78,-14],[86,-21],[101,-35],[124,-93],[117,-101]],"c":true}],"h":1},{"t":55,"s":[{"i":[[12.3,-0.929],[0.334,0.01],[0.332,-0.013],[7.404,-4.223],[-0.424,-5.87],[-1.586,-1.63],[-2.499,-0.282],[-1.556,1.296],[-1.569,1.177],[-0.564,0.12],[-0.354,0.225],[-6.344,-5.401],[-0.118,-3.707],[1.809,-2.989],[6.743,-6.198],[3.75,-10.485],[-0.208,-3.713],[-1.091,-1.383],[2.067,12.881],[-0.361,2.289],[-1.831,2.095],[-2.428,2.305],[-3.88,3.668],[-1.006,0.754],[-0.796,0.79],[-2.045,2.458],[-2.43,5.656],[-0.59,2.441],[2.858,4.937],[2.61,2.375]],"o":[[-0.331,0.025],[-0.334,-0.01],[-5.806,0.237],[-7.404,4.223],[0.159,2.197],[1.586,1.63],[3.209,0.362],[1.556,-1.296],[0.392,-0.294],[0.564,-0.12],[6.92,-4.404],[2.825,2.405],[0.107,3.347],[-5.399,8.921],[-11.244,10.335],[-2.003,5.601],[0.162,2.889],[6.774,8.581],[-0.434,-2.706],[0.213,-1.349],[2.331,-2.667],[5.725,-5.434],[1.796,-1.698],[0.952,-0.714],[2.279,-2.261],[4.543,-5.459],[0.729,-1.698],[2.288,-9.461],[-1.972,-3.406],[-7.468,-6.795]],"v":[[91,-109],[90.001,-108.991],[89,-109],[66.828,-101.724],[54,-86],[56.744,-80.063],[63,-77],[69.73,-78.846],[74,-83],[75.529,-83.551],[77,-84],[103,-87],[108,-78],[104,-68],[82,-45],[54,-13],[52,-3],[56,9],[75,1],[72,-6],[78,-13],[85,-20],[99,-34],[104,-39],[107,-40],[113,-48],[125,-64],[127,-71],[123,-94],[117,-101]],"c":true}],"h":1},{"t":56,"s":[{"i":[[12.291,-0.928],[0.334,0.01],[0.332,-0.013],[7.404,-4.223],[-0.424,-5.87],[-1.585,-1.63],[-2.5,-0.282],[-1.556,1.296],[-1.569,1.177],[-0.564,0.12],[-0.354,0.225],[-4.842,0.875],[-3.172,-2.701],[-0.118,-3.707],[1.809,-2.989],[6.743,-6.198],[3.75,-10.485],[-0.208,-3.713],[-1.091,-1.383],[2.067,12.881],[-0.361,2.289],[-1.78,2.037],[-2.428,2.305],[-5.434,5.059],[-0.68,0.664],[-2.148,2.482],[-1.904,2.579],[0.202,10.903],[1.709,2.951],[2.701,2.457]],"o":[[-0.331,0.025],[-0.334,-0.01],[-5.806,0.237],[-7.404,4.223],[0.159,2.197],[1.585,1.63],[3.209,0.362],[1.556,-1.296],[0.392,-0.294],[0.564,-0.12],[3.46,-2.202],[4.842,-0.875],[2.825,2.405],[0.107,3.347],[-5.399,8.921],[-11.244,10.335],[-2.003,5.601],[0.162,2.889],[6.774,8.581],[-0.434,-2.706],[0.207,-1.314],[2.331,-2.667],[7.595,-7.21],[0.69,-0.642],[2.343,-2.289],[2.151,-2.486],[4.975,-6.737],[-0.118,-6.386],[-1.971,-3.404],[-7.448,-6.777]],"v":[[91,-109],[90.001,-108.991],[89,-109],[66.828,-101.724],[54,-86],[56.744,-80.063],[63,-77],[69.73,-78.846],[74,-83],[75.529,-83.551],[77,-84],[90.216,-89.177],[103,-87],[108,-78],[104,-68],[82,-45],[54,-13],[52,-3],[56,9],[75,1],[72,-6],[78,-13],[85,-20],[103,-38],[106,-39],[112,-47],[118,-54],[128,-79],[123,-94],[117,-101]],"c":true}],"h":1},{"t":57,"s":[{"i":[[12.291,-0.928],[0.334,0.01],[0.332,-0.013],[7.406,-4.223],[-0.432,-5.871],[-1.584,-1.63],[-2.502,-0.282],[-1.556,1.296],[-1.569,1.177],[-0.564,0.12],[-0.354,0.225],[-4.842,0.875],[-3.172,-2.701],[-0.882,-1.486],[-0.059,-1.853],[0.787,-1.708],[0.904,-1.495],[6.663,-6.125],[3.75,-10.485],[-0.208,-3.713],[-1.091,-1.383],[2.067,12.881],[-0.361,2.289],[-1.78,2.037],[-2.428,2.305],[-7.457,6.16],[-0.666,0.747],[0.293,15.788],[1.709,2.951],[2.701,2.457]],"o":[[-0.331,0.025],[-0.334,-0.01],[-5.806,0.237],[-7.406,4.223],[0.162,2.198],[1.584,1.63],[3.209,0.362],[1.556,-1.296],[0.392,-0.294],[0.564,-0.12],[3.46,-2.202],[4.842,-0.875],[1.412,1.203],[0.882,1.486],[0.053,1.673],[-0.787,1.708],[-5.368,8.869],[-11.244,10.335],[-2.003,5.601],[0.162,2.889],[6.774,8.581],[-0.434,-2.706],[0.207,-1.314],[2.331,-2.667],[8.916,-8.463],[0.788,-0.651],[8.101,-9.083],[-0.118,-6.386],[-1.971,-3.404],[-7.448,-6.777]],"v":[[91,-109],[90.001,-108.991],[89,-109],[66.821,-101.725],[54,-86],[56.745,-80.063],[63,-77],[69.73,-78.846],[74,-83],[75.529,-83.551],[77,-84],[90.216,-89.177],[103,-87],[106.515,-82.988],[108,-78],[106.719,-72.866],[104,-68],[82,-45],[54,-13],[52,-3],[56,9],[75,1],[72,-6],[78,-13],[85,-20],[107,-41],[109,-43],[128,-79],[123,-94],[117,-101]],"c":true}],"h":1},{"t":58,"s":[{"i":[[12.291,-0.928],[0.334,0.01],[0.332,-0.013],[7.406,-4.223],[-0.432,-5.871],[-1.584,-1.63],[-2.502,-0.282],[-1.556,1.296],[-1.569,1.177],[-0.564,0.12],[-0.354,0.225],[-4.842,0.875],[-3.172,-2.701],[-0.882,-1.486],[-0.059,-1.853],[0.787,-1.708],[0.904,-1.495],[6.663,-6.125],[3.75,-10.485],[-0.208,-3.713],[-1.091,-1.383],[2.067,12.881],[-0.361,2.289],[-1.78,2.037],[-2.428,2.305],[-7.457,6.16],[-0.666,0.747],[0.293,15.788],[1.709,2.951],[2.701,2.457]],"o":[[-0.331,0.025],[-0.334,-0.01],[-5.806,0.237],[-7.406,4.223],[0.162,2.198],[1.584,1.63],[3.209,0.362],[1.556,-1.296],[0.392,-0.294],[0.564,-0.12],[3.46,-2.202],[4.842,-0.875],[1.412,1.203],[0.882,1.486],[0.053,1.673],[-0.787,1.708],[-5.368,8.869],[-11.244,10.335],[-2.003,5.601],[0.162,2.889],[6.774,8.581],[-0.434,-2.706],[0.207,-1.314],[2.331,-2.667],[8.916,-8.463],[0.788,-0.651],[8.101,-9.083],[-0.118,-6.386],[-1.971,-3.404],[-7.448,-6.777]],"v":[[91,-109],[90.001,-108.991],[89,-109],[66.821,-101.725],[54,-86],[56.745,-80.063],[63,-77],[69.73,-78.846],[74,-83],[75.529,-83.551],[77,-84],[90.216,-89.177],[103,-87],[106.515,-82.988],[108,-78],[106.719,-72.866],[104,-68],[82,-45],[54,-13],[52,-3],[56,9],[75,1],[72,-6],[78,-13],[85,-20],[107,-41],[109,-43],[128,-79],[123,-94],[117,-101]],"c":true}],"h":1},{"t":59,"s":[{"i":[[0.233,-0.018],[0.334,0.01],[0.332,-0.013],[7.34,-4.074],[-0.087,-5.471],[-1.568,-1.799],[-2.689,-0.303],[-1.556,1.296],[-1.569,1.177],[-0.564,0.12],[-0.354,0.225],[-4.842,0.875],[-3.172,-2.701],[-0.118,-3.707],[1.665,-2.75],[6.591,-6.058],[3.695,-10.333],[-0.208,-3.713],[-1.136,-1.439],[2.07,12.899],[-0.361,2.289],[-2.204,2.522],[-2.383,2.262],[-7.197,5.946],[-3.098,4.195],[0.194,10.463],[2.607,2.721],[3.203,1.444],[0.428,0.231],[6.327,0.288]],"o":[[-0.331,0.025],[-0.334,-0.01],[-5.729,0.234],[-7.339,4.074],[0.04,2.501],[1.568,1.799],[3.209,0.362],[1.556,-1.296],[0.392,-0.294],[0.564,-0.12],[3.46,-2.202],[4.842,-0.875],[2.825,2.405],[0.102,3.205],[-5.468,9.033],[-11.226,10.319],[-2.003,5.601],[0.162,2.892],[6.756,8.557],[-0.434,-2.706],[0.214,-1.36],[2.256,-2.581],[8.329,-7.906],[4.571,-3.776],[4.943,-6.693],[-0.111,-5.958],[-2.958,-3.088],[-0.358,-0.161],[-4.365,-2.357],[-0.963,-0.044]],"v":[[91,-109],[90.001,-108.991],[89,-109],[67.138,-101.928],[54,-87],[56.513,-80.352],[63,-77],[69.73,-78.846],[74,-83],[75.529,-83.551],[77,-84],[90.216,-89.177],[103,-87],[108,-78],[104,-68],[82,-45],[54,-13],[52,-3],[56,9],[75,1],[72,-6],[78,-13],[85,-20],[107,-41],[118,-54],[128,-79],[122,-96],[113,-104],[112,-106],[93,-110]],"c":true}],"h":1},{"t":60,"s":[{"i":[[12.291,-0.928],[0.334,0.01],[0.332,-0.013],[7.34,-4.074],[-0.087,-5.471],[-1.568,-1.799],[-2.689,-0.303],[-1.556,1.296],[-1.569,1.177],[-0.564,0.12],[-0.354,0.225],[-4.842,0.875],[-3.172,-2.701],[-0.882,-1.486],[-0.059,-1.853],[0.805,-1.756],[0.833,-1.375],[6.591,-6.058],[3.695,-10.333],[-0.208,-3.713],[-1.136,-1.439],[2.07,12.899],[-0.361,2.289],[-1.78,2.037],[-2.428,2.305],[-7.457,6.16],[-0.666,0.747],[0.293,15.788],[1.709,2.951],[2.701,2.457]],"o":[[-0.331,0.025],[-0.334,-0.01],[-5.729,0.234],[-7.339,4.074],[0.04,2.501],[1.568,1.799],[3.209,0.362],[1.556,-1.296],[0.392,-0.294],[0.564,-0.12],[3.46,-2.202],[4.842,-0.875],[1.412,1.203],[0.882,1.486],[0.051,1.602],[-0.805,1.756],[-5.468,9.033],[-11.226,10.319],[-2.003,5.601],[0.162,2.892],[6.756,8.557],[-0.434,-2.706],[0.207,-1.314],[2.331,-2.667],[8.916,-8.463],[0.788,-0.651],[8.101,-9.083],[-0.118,-6.386],[-1.971,-3.404],[-7.448,-6.777]],"v":[[91,-109],[90.001,-108.991],[89,-109],[67.138,-101.928],[54,-87],[56.513,-80.352],[63,-77],[69.73,-78.846],[74,-83],[75.529,-83.551],[77,-84],[90.216,-89.177],[103,-87],[106.515,-82.988],[108,-78],[106.663,-72.829],[104,-68],[82,-45],[54,-13],[52,-3],[56,9],[75,1],[72,-6],[78,-13],[85,-20],[107,-41],[109,-43],[128,-79],[123,-94],[117,-101]],"c":true}],"h":1}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.525490196078,0.270588235294,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":31,"op":300,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Shape Layer 4","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0.016,54.049,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":10,"s":[{"i":[[0,0],[11.928,-26.533],[-4,-20],[1.5,-2]],"o":[[-4.5,-7],[-12.25,27.25],[0.88,4.401],[-1.5,2]],"v":[[-64.5,-87],[-116.25,-85.75],[-62.5,-7],[-65.5,4]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":18,"s":[{"i":[[0,0],[-11.928,-26.533],[4,-20],[-1.5,-2]],"o":[[4.5,-7],[12.25,27.25],[-0.88,4.401],[1.5,2]],"v":[[64.42,-87],[116.17,-85.75],[62.42,-7],[65.42,4]],"c":false}]},{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":24,"s":[{"i":[[0,0],[11.928,-26.533],[-4,-20],[1.5,-2]],"o":[[-4.5,-7],[-12.25,27.25],[0.88,4.401],[-1.5,2]],"v":[[-64.5,-87],[-116.25,-85.75],[-62.5,-7],[-65.5,4]],"c":false}]},{"t":50,"s":[{"i":[[0,0],[-11.928,-26.533],[4,-20],[-1.5,-2]],"o":[[4.5,-7],[12.25,27.25],[-0.88,4.401],[1.5,2]],"v":[[64.42,-87],[116.17,-85.75],[62.42,-7],[65.42,4]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.525490196078,0.270588235294,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":20,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[43.313,-47.836],"ix":2},"a":{"a":0,"k":[43.313,-47.836],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":24,"op":31,"st":10,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Shape Layer 1","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0.016,54.049,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":10,"s":[{"i":[[0,0],[11.928,-26.533],[-4,-20],[1.5,-2]],"o":[[-4.5,-7],[-12.25,27.25],[0.88,4.401],[-1.5,2]],"v":[[-64.5,-87],[-116.25,-85.75],[-62.5,-7],[-65.5,4]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":18,"s":[{"i":[[0,0],[-11.928,-26.533],[4,-20],[-1.5,-2]],"o":[[4.5,-7],[12.25,27.25],[-0.88,4.401],[1.5,2]],"v":[[64.42,-87],[116.17,-85.75],[62.42,-7],[65.42,4]],"c":false}]},{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":24,"s":[{"i":[[0,0],[11.928,-26.533],[-4,-20],[1.5,-2]],"o":[[-4.5,-7],[-12.25,27.25],[0.88,4.401],[-1.5,2]],"v":[[-64.5,-87],[-116.25,-85.75],[-62.5,-7],[-65.5,4]],"c":false}]},{"t":50,"s":[{"i":[[0,0],[-11.928,-26.533],[4,-20],[-1.5,-2]],"o":[[4.5,-7],[12.25,27.25],[-0.88,4.401],[1.5,2]],"v":[[64.42,-87],[116.17,-85.75],[62.42,-7],[65.42,4]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.525490196078,0.270588235294,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":20,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[43.313,-47.836],"ix":2},"a":{"a":0,"k":[43.313,-47.836],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":18,"st":10,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"Shape Layer 5","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0.016,54.049,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[-100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":10,"s":[{"i":[[0,0],[11.928,-26.533],[-4,-20],[1.5,-2]],"o":[[-4.5,-7],[-12.25,27.25],[0.88,4.401],[-1.5,2]],"v":[[-64.5,-87],[-116.25,-85.75],[-62.5,-7],[-65.5,4]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":18,"s":[{"i":[[0,0],[-11.928,-26.533],[4,-20],[-1.5,-2]],"o":[[4.5,-7],[12.25,27.25],[-0.88,4.401],[1.5,2]],"v":[[64.42,-87],[116.17,-85.75],[62.42,-7],[65.42,4]],"c":false}]},{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":24,"s":[{"i":[[0,0],[11.928,-26.533],[-4,-20],[1.5,-2]],"o":[[-4.5,-7],[-12.25,27.25],[0.88,4.401],[-1.5,2]],"v":[[-64.5,-87],[-116.25,-85.75],[-62.5,-7],[-65.5,4]],"c":false}]},{"t":50,"s":[{"i":[[0,0],[-11.928,-26.533],[4,-20],[-1.5,-2]],"o":[[4.5,-7],[12.25,27.25],[-0.88,4.401],[1.5,2]],"v":[[64.42,-87],[116.17,-85.75],[62.42,-7],[65.42,4]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.525490196078,0.270588235294,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":20,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-78.173,-47.836],"ix":2},"a":{"a":0,"k":[-78.173,-47.836],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":18,"op":24,"st":10,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Cup 2","parent":15,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-11.815,0],[0,0],[1.176,-11.756],[0,0],[5.492,54.916],[0,0]],"o":[[0,0],[11.815,0],[0,0],[-5.492,54.916],[0,0],[-1.176,-11.756]],"v":[[-49.55,-73.91],[49.55,-73.91],[70.876,-52.583],[62.346,32.723],[-62.346,32.723],[-70.876,-52.583]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.705882370472,0.247058823705,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Cup","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":310,"st":10,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"Star 4 :M","parent":15,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":10,"s":[-225,-6.953,0],"to":[75,0,0],"ti":[-75,0,0]},{"t":50,"s":[225,-6.953,0]}],"ix":2,"l":2},"a":{"a":0,"k":[24.984,188.998,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[5.278,-3.874],[6.547,-0.032],[5.316,3.822],[2.054,6.217],[-1.993,6.237],[-5.278,3.874],[-6.547,0.032],[-5.316,-3.822],[-2.054,-6.217],[1.993,-6.237]],"o":[[-5.278,3.874],[-6.547,0.032],[-5.316,-3.822],[-2.054,-6.217],[1.993,-6.237],[5.278,-3.874],[6.547,-0.033],[5.316,3.822],[2.054,6.217],[-1.993,6.237]],"v":[[19.304,28.834],[0.146,23.68],[-18.962,29.022],[-19.98,9.209],[-30.965,-7.313],[-12.436,-14.404],[-0.118,-29.957],[12.352,-14.526],[30.95,-7.617],[20.128,9.011]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Star","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[249.984,188.998],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Star","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[-200.016,188.998],"ix":2},"a":{"a":0,"k":[249.984,188.998],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Star 4","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[5.278,-3.874],[6.547,-0.032],[5.316,3.822],[2.054,6.217],[-1.993,6.237],[-5.278,3.874],[-6.547,0.032],[-5.316,-3.822],[-2.054,-6.217],[1.993,-6.237]],"o":[[-5.278,3.874],[-6.547,0.032],[-5.316,-3.822],[-2.054,-6.217],[1.993,-6.237],[5.278,-3.874],[6.547,-0.033],[5.316,3.822],[2.054,6.217],[-1.993,6.237]],"v":[[19.304,28.834],[0.146,23.68],[-18.962,29.022],[-19.98,9.209],[-30.965,-7.313],[-12.436,-14.404],[-0.118,-29.957],[12.352,-14.526],[30.95,-7.617],[20.128,9.011]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Star","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[249.984,188.998],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Star","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[-50.016,188.998],"ix":2},"a":{"a":0,"k":[249.984,188.998],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Star 3","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[5.278,-3.874],[6.547,-0.032],[5.316,3.822],[2.054,6.217],[-1.993,6.237],[-5.278,3.874],[-6.547,0.032],[-5.316,-3.822],[-2.054,-6.217],[1.993,-6.237]],"o":[[-5.278,3.874],[-6.547,0.032],[-5.316,-3.822],[-2.054,-6.217],[1.993,-6.237],[5.278,-3.874],[6.547,-0.033],[5.316,3.822],[2.054,6.217],[-1.993,6.237]],"v":[[19.304,28.834],[0.146,23.68],[-18.962,29.022],[-19.98,9.209],[-30.965,-7.313],[-12.436,-14.404],[-0.118,-29.957],[12.352,-14.526],[30.95,-7.617],[20.128,9.011]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Star","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[249.984,188.998],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Star","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[99.984,188.998],"ix":2},"a":{"a":0,"k":[249.984,188.998],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Star 2","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[5.278,-3.874],[6.547,-0.032],[5.316,3.822],[2.054,6.217],[-1.993,6.237],[-5.278,3.874],[-6.547,0.032],[-5.316,-3.822],[-2.054,-6.217],[1.993,-6.237]],"o":[[-5.278,3.874],[-6.547,0.032],[-5.316,-3.822],[-2.054,-6.217],[1.993,-6.237],[5.278,-3.874],[6.547,-0.033],[5.316,3.822],[2.054,6.217],[-1.993,6.237]],"v":[[19.304,28.834],[0.146,23.68],[-18.962,29.022],[-19.98,9.209],[-30.965,-7.313],[-12.436,-14.404],[-0.118,-29.957],[12.352,-14.526],[30.95,-7.617],[20.128,9.011]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Star","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[249.984,188.998],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Star","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[249.984,188.998],"ix":2},"a":{"a":0,"k":[249.984,188.998],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Star","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":310,"st":10,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":"Black Stand 2","parent":14,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-24.605,0],[0,0],[18.303,0]],"o":[[-18.303,0],[0,0],[24.605,0],[0,0]],"v":[[-42.653,-29.114],[-53.962,29.114],[53.962,29.114],[42.653,-29.114]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.349019616842,0.345098048449,0.43137255311,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Black Stand","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":310,"st":10,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":"White Stand 4 :M","parent":14,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":10,"s":[-225,-1.544,0],"to":[75,0,0],"ti":[-75,0,0]},{"t":50,"s":[225,-1.544,0]}],"ix":2,"l":2},"a":{"a":0,"k":[24.984,347.302,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.323,0],[0,0],[-1.582,-4.024],[0,0],[4.323,0],[0,0],[-1.582,4.024],[0,0]],"o":[[0,0],[4.323,0],[0,0],[1.582,4.024],[0,0],[-4.323,0],[0,0],[1.582,-4.024]],"v":[[-25.949,-12.268],[25.998,-12.268],[33.803,-4.464],[37.313,4.464],[31.758,12.268],[-32.174,12.268],[-37.263,4.464],[-33.753,-4.464]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"White Stand","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[249.984,347.302],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"White Stand","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[-200.016,347.302],"ix":2},"a":{"a":0,"k":[249.984,347.302],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"White Stand 4","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.323,0],[0,0],[-1.582,-4.024],[0,0],[4.323,0],[0,0],[-1.582,4.024],[0,0]],"o":[[0,0],[4.323,0],[0,0],[1.582,4.024],[0,0],[-4.323,0],[0,0],[1.582,-4.024]],"v":[[-25.949,-12.268],[25.998,-12.268],[33.803,-4.464],[37.313,4.464],[31.758,12.268],[-32.174,12.268],[-37.263,4.464],[-33.753,-4.464]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"White Stand","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[249.984,347.302],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"White Stand","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[-50.016,347.302],"ix":2},"a":{"a":0,"k":[249.984,347.302],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"White Stand 3","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.323,0],[0,0],[-1.582,-4.024],[0,0],[4.323,0],[0,0],[-1.582,4.024],[0,0]],"o":[[0,0],[4.323,0],[0,0],[1.582,4.024],[0,0],[-4.323,0],[0,0],[1.582,-4.024]],"v":[[-25.949,-12.268],[25.998,-12.268],[33.803,-4.464],[37.313,4.464],[31.758,12.268],[-32.174,12.268],[-37.263,4.464],[-33.753,-4.464]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"White Stand","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[249.984,347.302],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"White Stand","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[99.984,347.302],"ix":2},"a":{"a":0,"k":[249.984,347.302],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"White Stand 2","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.323,0],[0,0],[-1.582,-4.024],[0,0],[4.323,0],[0,0],[-1.582,4.024],[0,0]],"o":[[0,0],[4.323,0],[0,0],[1.582,4.024],[0,0],[-4.323,0],[0,0],[1.582,-4.024]],"v":[[-25.949,-12.268],[25.998,-12.268],[33.803,-4.464],[37.313,4.464],[31.758,12.268],[-32.174,12.268],[-37.263,4.464],[-33.753,-4.464]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"White Stand","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[249.984,347.302],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"White Stand","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[249.984,347.302],"ix":2},"a":{"a":0,"k":[249.984,347.302],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"White Stand","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":310,"st":10,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":"Black Stand","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"k":[{"s":[90],"t":2,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[88.052],"t":3,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[83.09],"t":4,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[75.985],"t":5,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[67.277],"t":6,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[57.336],"t":7,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[46.447],"t":8,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[34.86],"t":9,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[10.836],"t":11,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":12,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-6.514],"t":13,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-10.253],"t":14,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-11.772],"t":15,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-11.657],"t":16,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-10.457],"t":17,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-8.646],"t":18,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-6.599],"t":19,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-4.592],"t":20,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-2.804],"t":21,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-1.336],"t":22,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-0.223],"t":23,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.544],"t":24,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.006],"t":25,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.219],"t":26,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.245],"t":27,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.142],"t":28,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.963],"t":29,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.75],"t":30,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.535],"t":31,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.34],"t":32,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.176],"t":33,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.049],"t":34,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-0.04],"t":35,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-0.097],"t":36,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-0.125],"t":37,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-0.132],"t":38,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-0.124],"t":39,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-0.107],"t":40,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-0.085],"t":41,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-0.062],"t":42,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-0.041],"t":43,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-0.023],"t":44,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-0.008],"t":45,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.002],"t":46,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.009],"t":47,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.013],"t":48,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.014],"t":49,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.013],"t":50,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.012],"t":51,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.01],"t":52,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.007],"t":53,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.005],"t":54,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.003],"t":55,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.001],"t":56,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":57,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-0.001],"t":58,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-0.001],"t":59,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-0.001],"t":60,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-0.001],"t":61,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-0.001],"t":62,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-0.001],"t":63,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-0.001],"t":65,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":66,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":67,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":68,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":69,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"p":{"k":[{"s":[138.235,254.547,0],"t":0,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[143.584,250.368,0],"t":1,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[157.812,240.556,0],"t":2,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[179.791,229.215,0],"t":3,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[209.087,221.759,0],"t":4,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[243.189,225.873,0],"t":5,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[274.404,246.799,0],"t":6,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[294.84,281.274,0],"t":7,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[299.502,322.507,0],"t":8,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[282.589,360.014,0],"t":9,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[249.984,377.959,0],"t":10,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[228.111,384.013,0],"t":11,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[215.555,387.488,0],"t":12,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[210.454,388.9,0],"t":13,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[210.841,388.792,0],"t":14,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[214.869,387.678,0],"t":15,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[220.951,385.994,0],"t":16,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[227.823,384.092,0],"t":17,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[234.564,382.227,0],"t":18,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[240.567,380.565,0],"t":19,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[245.498,379.201,0],"t":20,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[249.235,378.166,0],"t":21,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[251.813,377.453,0],"t":22,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[253.364,377.023,0],"t":23,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[254.079,376.826,0],"t":24,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[254.164,376.802,0],"t":25,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[253.818,376.898,0],"t":26,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[253.217,377.064,0],"t":27,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252.503,377.262,0],"t":28,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[251.782,377.461,0],"t":29,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[251.126,377.643,0],"t":30,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[250.576,377.795,0],"t":31,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[250.15,377.913,0],"t":32,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[249.849,377.996,0],"t":33,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[249.66,378.049,0],"t":34,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[249.909,377.98,0],"t":42,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,29.114,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[0,0,100]},{"t":10,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Elastic Controller","np":5,"mn":"Pseudo/MDS Elastic Controller","ix":1,"en":1,"ef":[{"ty":0,"nm":"Amplitude","mn":"Pseudo/MDS Elastic Controller-0001","ix":1,"v":{"a":0,"k":20,"ix":1}},{"ty":0,"nm":"Frequency","mn":"Pseudo/MDS Elastic Controller-0002","ix":2,"v":{"a":0,"k":40,"ix":2}},{"ty":0,"nm":"Decay","mn":"Pseudo/MDS Elastic Controller-0003","ix":3,"v":{"a":0,"k":60,"ix":3}}]},{"ty":5,"nm":"Elastic Controller 2","np":5,"mn":"Pseudo/MDS Elastic Controller","ix":2,"en":1,"ef":[{"ty":0,"nm":"Amplitude","mn":"Pseudo/MDS Elastic Controller-0001","ix":1,"v":{"a":0,"k":20,"ix":1}},{"ty":0,"nm":"Frequency","mn":"Pseudo/MDS Elastic Controller-0002","ix":2,"v":{"a":0,"k":40,"ix":2}},{"ty":0,"nm":"Decay","mn":"Pseudo/MDS Elastic Controller-0003","ix":3,"v":{"a":0,"k":60,"ix":3}}]}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-24.605,0],[0,0],[18.303,0]],"o":[[-18.303,0],[0,0],[24.605,0],[0,0]],"v":[[-42.653,-29.114],[-53.962,29.114],[53.962,29.114],[42.653,-29.114]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.349019616842,0.345098048449,0.43137255311,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Black Stand","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":310,"st":10,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":"Cup","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,-152.895,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-11.815,0],[0,0],[1.176,-11.756],[0,0],[5.492,54.916],[0,0]],"o":[[0,0],[11.815,0],[0,0],[-5.492,54.916],[0,0],[-1.176,-11.756]],"v":[[-49.55,-73.91],[49.55,-73.91],[70.876,-52.583],[62.346,32.723],[-62.346,32.723],[-70.876,-52.583]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.705882370472,0.247058823705,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Cup","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":310,"st":10,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":"Stand","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,-56.636,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[19.235,36.65],[0,0],[-15.853,-38.082],[0,0]],"o":[[0,0],[-20.405,35.342],[0,0],[17.561,-38.659]],"v":[[-33.841,-56.55],[33.841,-56.55],[25.31,56.55],[-25.31,56.55]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.525490224361,0.270588248968,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Stand","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":310,"st":10,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":"Shape Layer 3","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0.016,54.049,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":10,"s":[{"i":[[0,0],[11.928,-26.533],[-4,-20],[1.5,-2]],"o":[[-4.5,-7],[-12.25,27.25],[0.88,4.401],[-1.5,2]],"v":[[-64.5,-87],[-116.25,-85.75],[-62.5,-7],[-65.5,4]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":18,"s":[{"i":[[0,0],[-11.928,-26.533],[4,-20],[-1.5,-2]],"o":[[4.5,-7],[12.25,27.25],[-0.88,4.401],[1.5,2]],"v":[[64.42,-87],[116.17,-85.75],[62.42,-7],[65.42,4]],"c":false}]},{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":24,"s":[{"i":[[0,0],[11.928,-26.533],[-4,-20],[1.5,-2]],"o":[[-4.5,-7],[-12.25,27.25],[0.88,4.401],[-1.5,2]],"v":[[-64.5,-87],[-116.25,-85.75],[-62.5,-7],[-65.5,4]],"c":false}]},{"t":50,"s":[{"i":[[0,0],[-11.928,-26.533],[4,-20],[-1.5,-2]],"o":[[4.5,-7],[12.25,27.25],[-0.88,4.401],[1.5,2]],"v":[[64.42,-87],[116.17,-85.75],[62.42,-7],[65.42,4]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.525490196078,0.270588235294,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":20,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[43.313,-47.836],"ix":2},"a":{"a":0,"k":[43.313,-47.836],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":18,"op":24,"st":10,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":"Shape Layer 6","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0.016,54.049,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[-100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":10,"s":[{"i":[[0,0],[11.928,-26.533],[-4,-20],[1.5,-2]],"o":[[-4.5,-7],[-12.25,27.25],[0.88,4.401],[-1.5,2]],"v":[[-64.5,-87],[-116.25,-85.75],[-62.5,-7],[-65.5,4]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":18,"s":[{"i":[[0,0],[-11.928,-26.533],[4,-20],[-1.5,-2]],"o":[[4.5,-7],[12.25,27.25],[-0.88,4.401],[1.5,2]],"v":[[64.42,-87],[116.17,-85.75],[62.42,-7],[65.42,4]],"c":false}]},{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":24,"s":[{"i":[[0,0],[11.928,-26.533],[-4,-20],[1.5,-2]],"o":[[-4.5,-7],[-12.25,27.25],[0.88,4.401],[-1.5,2]],"v":[[-64.5,-87],[-116.25,-85.75],[-62.5,-7],[-65.5,4]],"c":false}]},{"t":50,"s":[{"i":[[0,0],[-11.928,-26.533],[4,-20],[-1.5,-2]],"o":[[4.5,-7],[12.25,27.25],[-0.88,4.401],[1.5,2]],"v":[[64.42,-87],[116.17,-85.75],[62.42,-7],[65.42,4]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.525490196078,0.270588235294,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":20,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-78.173,-47.836],"ix":2},"a":{"a":0,"k":[-78.173,-47.836],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":24,"op":310,"st":10,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":"Shape Layer 2","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0.016,54.049,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[-100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":10,"s":[{"i":[[0,0],[11.928,-26.533],[-4,-20],[1.5,-2]],"o":[[-4.5,-7],[-12.25,27.25],[0.88,4.401],[-1.5,2]],"v":[[-64.5,-87],[-116.25,-85.75],[-62.5,-7],[-65.5,4]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":18,"s":[{"i":[[0,0],[-11.928,-26.533],[4,-20],[-1.5,-2]],"o":[[4.5,-7],[12.25,27.25],[-0.88,4.401],[1.5,2]],"v":[[64.42,-87],[116.17,-85.75],[62.42,-7],[65.42,4]],"c":false}]},{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":24,"s":[{"i":[[0,0],[11.928,-26.533],[-4,-20],[1.5,-2]],"o":[[-4.5,-7],[-12.25,27.25],[0.88,4.401],[-1.5,2]],"v":[[-64.5,-87],[-116.25,-85.75],[-62.5,-7],[-65.5,4]],"c":false}]},{"t":50,"s":[{"i":[[0,0],[-11.928,-26.533],[4,-20],[-1.5,-2]],"o":[[4.5,-7],[12.25,27.25],[-0.88,4.401],[1.5,2]],"v":[[64.42,-87],[116.17,-85.75],[62.42,-7],[65.42,4]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.525490196078,0.270588235294,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":20,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-78.173,-47.836],"ix":2},"a":{"a":0,"k":[-78.173,-47.836],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":18,"st":10,"bm":0},{"ddd":0,"ind":21,"ty":0,"nm":"Pre-comp 1","refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":60,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2,"l":2},"a":{"a":0,"k":[250,250,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":500,"h":500,"ip":16,"op":316,"st":16,"bm":0},{"ddd":0,"ind":22,"ty":0,"nm":"Pre-comp 1","refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":45,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2,"l":2},"a":{"a":0,"k":[250,250,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":500,"h":500,"ip":11,"op":311,"st":11,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/app/src/main/res/values-night/ic_launcher_background.xml b/app/src/main/res/values-night/ic_launcher_background.xml new file mode 100644 index 0000000..c0f8818 --- /dev/null +++ b/app/src/main/res/values-night/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #000 + \ No newline at end of file diff --git a/app/src/main/res/values-v31/themes.xml b/app/src/main/res/values-v31/themes.xml new file mode 100644 index 0000000..2ce2d86 --- /dev/null +++ b/app/src/main/res/values-v31/themes.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/ic_launcher_background.xml b/app/src/main/res/values/ic_launcher_background.xml new file mode 100644 index 0000000..c5d5899 --- /dev/null +++ b/app/src/main/res/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #FFFFFF + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..9f77183 --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + hydro + \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml new file mode 100644 index 0000000..3f286bc --- /dev/null +++ b/app/src/main/res/values/themes.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/backup_rules.xml b/app/src/main/res/xml/backup_rules.xml new file mode 100644 index 0000000..fa0f996 --- /dev/null +++ b/app/src/main/res/xml/backup_rules.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/data_extraction_rules.xml b/app/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000..6cebe05 --- /dev/null +++ b/app/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..7012ee7 --- /dev/null +++ b/build.gradle @@ -0,0 +1,8 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +plugins { + id "com.android.application" version "8.1.0" apply false + id "org.jetbrains.kotlin.android" version "1.9.0" apply false + id "org.jetbrains.kotlin.plugin.serialization" version "1.9.0" apply false + id "com.google.devtools.ksp" version "1.9.0-1.0.13" apply false + id "org.jlleitschuh.gradle.ktlint" version "11.3.2" apply false +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..3c5031e --- /dev/null +++ b/gradle.properties @@ -0,0 +1,23 @@ +# 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=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Kotlin code style for this project: "official" or "obsolete": +kotlin.code.style=official +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..e708b1c Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..df7f0c0 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed Jul 12 18:38:17 CEST 2023 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..4f906e0 --- /dev/null +++ b/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..ac1b06f --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..088beb9 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,16 @@ +pluginManagement { + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} +rootProject.name = "hydro" +include ':app'