Full Code of bk20dev/forest for AI

main 42b6b3ded72f cached
144 files
247.3 KB
73.7k tokens
1 requests
Download .txt
Showing preview only (288K chars total). Download the full file or copy to clipboard to get everything.
Repository: bk20dev/forest
Branch: main
Commit: 42b6b3ded72f
Files: 144
Total size: 247.3 KB

Directory structure:
gitextract__9jakuws/

├── .gitignore
├── .idea/
│   ├── .gitignore
│   ├── .name
│   ├── AndroidProjectSystem.xml
│   ├── codeStyles/
│   │   ├── Project.xml
│   │   └── codeStyleConfig.xml
│   ├── compiler.xml
│   ├── deploymentTargetSelector.xml
│   ├── deviceManager.xml
│   ├── gradle.xml
│   ├── kotlinc.xml
│   ├── migrations.xml
│   ├── misc.xml
│   ├── runConfigurations.xml
│   └── vcs.xml
├── README.md
├── app/
│   ├── .gitignore
│   ├── build.gradle
│   ├── proguard-rules.pro
│   └── src/
│       ├── androidTest/
│       │   └── java/
│       │       └── pl/
│       │           └── bartek537/
│       │               └── forest/
│       │                   └── ExampleInstrumentedTest.kt
│       ├── main/
│       │   ├── AndroidManifest.xml
│       │   ├── java/
│       │   │   └── pl/
│       │   │       └── bartek537/
│       │   │           └── forest/
│       │   │               ├── ForestApplication.kt
│       │   │               ├── core/
│       │   │               │   ├── data/
│       │   │               │   │   ├── repository/
│       │   │               │   │   │   └── DayRepositoryImpl.kt
│       │   │               │   │   └── source/
│       │   │               │   │       ├── DayDao.kt
│       │   │               │   │       ├── ForestDatabase.kt
│       │   │               │   │       └── util/
│       │   │               │   │           └── Converters.kt
│       │   │               │   ├── domain/
│       │   │               │   │   ├── model/
│       │   │               │   │   │   ├── Day.kt
│       │   │               │   │   │   ├── DaySettings.kt
│       │   │               │   │   │   └── StatsSummary.kt
│       │   │               │   │   ├── repository/
│       │   │               │   │   │   └── DayRepository.kt
│       │   │               │   │   └── usecase/
│       │   │               │   │       ├── DayUseCases.kt
│       │   │               │   │       ├── GetDay.kt
│       │   │               │   │       ├── GetDayImpl.kt
│       │   │               │   │       ├── IncrementStepCount.kt
│       │   │               │   │       └── IncrementStepCountImpl.kt
│       │   │               │   └── presentation/
│       │   │               │       ├── ActivityRecognitionPermissionFragment.kt
│       │   │               │       ├── MainActivity.kt
│       │   │               │       ├── OnboardingActivity.kt
│       │   │               │       └── SplashActivity.kt
│       │   │               ├── progress/
│       │   │               │   ├── ProgressFragment.kt
│       │   │               │   ├── ProgressState.kt
│       │   │               │   └── ProgressViewModel.kt
│       │   │               ├── service/
│       │   │               │   ├── StepCounterController.kt
│       │   │               │   ├── StepCounterEvent.kt
│       │   │               │   ├── StepCounterService.kt
│       │   │               │   ├── StepCounterServiceLauncher.kt
│       │   │               │   └── StepCounterState.kt
│       │   │               ├── settings/
│       │   │               │   ├── SettingsActivity.kt
│       │   │               │   ├── SettingsFragment.kt
│       │   │               │   ├── SettingsViewModel.kt
│       │   │               │   ├── data/
│       │   │               │   │   ├── repository/
│       │   │               │   │   │   └── SettingsRepositoryImpl.kt
│       │   │               │   │   └── source/
│       │   │               │   │       ├── SettingsStore.kt
│       │   │               │   │       └── SettingsStoreImpl.kt
│       │   │               │   └── domain/
│       │   │               │       ├── model/
│       │   │               │       │   └── Settings.kt
│       │   │               │       ├── repository/
│       │   │               │       │   └── SettingsRepository.kt
│       │   │               │       └── usecase/
│       │   │               │           ├── GetSettings.kt
│       │   │               │           ├── SettingsUseCases.kt
│       │   │               │           └── UpdateDaySettings.kt
│       │   │               ├── stats/
│       │   │               │   ├── StatsFragment.kt
│       │   │               │   ├── domain/
│       │   │               │   │   └── usecase/
│       │   │               │   │       ├── GetFirstDate.kt
│       │   │               │   │       ├── GetSummary.kt
│       │   │               │   │       ├── GetWeek.kt
│       │   │               │   │       ├── StatsChartPageUseCases.kt
│       │   │               │   │       ├── StatsDetailsUseCases.kt
│       │   │               │   │       └── StatsSummaryUseCases.kt
│       │   │               │   ├── presentation/
│       │   │               │   │   ├── ChartAdapter.kt
│       │   │               │   │   ├── StatsChartFragment.kt
│       │   │               │   │   ├── StatsChartPageFragment.kt
│       │   │               │   │   ├── StatsChartPageViewModel.kt
│       │   │               │   │   ├── StatsChartState.kt
│       │   │               │   │   ├── StatsDetailsFragment.kt
│       │   │               │   │   ├── StatsDetailsState.kt
│       │   │               │   │   ├── StatsDetailsViewModel.kt
│       │   │               │   │   ├── StatsSummaryFragment.kt
│       │   │               │   │   ├── StatsSummaryState.kt
│       │   │               │   │   └── StatsSummaryViewModel.kt
│       │   │               │   └── util/
│       │   │               │       ├── ContextExtension.kt
│       │   │               │       ├── DayExtension.kt
│       │   │               │       └── LocalDateExtension.kt
│       │   │               └── trees/
│       │   │                   ├── ForestFragment.kt
│       │   │                   ├── ForestState.kt
│       │   │                   ├── ForestViewModel.kt
│       │   │                   └── domain/
│       │   │                       └── usecase/
│       │   │                           ├── ForestUseCases.kt
│       │   │                           └── GetTreeCount.kt
│       │   └── res/
│       │       ├── drawable/
│       │       │   ├── bubble_chart_fill0_wght400_grad0_opsz24.xml
│       │       │   ├── chevron_left_fill0_wght400_grad0_opsz24.xml
│       │       │   ├── chevron_right_fill0_wght400_grad0_opsz24.xml
│       │       │   ├── conversion_path_fill0_wght400_grad0_opsz24.xml
│       │       │   ├── directions_walk_fill0_wght400_grad0_opsz48.xml
│       │       │   ├── do_not_disturb_on_fill0_wght400_grad0_opsz24.xml
│       │       │   ├── forest_fill0_wght400_grad0_opsz24.xml
│       │       │   ├── ic_launcher_background.xml
│       │       │   ├── ic_launcher_foreground.xml
│       │       │   ├── local_fire_department_fill0_wght400_grad0_opsz24.xml
│       │       │   ├── nature_fill0_wght400_grad0_opsz24.xml
│       │       │   ├── shape_chart_bar.xml
│       │       │   ├── shape_circle.xml
│       │       │   ├── shape_divider.xml
│       │       │   ├── shape_ground.xml
│       │       │   ├── show_chart_fill0_wght400_grad0_opsz24.xml
│       │       │   ├── stage_1.xml
│       │       │   ├── stage_2.xml
│       │       │   ├── stage_3.xml
│       │       │   ├── stage_4.xml
│       │       │   ├── stage_5.xml
│       │       │   ├── stage_6.xml
│       │       │   ├── steps_fill0_wght400_grad0_opsz24.xml
│       │       │   └── tree_collected.xml
│       │       ├── layout/
│       │       │   ├── activity_main.xml
│       │       │   ├── activity_onboarding.xml
│       │       │   ├── activity_settings.xml
│       │       │   ├── fragment_activity_recognition_permission.xml
│       │       │   ├── fragment_forest.xml
│       │       │   ├── fragment_progress.xml
│       │       │   ├── fragment_stats.xml
│       │       │   ├── fragment_stats_chart.xml
│       │       │   ├── fragment_stats_details.xml
│       │       │   ├── fragment_stats_page_chart.xml
│       │       │   ├── fragment_stats_summary.xml
│       │       │   └── item_chart_bar.xml
│       │       ├── menu/
│       │       │   ├── bottom_navigation_menu.xml
│       │       │   └── main_menu.xml
│       │       ├── mipmap-anydpi-v26/
│       │       │   ├── ic_launcher.xml
│       │       │   └── ic_launcher_round.xml
│       │       ├── navigation/
│       │       │   ├── nav_graph.xml
│       │       │   └── onboarding_nav_graph.xml
│       │       ├── values/
│       │       │   ├── colors.xml
│       │       │   ├── ic_launcher_background.xml
│       │       │   ├── strings.xml
│       │       │   └── themes.xml
│       │       ├── values-night/
│       │       │   └── themes.xml
│       │       ├── values-v29/
│       │       │   └── themes.xml
│       │       └── xml/
│       │           ├── backup_rules.xml
│       │           ├── data_extraction_rules.xml
│       │           └── settings.xml
│       └── test/
│           └── java/
│               └── pl/
│                   └── bartek537/
│                       └── forest/
│                           └── ExampleUnitTest.kt
├── build.gradle
├── gradle/
│   ├── libs.versions.toml
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
└── settings.gradle

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties


================================================
FILE: .idea/.gitignore
================================================
# Default ignored files
/shelf/
/workspace.xml


================================================
FILE: .idea/.name
================================================
Forest

================================================
FILE: .idea/AndroidProjectSystem.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="AndroidProjectSystem">
    <option name="providerId" value="com.android.tools.idea.GradleProjectSystem" />
  </component>
</project>

================================================
FILE: .idea/codeStyles/Project.xml
================================================
<component name="ProjectCodeStyleConfiguration">
  <code_scheme name="Project" version="173">
    <JavaCodeStyleSettings>
      <option name="IMPORT_LAYOUT_TABLE">
        <value>
          <package name="" withSubpackages="true" static="false" module="true" />
          <package name="android" withSubpackages="true" static="true" />
          <package name="androidx" withSubpackages="true" static="true" />
          <package name="com" withSubpackages="true" static="true" />
          <package name="junit" withSubpackages="true" static="true" />
          <package name="net" withSubpackages="true" static="true" />
          <package name="org" withSubpackages="true" static="true" />
          <package name="java" withSubpackages="true" static="true" />
          <package name="javax" withSubpackages="true" static="true" />
          <package name="" withSubpackages="true" static="true" />
          <emptyLine />
          <package name="android" withSubpackages="true" static="false" />
          <emptyLine />
          <package name="androidx" withSubpackages="true" static="false" />
          <emptyLine />
          <package name="com" withSubpackages="true" static="false" />
          <emptyLine />
          <package name="junit" withSubpackages="true" static="false" />
          <emptyLine />
          <package name="net" withSubpackages="true" static="false" />
          <emptyLine />
          <package name="org" withSubpackages="true" static="false" />
          <emptyLine />
          <package name="java" withSubpackages="true" static="false" />
          <emptyLine />
          <package name="javax" withSubpackages="true" static="false" />
          <emptyLine />
          <package name="" withSubpackages="true" static="false" />
          <emptyLine />
        </value>
      </option>
    </JavaCodeStyleSettings>
    <JetCodeStyleSettings>
      <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
    </JetCodeStyleSettings>
    <codeStyleSettings language="XML">
      <option name="FORCE_REARRANGE_MODE" value="1" />
      <indentOptions>
        <option name="CONTINUATION_INDENT_SIZE" value="4" />
      </indentOptions>
      <arrangement>
        <rules>
          <section>
            <rule>
              <match>
                <AND>
                  <NAME>xmlns:android</NAME>
                  <XML_ATTRIBUTE />
                  <XML_NAMESPACE>^$</XML_NAMESPACE>
                </AND>
              </match>
            </rule>
          </section>
          <section>
            <rule>
              <match>
                <AND>
                  <NAME>xmlns:.*</NAME>
                  <XML_ATTRIBUTE />
                  <XML_NAMESPACE>^$</XML_NAMESPACE>
                </AND>
              </match>
              <order>BY_NAME</order>
            </rule>
          </section>
          <section>
            <rule>
              <match>
                <AND>
                  <NAME>.*:id</NAME>
                  <XML_ATTRIBUTE />
                  <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
                </AND>
              </match>
            </rule>
          </section>
          <section>
            <rule>
              <match>
                <AND>
                  <NAME>.*:name</NAME>
                  <XML_ATTRIBUTE />
                  <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
                </AND>
              </match>
            </rule>
          </section>
          <section>
            <rule>
              <match>
                <AND>
                  <NAME>name</NAME>
                  <XML_ATTRIBUTE />
                  <XML_NAMESPACE>^$</XML_NAMESPACE>
                </AND>
              </match>
            </rule>
          </section>
          <section>
            <rule>
              <match>
                <AND>
                  <NAME>style</NAME>
                  <XML_ATTRIBUTE />
                  <XML_NAMESPACE>^$</XML_NAMESPACE>
                </AND>
              </match>
            </rule>
          </section>
          <section>
            <rule>
              <match>
                <AND>
                  <NAME>.*</NAME>
                  <XML_ATTRIBUTE />
                  <XML_NAMESPACE>^$</XML_NAMESPACE>
                </AND>
              </match>
              <order>BY_NAME</order>
            </rule>
          </section>
          <section>
            <rule>
              <match>
                <AND>
                  <NAME>.*</NAME>
                  <XML_ATTRIBUTE />
                  <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
                </AND>
              </match>
              <order>ANDROID_ATTRIBUTE_ORDER</order>
            </rule>
          </section>
          <section>
            <rule>
              <match>
                <AND>
                  <NAME>.*</NAME>
                  <XML_ATTRIBUTE />
                  <XML_NAMESPACE>.*</XML_NAMESPACE>
                </AND>
              </match>
              <order>BY_NAME</order>
            </rule>
          </section>
        </rules>
      </arrangement>
    </codeStyleSettings>
    <codeStyleSettings language="kotlin">
      <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
    </codeStyleSettings>
  </code_scheme>
</component>

================================================
FILE: .idea/codeStyles/codeStyleConfig.xml
================================================
<component name="ProjectCodeStyleConfiguration">
  <state>
    <option name="USE_PER_PROJECT_SETTINGS" value="true" />
  </state>
</component>

================================================
FILE: .idea/compiler.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="CompilerConfiguration">
    <bytecodeTargetLevel target="21" />
  </component>
</project>

================================================
FILE: .idea/deploymentTargetSelector.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="deploymentTargetSelector">
    <selectionStates>
      <SelectionState runConfigName="app">
        <option name="selectionMode" value="DROPDOWN" />
      </SelectionState>
    </selectionStates>
  </component>
</project>

================================================
FILE: .idea/deviceManager.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="DeviceTable">
    <option name="columnSorters">
      <list>
        <ColumnSorterState>
          <option name="column" value="Name" />
          <option name="order" value="ASCENDING" />
        </ColumnSorterState>
      </list>
    </option>
  </component>
</project>

================================================
FILE: .idea/gradle.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="GradleMigrationSettings" migrationVersion="1" />
  <component name="GradleSettings">
    <option name="linkedExternalProjectsSettings">
      <GradleProjectSettings>
        <option name="testRunner" value="CHOOSE_PER_TEST" />
        <option name="externalProjectPath" value="$PROJECT_DIR$" />
        <option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
        <option name="modules">
          <set>
            <option value="$PROJECT_DIR$" />
            <option value="$PROJECT_DIR$/app" />
          </set>
        </option>
      </GradleProjectSettings>
    </option>
  </component>
</project>

================================================
FILE: .idea/kotlinc.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="KotlinJpsPluginSettings">
    <option name="version" value="1.8.0" />
  </component>
</project>

================================================
FILE: .idea/migrations.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="ProjectMigrations">
    <option name="MigrateToGradleLocalJavaHome">
      <set>
        <option value="$PROJECT_DIR$" />
      </set>
    </option>
  </component>
</project>

================================================
FILE: .idea/misc.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="ExternalStorageConfigurationManager" enabled="true" />
  <component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
    <output url="file://$PROJECT_DIR$/build/classes" />
  </component>
  <component name="ProjectType">
    <option name="id" value="Android" />
  </component>
</project>

================================================
FILE: .idea/runConfigurations.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="RunConfigurationProducerService">
    <option name="ignoredProducers">
      <set>
        <option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
        <option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
        <option value="com.intellij.execution.junit.PatternConfigurationProducer" />
        <option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
        <option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
        <option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
        <option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
        <option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
      </set>
    </option>
  </component>
</project>

================================================
FILE: .idea/vcs.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="VcsDirectoryMappings">
    <mapping directory="$PROJECT_DIR$" vcs="Git" />
  </component>
</project>

================================================
FILE: README.md
================================================
# Forest

Track your daily step count, stay healthy and fight the climate change, one step at a time.

![banner](https://user-images.githubusercontent.com/60577942/221682753-a0251f61-63e0-4ae9-bb40-2854864cebc3.jpg)

## 🦁 Table of Contents

- [Forest](#forest)
    - [🦁 Table of Contents](#-table-of-contents)
    - [🌳 Inspiration](#-inspiration)
    - [🥕 Features](#-features)
    - [🐻‍❄️ Installation and First Launch](#-installation-and-first-launch)
    - [🪴 Technologies](#-technologies)
    - [🐌 Resources](#-resources)

## 🌳 Inspiration

A couple of years ago together with my friends, I took part in a programming competition. The
objective was to build a mobile app that solves a global problem. We didn't win, but the app we
built quickly spread in our families.

## 🥕 Features

<img src="https://user-images.githubusercontent.com/60577942/221682705-39a0e476-bb52-4257-8d3b-5e5a64e72424.gif" alt="application demo" width="270">

<br />

- Track your step count, burned calories, distance traveled and CO₂ saved
- Get rewarded by completing your daily goal and stay motivated
- Get handy notifications when your daily stats get updated
- View a daily history of your progress
- View a detailed summary of your overall progress

## 🐻‍❄️ Installation and First Launch

1. Download the **latest stable** application binary (.apk file) from
   [Releases](https://github.com/bartek537/forest/releases).
2. Tap on the downloaded file and temporarily **allow installation from unknown sources**,
   if prompted (turn it back off after installation).
3. On some devices you may encounter a Play Protect warning, but don't worry — the app is safe
   to use and open-sourced. I'm just an unverified developer.
4. Click “Install” and wait for the app to install.
5. You are now good to go 🚀.

[//]: # (@formatter:off)

> [!CAUTION]
> On most devices you'll need to **turn off the app battery optimizations** for the
> app to count steps accurately. Forest uses a minimal amount of power and it won't impact your
> battery life.
> 
> - Xiaomi devices running MIUI 14
>     1. Go to Settings > Apps > Manage apps > Forest > Battery saver.
>     2. Select "No restrictions".
> 
> - Devices running Lineage OS 22.2
>     1. Go to Settings > Apps > All apps > Forest > App battery usage > Allow background usage
>        (tap on the setting name to enter another menu).
>     2. Enable “Allow background usage”.
>     3. Change battery optimizations to “Unrestricted”.

[//]: # (@formatter:on)

## 🪴 Technologies

- Kotlin
- Flows and Coroutines
- Room
- Shared Preferences
- Navigation Component
- AndroidX Preference Library
- MVVM Design Pattern
- Clean Architecture
- Material You Dynamic Theming

## 🐌 Resources

https://www.notion.so/bartek537/Forest-223bc0c0f5be80bcbb4cc738eefe1ddd

================================================
FILE: app/.gitignore
================================================
/build

================================================
FILE: app/build.gradle
================================================
import org.jetbrains.kotlin.gradle.dsl.JvmTarget

plugins {
    alias libs.plugins.android.application
    alias libs.plugins.kotlin.android
    alias libs.plugins.devtools.ksp
}

android {
    namespace = 'pl.bartek537.forest'
    compileSdk = 36

    defaultConfig {
        applicationId "pl.bk20.forest"
        minSdk 24
        targetSdk 33
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildFeatures {
        viewBinding = true
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_11
        targetCompatibility JavaVersion.VERSION_11
        coreLibraryDesugaringEnabled = true
    }
    kotlin {
        compilerOptions {
            jvmTarget = JvmTarget.JVM_11
        }
    }
}

dependencies {

    implementation libs.koin.androidx

    coreLibraryDesugaring libs.tools.desugar.jdk.libs

    implementation libs.androidx.core.ktx
    implementation libs.androidx.appcompat

    // User interface
    implementation libs.android.material.design
    implementation libs.androidx.activity.ktx
    implementation libs.androidx.constraintlayout
    implementation libs.androidx.lifecycle.runtime.ktx
    implementation libs.androidx.lifecycle.service
    implementation libs.androidx.lifecycle.viewmodel.ktx
    implementation libs.androidx.navigation.fragment.ktx
    implementation libs.androidx.navigation.ui.ktx
    implementation libs.androidx.swiperefreshlayout

    // Persistence
    implementation libs.androidx.preference
    implementation libs.androidx.room.ktx
    ksp libs.androidx.room.compiler

    // Test
    testImplementation libs.junit
    androidTestImplementation libs.androidx.test.espresso
    androidTestImplementation libs.androidx.test.junit
}

================================================
FILE: app/proguard-rules.pro
================================================
# 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

================================================
FILE: app/src/androidTest/java/pl/bartek537/forest/ExampleInstrumentedTest.kt
================================================
package pl.bartek537.forest

import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4

import org.junit.Test
import org.junit.runner.RunWith

import org.junit.Assert.*

/**
 * Instrumented test, which will execute on an Android device.
 *
 * See [testing documentation](http://d.android.com/tools/testing).
 */
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
    @Test
    fun useAppContext() {
        // Context of the app under test.
        val appContext = InstrumentationRegistry.getInstrumentation().targetContext
        assertEquals("pl.bk20.forest", appContext.packageName)
    }
}

================================================
FILE: app/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

    <uses-feature
        android:name="android.hardware.sensor.stepcounter"
        android:required="true" />

    <application
        android:name=".ForestApplication"
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Forest"
        tools:targetApi="31">

        <service android:name=".service.StepCounterService" />

        <receiver
            android:name=".service.StepCounterServiceLauncher"
            android:exported="false">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>

        <activity
            android:name=".core.presentation.SplashActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".core.presentation.OnboardingActivity"
            android:exported="false" />
        <activity
            android:name=".core.presentation.MainActivity"
            android:exported="true"
            android:launchMode="singleInstance" />
        <activity
            android:name=".settings.SettingsActivity"
            android:exported="false"
            android:launchMode="singleTop"
            android:parentActivityName=".core.presentation.MainActivity" />
    </application>

</manifest>

================================================
FILE: app/src/main/java/pl/bartek537/forest/ForestApplication.kt
================================================
package pl.bartek537.forest

import android.app.Application
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import androidx.preference.PreferenceManager
import androidx.room.Room
import com.google.android.material.color.DynamicColors
import kotlinx.coroutines.flow.MutableStateFlow
import org.koin.android.ext.koin.androidContext
import org.koin.android.ext.koin.androidLogger
import org.koin.core.context.startKoin
import org.koin.dsl.module
import pl.bartek537.forest.core.data.source.ForestDatabase
import pl.bartek537.forest.settings.data.source.SettingsStore
import pl.bartek537.forest.settings.data.source.SettingsStoreImpl
import java.time.LocalDate

val forestApplicationModule = module {
}

class ForestApplication : Application() {

    lateinit var settingsStore: SettingsStore
    lateinit var forestDatabase: ForestDatabase

    val currentDate = MutableStateFlow<LocalDate>(LocalDate.now())

    override fun onCreate() {
        super.onCreate()

        startKoin {
            androidLogger()
            androidContext(this@ForestApplication)
            modules(forestApplicationModule)
        }

        DynamicColors.applyToActivitiesIfAvailable(this)
        PreferenceManager.setDefaultValues(this, R.xml.settings, false)
        registerMidnightTimer()

        val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
        settingsStore = SettingsStoreImpl(sharedPreferences)

        forestDatabase = Room.databaseBuilder(
            applicationContext,
            ForestDatabase::class.java,
            ForestDatabase.DATABASE_NAME
        ).build()
    }

    private fun registerMidnightTimer() {
        val intentFilter = IntentFilter().apply {
            addAction(Intent.ACTION_TIME_TICK)
            addAction(Intent.ACTION_TIME_CHANGED)
            addAction(Intent.ACTION_TIMEZONE_CHANGED)
        }
        registerReceiver(midnightBroadcastReceiver, intentFilter)
    }

    private val midnightBroadcastReceiver = object : BroadcastReceiver() {

        override fun onReceive(context: Context?, intent: Intent?) {
            val today = LocalDate.now()
            if (today != currentDate.value) {
                currentDate.value = today
            }
        }
    }
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/core/data/repository/DayRepositoryImpl.kt
================================================
package pl.bartek537.forest.core.data.repository

import kotlinx.coroutines.flow.Flow
import pl.bartek537.forest.core.data.source.DayDao
import pl.bartek537.forest.core.domain.model.Day
import pl.bartek537.forest.core.domain.model.DaySettings
import pl.bartek537.forest.core.domain.repository.DayRepository
import java.time.LocalDate

class DayRepositoryImpl(
    private val dao: DayDao
) : DayRepository {

    override fun getTreeCount(): Flow<Int> {
        return dao.getTreeCount()
    }

    override fun getFirstDay(): Flow<Day?> {
        return dao.getFirstDay()
    }

    override fun getDay(date: LocalDate): Flow<Day?> {
        return dao.getDay(date)
    }

    override suspend fun getAllDays(): List<Day> {
        return dao.getAllDays()
    }

    override fun getDays(range: ClosedRange<LocalDate>): Flow<List<Day>> {
        return dao.getDays(range.start, range.endInclusive)
    }

    override suspend fun upsertDay(day: Day) {
        dao.upsertDay(day)
    }

    override suspend fun updateDaySettings(daySettings: DaySettings) {
        dao.updateDaySettings(daySettings)
    }
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/core/data/source/DayDao.kt
================================================
package pl.bartek537.forest.core.data.source

import androidx.room.*
import kotlinx.coroutines.flow.Flow
import pl.bartek537.forest.core.domain.model.Day
import pl.bartek537.forest.core.domain.model.DaySettings
import java.time.LocalDate

@Dao
interface DayDao {

    @Query("SELECT COUNT(*) FROM day WHERE steps >= goal")
    fun getTreeCount(): Flow<Int>

    @Query("SELECT * FROM day ORDER BY date ASC LIMIT 1")
    fun getFirstDay(): Flow<Day?>

    @Query("SELECT * FROM day WHERE date = :date")
    fun getDay(date: LocalDate): Flow<Day?>

    @Query("SELECT * FROM day")
    suspend fun getAllDays(): List<Day>

    @Query("SELECT * FROM day WHERE date BETWEEN :start AND :endInclusive")
    fun getDays(start: LocalDate, endInclusive: LocalDate): Flow<List<Day>>

    @Upsert
    suspend fun upsertDay(day: Day)

    @Update(entity = Day::class)
    suspend fun updateDaySettings(day: DaySettings)
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/core/data/source/ForestDatabase.kt
================================================
package pl.bartek537.forest.core.data.source

import androidx.room.Database
import androidx.room.RoomDatabase
import androidx.room.TypeConverters
import pl.bartek537.forest.core.data.source.util.Converters
import pl.bartek537.forest.core.domain.model.Day

@Database(entities = [Day::class], version = 1)
@TypeConverters(Converters::class)
abstract class ForestDatabase : RoomDatabase() {

    abstract val dayDao: DayDao

    companion object {
        const val DATABASE_NAME = "forest_database"
    }
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/core/data/source/util/Converters.kt
================================================
package pl.bartek537.forest.core.data.source.util

import androidx.room.TypeConverter
import java.time.LocalDate

@Suppress("unused")
class Converters {
    @TypeConverter
    fun localDateToTimestamp(date: LocalDate): Long {
        return date.toEpochDay()
    }

    @TypeConverter
    fun timestampToLocalDate(timestamp: Long): LocalDate {
        return LocalDate.ofEpochDay(timestamp)
    }
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/core/domain/model/Day.kt
================================================
package pl.bartek537.forest.core.domain.model

import androidx.room.Entity
import androidx.room.PrimaryKey
import pl.bartek537.forest.settings.domain.model.Settings
import java.time.LocalDate

@Entity(tableName = "day")
data class Day(

    @PrimaryKey val date: LocalDate,

    val steps: Int = 0,

    val goal: Int,

    val height: Int = 188,

    val weight: Int = 70,

    val stepLength: Int = 72,

    val pace: Double = 1.0
) {

    companion object

    val distanceTravelled
        get() = run {
            val distanceCentimeters = steps * stepLength
            distanceCentimeters.toDouble() / 100_000
        }

    val calorieBurned
        get() = run {
            val modifier = height / 182.0 + weight / 70.0 - 1
            0.04 * steps * pace * modifier
        }

    val carbonDioxideSaved
        get() = run {
            steps * 0.1925 / 1000.0
        }
}

fun Day.Companion.of(date: LocalDate, settings: Settings, steps: Int = 0): Day {
    return settings.run {
        Day(
            date = date,
            steps = steps,
            goal = dailyGoal,
            height = height,
            weight = weight,
            stepLength = stepLength,
            pace = pace
        )
    }
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/core/domain/model/DaySettings.kt
================================================
package pl.bartek537.forest.core.domain.model

import java.time.LocalDate

data class DaySettings(

    val date: LocalDate,

    val goal: Int,

    val height: Int,

    val weight: Int,

    val stepLength: Int,

    val pace: Double
)


================================================
FILE: app/src/main/java/pl/bartek537/forest/core/domain/model/StatsSummary.kt
================================================
package pl.bartek537.forest.core.domain.model

data class StatsSummary(
    val treesCollected: Int = 0,
    val stepsTaken: Long = 0L,
    val calorieBurned: Double = 0.0,
    val distanceTravelled: Double = 0.0,
    val carbonDioxideSaved: Double = 0.0,
) {
    companion object
}

fun StatsSummary.Companion.of(days: List<Day>): StatsSummary {
    val treesCollected = days.count { it.steps >= it.goal }
    val stepsTaken = days.sumOf { it.steps.toLong() }
    val calorieBurned = days.sumOf { it.calorieBurned }
    val distanceTravelled = days.sumOf { it.distanceTravelled }
    val carbonDioxideSaved = days.sumOf { it.carbonDioxideSaved }
    return StatsSummary(
        treesCollected = treesCollected,
        stepsTaken = stepsTaken,
        calorieBurned = calorieBurned,
        distanceTravelled = distanceTravelled,
        carbonDioxideSaved = carbonDioxideSaved
    )
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/core/domain/repository/DayRepository.kt
================================================
package pl.bartek537.forest.core.domain.repository

import kotlinx.coroutines.flow.Flow
import pl.bartek537.forest.core.domain.model.Day
import pl.bartek537.forest.core.domain.model.DaySettings
import java.time.LocalDate

interface DayRepository {

    fun getTreeCount(): Flow<Int>

    fun getFirstDay(): Flow<Day?>

    fun getDay(date: LocalDate): Flow<Day?>

    suspend fun getAllDays(): List<Day>

    fun getDays(range: ClosedRange<LocalDate>): Flow<List<Day>>

    suspend fun upsertDay(day: Day)

    suspend fun updateDaySettings(daySettings: DaySettings)
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/core/domain/usecase/DayUseCases.kt
================================================
package pl.bartek537.forest.core.domain.usecase

import pl.bartek537.forest.core.domain.repository.DayRepository
import pl.bartek537.forest.settings.domain.repository.SettingsRepository

class DayUseCases(
    dayRepository: DayRepository,
    settingsRepository: SettingsRepository
) {

    val getDay: GetDay = GetDayImpl(dayRepository, settingsRepository)
    val incrementStepCount: IncrementStepCount = IncrementStepCountImpl(dayRepository, getDay)
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/core/domain/usecase/GetDay.kt
================================================
package pl.bartek537.forest.core.domain.usecase

import kotlinx.coroutines.flow.Flow
import pl.bartek537.forest.core.domain.model.Day
import java.time.LocalDate

interface GetDay {

    operator fun invoke(date: LocalDate): Flow<Day>
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/core/domain/usecase/GetDayImpl.kt
================================================
package pl.bartek537.forest.core.domain.usecase

import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import pl.bartek537.forest.core.domain.model.Day
import pl.bartek537.forest.core.domain.model.of
import pl.bartek537.forest.core.domain.repository.DayRepository
import pl.bartek537.forest.settings.domain.repository.SettingsRepository
import java.time.LocalDate

class GetDayImpl(
    private val dayRepository: DayRepository,
    private val settingsRepository: SettingsRepository,
) : GetDay {

    override fun invoke(date: LocalDate): Flow<Day> {
        val settingsFlow = settingsRepository.getSettings()
        val dayFlow = dayRepository.getDay(date)

        return settingsFlow.combine(dayFlow) { settings, day ->
            day ?: Day.of(date, settings, steps = 0)
        }
    }
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/core/domain/usecase/IncrementStepCount.kt
================================================
package pl.bartek537.forest.core.domain.usecase

import java.time.LocalDate

interface IncrementStepCount {

    suspend operator fun invoke(date: LocalDate, by: Int)
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/core/domain/usecase/IncrementStepCountImpl.kt
================================================
package pl.bartek537.forest.core.domain.usecase

import kotlinx.coroutines.flow.first
import pl.bartek537.forest.core.domain.repository.DayRepository
import java.time.LocalDate

class IncrementStepCountImpl(
    private val repository: DayRepository,
    private val getDayUseCase: GetDay
) : IncrementStepCount {

    override suspend fun invoke(date: LocalDate, by: Int) {
        val day = getDayUseCase(date).first()
        val updatedDay = day.copy(steps = day.steps + by)
        repository.upsertDay(updatedDay)
    }
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/core/presentation/ActivityRecognitionPermissionFragment.kt
================================================
package pl.bartek537.forest.core.presentation

import android.Manifest
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.Settings
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.activity.result.contract.ActivityResultContracts.RequestPermission
import androidx.annotation.RequiresApi
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import pl.bartek537.forest.R
import pl.bartek537.forest.databinding.FragmentActivityRecognitionPermissionBinding

class ActivityRecognitionPermissionFragment : Fragment() {

    private var _binding: FragmentActivityRecognitionPermissionBinding? = null
    private val binding get() = _binding!!

    @RequiresApi(Build.VERSION_CODES.Q)
    private val requestPermissionLauncher = registerForActivityResult(RequestPermission()) {
        when (ContextCompat.checkSelfPermission(
            requireContext(), Manifest.permission.ACTIVITY_RECOGNITION
        )) {
            PackageManager.PERMISSION_GRANTED -> openMainActivity()
            PackageManager.PERMISSION_DENIED -> openPermissionSettings()
        }
    }

    private fun openMainActivity() {
        val action = R.id.action_activityRecognitionPermissionFragment_to_mainActivity
        findNavController().navigate(action)
        requireActivity().finish()
    }

    private fun openPermissionSettings() {
        startActivity(Intent().apply {
            action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
            data = Uri.fromParts("package", requireContext().packageName, null)
        })
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
    ): View {
        _binding = FragmentActivityRecognitionPermissionBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
            openMainActivity()
            return
        }
        binding.buttonContinue.setOnClickListener {
            requestPermission()
        }
    }

    @RequiresApi(Build.VERSION_CODES.Q)
    private fun requestPermission() {
        requestPermissionLauncher.launch(Manifest.permission.ACTIVITY_RECOGNITION)
    }
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/core/presentation/MainActivity.kt
================================================
package pl.bartek537.forest.core.presentation

import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.view.WindowCompat
import androidx.navigation.NavController
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.setupActionBarWithNavController
import androidx.navigation.ui.setupWithNavController
import pl.bartek537.forest.R
import pl.bartek537.forest.databinding.ActivityMainBinding
import pl.bartek537.forest.service.StepCounterService
import pl.bartek537.forest.settings.SettingsActivity

class MainActivity : AppCompatActivity() {

    private lateinit var navController: NavController

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        WindowCompat.setDecorFitsSystemWindows(window, false)

        val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        setSupportActionBar(binding.toolbar)

        val navHostFragment = supportFragmentManager
            .findFragmentById(R.id.nav_host_fragment) as NavHostFragment
        navController = navHostFragment.navController

        val appBarConfiguration = AppBarConfiguration(
            setOf(
                R.id.progressFragment,
                R.id.statsFragment,
                R.id.forestFragment,
            )
        )

        setupActionBarWithNavController(navController, appBarConfiguration)
        binding.bottomNavigation.setupWithNavController(navController)

        startStepCounterService()

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            askForNotificationPermission()
        }
    }

    private val requestPermissionLauncher =
        registerForActivityResult(ActivityResultContracts.RequestPermission()) {}

    @RequiresApi(Build.VERSION_CODES.TIRAMISU)
    private fun askForNotificationPermission() {
        val notificationPermission = android.Manifest.permission.POST_NOTIFICATIONS
        val notificationPermissionStatus = ContextCompat
            .checkSelfPermission(this, notificationPermission)
        if (notificationPermissionStatus == PackageManager.PERMISSION_DENIED) {
            requestPermissionLauncher.launch(notificationPermission)
        }
    }

    private fun startStepCounterService() {
        val intent = Intent(this, StepCounterService::class.java)
        ContextCompat.startForegroundService(this, intent)
    }

    override fun onCreateOptionsMenu(menu: Menu?): Boolean {
        menuInflater.inflate(R.menu.main_menu, menu)
        return true
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) {
        R.id.settings -> {
            openSettings()
            true
        }
        else -> false
    }

    private fun openSettings() {
        val settingsIntent = Intent(this, SettingsActivity::class.java)
        startActivity(settingsIntent)
    }
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/core/presentation/OnboardingActivity.kt
================================================
package pl.bartek537.forest.core.presentation

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.NavController
import androidx.navigation.fragment.NavHostFragment
import pl.bartek537.forest.R
import pl.bartek537.forest.databinding.ActivityOnboardingBinding

class OnboardingActivity : AppCompatActivity() {

    private lateinit var navController: NavController

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val binding = ActivityOnboardingBinding.inflate(layoutInflater)
        setContentView(binding.root)

        val navHostFragment = supportFragmentManager
            .findFragmentById(R.id.nav_host_fragment) as NavHostFragment
        navController = navHostFragment.navController
    }

    override fun onSupportNavigateUp(): Boolean {
        return navController.navigateUp() || super.onSupportNavigateUp()
    }
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/core/presentation/SplashActivity.kt
================================================
package pl.bartek537.forest.core.presentation

import android.Manifest
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat

@SuppressLint("CustomSplashScreen")
class SplashActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        if (shouldOpenOnboarding()) {
            openOnboardingActivity()
        } else {
            openMainActivity()
        }
        finish()
    }

    private fun shouldOpenOnboarding(): Boolean {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
            return false
        }
        val permission = Manifest.permission.ACTIVITY_RECOGNITION
        return !hasPermission(this, permission)
    }

    private fun openOnboardingActivity() {
        val intent = Intent(this, OnboardingActivity::class.java)
        startActivity(intent)
    }

    private fun openMainActivity() {
        val intent = Intent(this, MainActivity::class.java)
        startActivity(intent)
    }

    @Suppress("SameParameterValue")
    private fun hasPermission(context: Context, permission: String): Boolean {
        val status = ContextCompat.checkSelfPermission(context, permission)
        return status == PackageManager.PERMISSION_GRANTED
    }
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/progress/ProgressFragment.kt
================================================
package pl.bartek537.forest.progress

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import kotlinx.coroutines.launch
import pl.bartek537.forest.R
import pl.bartek537.forest.databinding.FragmentProgressBinding
import java.text.DecimalFormat

class ProgressFragment : Fragment() {

    private val viewModel: ProgressViewModel by activityViewModels { ProgressViewModel }

    private var _binding: FragmentProgressBinding? = null
    private val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        _binding = FragmentProgressBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        viewLifecycleOwner.lifecycleScope.launch {
            viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.progress.collect { progress -> updateUserInterface(progress) }
            }
        }
    }

    private fun updateUserInterface(state: ProgressState) {
        updateProgress(state)
        updateTree(state)
        updateTiles(state)
    }

    private fun updateProgress(state: ProgressState) = state.apply {
        val numberFormat = DecimalFormat.getIntegerInstance()
        val formattedStepCount = numberFormat.format(stepsTaken)
        val dailyGoalStepCount = numberFormat.format(dailyGoal)
        val dailyGoalText = getString(R.string.step_goal, dailyGoalStepCount)
        binding.apply {
            textStepCount.text = formattedStepCount
            textDailyGoal.text = dailyGoalText
            progressDailyGoal.max = dailyGoal
            progressDailyGoal.progress = stepsTaken
        }
    }

    private fun updateTree(state: ProgressState) = state.apply {
        val treeResource = getTreeResource(stepsTaken.toDouble() / dailyGoal)
        binding.imageTree.setImageResource(treeResource)
    }

    private fun updateTiles(state: ProgressState) = state.apply {
        val calorieText = getString(
            R.string.calorie_burned_format, calorieBurned
        )
        val distanceText = getString(
            R.string.distance_travelled_format, distanceTravelled
        )
        val carbonDioxideText = getString(
            R.string.carbon_dioxide_saved_format, carbonDioxideSaved
        )
        binding.apply {
            textCalorieBurned.text = calorieText
            textDistanceTravelled.text = distanceText
            textCarbonDioxideSaved.text = carbonDioxideText
        }
    }

    private fun getTreeResource(progress: Double) =
        when {
            progress < .2 -> R.drawable.stage_1
            progress < .4 -> R.drawable.stage_2
            progress < .6 -> R.drawable.stage_3
            progress < .8 -> R.drawable.stage_4
            progress < 1 -> R.drawable.stage_5
            else -> R.drawable.stage_6
        }
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/progress/ProgressState.kt
================================================
package pl.bartek537.forest.progress

import java.time.LocalDate

data class ProgressState(
    val date: LocalDate,
    val stepsTaken: Int,
    val dailyGoal: Int,
    val calorieBurned: Int,
    val distanceTravelled: Double,
    val carbonDioxideSaved: Double,
)

================================================
FILE: app/src/main/java/pl/bartek537/forest/progress/ProgressViewModel.kt
================================================
package pl.bartek537.forest.progress

import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.CreationExtras
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import pl.bartek537.forest.ForestApplication
import pl.bartek537.forest.core.data.repository.DayRepositoryImpl
import pl.bartek537.forest.core.domain.usecase.DayUseCases
import pl.bartek537.forest.settings.data.repository.SettingsRepositoryImpl
import java.time.LocalDate
import kotlin.math.roundToInt

class ProgressViewModel(
    private val dayUseCases: DayUseCases,
    private val currentDateFlow: StateFlow<LocalDate>
) : ViewModel() {

    private val _progress = MutableStateFlow(
        ProgressState(
            date = LocalDate.MIN,
            stepsTaken = 0,
            dailyGoal = 0,
            calorieBurned = 0,
            distanceTravelled = 0.0,
            carbonDioxideSaved = 0.0,
        )
    )
    val progress: StateFlow<ProgressState> = _progress.asStateFlow()

    private var getProgressJob: Job? = null

    init {
        viewModelScope.launch {
            currentDateFlow.collect { date ->
                getProgress(date)
            }
        }
    }

    private fun getProgress(date: LocalDate) {
        getProgressJob?.cancel()

        getProgressJob = dayUseCases.getDay(date).onEach { day ->
            _progress.value = progress.value.copy(
                date = day.date,
                stepsTaken = day.steps,
                dailyGoal = day.goal,
                calorieBurned = day.calorieBurned.roundToInt(),
                distanceTravelled = day.distanceTravelled,
                carbonDioxideSaved = day.carbonDioxideSaved,
            )
        }.launchIn(viewModelScope)
    }

    companion object Factory : ViewModelProvider.Factory {

        @Suppress("UNCHECKED_CAST")
        override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {
            val application = checkNotNull(extras[APPLICATION_KEY]) as ForestApplication

            val settingsStore = application.settingsStore
            val settingsRepository = SettingsRepositoryImpl(settingsStore)
            val dayDatabase = application.forestDatabase
            val dayRepository = DayRepositoryImpl(dayDatabase.dayDao)
            val dayUseCases = DayUseCases(dayRepository, settingsRepository)
            val currentDateFlow = application.currentDate

            return ProgressViewModel(dayUseCases, currentDateFlow) as T
        }
    }
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/service/StepCounterController.kt
================================================
package pl.bartek537.forest.service

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import pl.bartek537.forest.core.domain.usecase.DayUseCases
import java.time.LocalDate
import kotlin.math.roundToInt

class StepCounterController(
    private val dayUseCases: DayUseCases,
    private val coroutineScope: CoroutineScope,
    currentDateFlow: StateFlow<LocalDate>,
) {

    private val _stats = MutableStateFlow(StepCounterState(LocalDate.now(), 0, 0, 0.0, 0))
    val stats: StateFlow<StepCounterState> = _stats.asStateFlow()

    private var getStatsJob: Job? = null

    init {
        coroutineScope.launch {
            currentDateFlow.collect { getStats(it) }
        }
    }

    private fun getStats(date: LocalDate) {
        getStatsJob?.cancel()

        getStatsJob = dayUseCases.getDay(date).onEach { day ->
            _stats.value = day.run {
                StepCounterState(
                    date = date,
                    steps = steps,
                    goal = goal,
                    distanceTravelled = distanceTravelled,
                    calorieBurned = calorieBurned.roundToInt()
                )
            }
        }.launchIn(coroutineScope)
    }

    private val rawStepSensorReadings = MutableStateFlow(StepCounterEvent(0, LocalDate.MIN))
    private var previousStepCount: Int? = null

    init {
        rawStepSensorReadings.drop(1).onEach { event ->
            val stepCountDifference = event.stepCount - (previousStepCount ?: event.stepCount)
            previousStepCount = event.stepCount
            dayUseCases.incrementStepCount(event.eventDate, stepCountDifference)
        }.launchIn(coroutineScope)
    }

    fun onStepCountChanged(newStepCount: Int, eventDate: LocalDate) {
        rawStepSensorReadings.value = StepCounterEvent(newStepCount, eventDate)
    }
}


================================================
FILE: app/src/main/java/pl/bartek537/forest/service/StepCounterEvent.kt
================================================
package pl.bartek537.forest.service

import java.time.LocalDate

data class StepCounterEvent(
    val stepCount: Int,
    val eventDate: LocalDate,
)

================================================
FILE: app/src/main/java/pl/bartek537/forest/service/StepCounterService.kt
================================================
package pl.bartek537.forest.service

import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import android.os.Build
import android.os.Build.VERSION_CODES
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleService
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import kotlinx.coroutines.launch
import pl.bartek537.forest.ForestApplication
import pl.bartek537.forest.R
import pl.bartek537.forest.core.data.repository.DayRepositoryImpl
import pl.bartek537.forest.core.domain.usecase.DayUseCases
import pl.bartek537.forest.core.presentation.MainActivity
import pl.bartek537.forest.settings.data.repository.SettingsRepositoryImpl
import java.time.LocalDate

class StepCounterService : LifecycleService(), SensorEventListener {

    private lateinit var sensorManager: SensorManager
    private lateinit var controller: StepCounterController

    companion object {
        private const val NOTIFICATION_CHANNEL_ID = "step_counter_channel"
        private const val NOTIFICATION_ID = 0x1
        private const val PENDING_INTENT_ID = 0x1
    }

    override fun onCreate() {
        super.onCreate()
        if (Build.VERSION.SDK_INT >= VERSION_CODES.O) {
            val notificationChannel = createNotificationChannel()
            registerNotificationChannel(notificationChannel)
        }
        sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
        registerStepCounter(sensorManager)

        // Initialise controller
        val application = application as ForestApplication

        val settingsStore = application.settingsStore
        val settingsRepository = SettingsRepositoryImpl(settingsStore)
        val dayDatabase = application.forestDatabase
        val dayRepository = DayRepositoryImpl(dayDatabase.dayDao)
        val dayUseCases = DayUseCases(dayRepository, settingsRepository)

        controller = StepCounterController(dayUseCases, lifecycleScope, application.currentDate)

        // Create notification
        val notification = createNotification(controller.stats.value)
        startForeground(NOTIFICATION_ID, notification)

        val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                controller.stats.collect {
                    val updatedNotification = createNotification(it)
                    notificationManager.notify(NOTIFICATION_ID, updatedNotification)
                }
            }
        }
    }

    private fun createNotification(state: StepCounterState): Notification = state.run {
        val title = resources.getQuantityString(R.plurals.step_count, steps, steps)
        val progress = if (goal == 0) 0 else steps * 100 / goal
        val content = getString(
            R.string.step_counter_stats, calorieBurned, distanceTravelled, progress
        )

        NotificationCompat.Builder(this@StepCounterService, NOTIFICATION_CHANNEL_ID)
            .setContentIntent(launchApplicationPendingIntent)
            .setSmallIcon(R.drawable.nature_fill0_wght400_grad0_opsz24)
            .setContentTitle(title)
            .setContentText(content)
            .setOnlyAlertOnce(true)
            .setOngoing(true)
            .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
            .setSilent(true)
            .build()
    }

    private val launchApplicationPendingIntent
        get(): PendingIntent {
            val intent = Intent(applicationContext, MainActivity::class.java)
            val flags = PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
            return PendingIntent.getActivity(this, PENDING_INTENT_ID, intent, flags)
        }

    private fun registerStepCounter(sensorManager: SensorManager) {
        val stepCounterSensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER)
        stepCounterSensor?.let {
            sensorManager.registerListener(this, it, SensorManager.SENSOR_DELAY_NORMAL)
        }
    }

    override fun onSensorChanged(event: SensorEvent?) {
        event?.let {
            val eventStepCount = it.values[0].toInt()
            controller.onStepCountChanged(eventStepCount, LocalDate.now())
        }
    }

    override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {}

    override fun onDestroy() {
        super.onDestroy()
        sensorManager.unregisterListener(this)
    }

    @RequiresApi(VERSION_CODES.O)
    private fun createNotificationChannel(): NotificationChannel {
        val name = getString(R.string.step_counter_channel)
        val importance = NotificationManager.IMPORTANCE_DEFAULT

        return NotificationChannel(NOTIFICATION_CHANNEL_ID, name, importance).apply {
            setShowBadge(false)
        }
    }

    @RequiresApi(VERSION_CODES.O)
    private fun registerNotificationChannel(channel: NotificationChannel) {
        val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
        notificationManager.createNotificationChannel(channel)
    }
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/service/StepCounterServiceLauncher.kt
================================================
package pl.bartek537.forest.service

import android.Manifest
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import androidx.core.content.ContextCompat

class StepCounterServiceLauncher : BroadcastReceiver() {

    override fun onReceive(context: Context?, intent: Intent?) {
        context?.run {
            if (intent?.action == Intent.ACTION_BOOT_COMPLETED && hasPermissions(context)) {
                val launchIntent = Intent(applicationContext, StepCounterService::class.java)
                ContextCompat.startForegroundService(applicationContext, launchIntent)
            }
        }
    }

    private fun hasPermissions(context: Context): Boolean {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            if (!hasPermission(context, Manifest.permission.ACTIVITY_RECOGNITION)) {
                return false
            }
        }
        return true
    }

    @Suppress("SameParameterValue")
    private fun hasPermission(context: Context, permission: String): Boolean {
        val status = ContextCompat.checkSelfPermission(context, permission)
        return status == PackageManager.PERMISSION_GRANTED
    }
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/service/StepCounterState.kt
================================================
package pl.bartek537.forest.service

import java.time.LocalDate

data class StepCounterState(
    val date: LocalDate,
    val steps: Int,
    val goal: Int,
    val distanceTravelled: Double,
    val calorieBurned: Int
)

================================================
FILE: app/src/main/java/pl/bartek537/forest/settings/SettingsActivity.kt
================================================
package pl.bartek537.forest.settings

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.WindowCompat
import pl.bartek537.forest.databinding.ActivitySettingsBinding

class SettingsActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        WindowCompat.setDecorFitsSystemWindows(window, false)

        val binding = ActivitySettingsBinding.inflate(layoutInflater)
        setContentView(binding.root)
        setupActionBar(binding)
    }

    private fun setupActionBar(binding: ActivitySettingsBinding) {
        setSupportActionBar(binding.toolbar)
        supportActionBar?.setDisplayHomeAsUpEnabled(true)
    }
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/settings/SettingsFragment.kt
================================================
package pl.bartek537.forest.settings

import android.os.Bundle
import android.text.InputType
import androidx.fragment.app.activityViewModels
import androidx.preference.EditTextPreference
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import pl.bartek537.forest.R

class SettingsFragment : PreferenceFragmentCompat() {

    private val viewModel: SettingsViewModel by activityViewModels { SettingsViewModel }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        viewModel.observeSettingsChanges()
    }

    override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
        setPreferencesFromResource(R.xml.settings, rootKey)

        val dailyGoalPreference = preferenceManager.findPreference<EditTextPreference>("daily_goal")
        dailyGoalPreference?.summaryProvider = Preference.SummaryProvider<EditTextPreference> {
            val dailyGoal = it.text?.toIntOrNull() ?: 0
            resources.getQuantityString(R.plurals.daily_goal_summary, dailyGoal, dailyGoal)
        }

        val numericPreferenceKeys = listOf("daily_goal", "step_length", "height", "weight")
        numericPreferenceKeys.forEach {
            val preference = preferenceManager.findPreference<EditTextPreference>(it)
            preference?.setOnBindEditTextListener { editText ->
                editText.inputType = InputType.TYPE_CLASS_NUMBER
            }
        }
    }
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/settings/SettingsViewModel.kt
================================================
package pl.bartek537.forest.settings

import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.CreationExtras
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import pl.bartek537.forest.ForestApplication
import pl.bartek537.forest.core.data.repository.DayRepositoryImpl
import pl.bartek537.forest.core.domain.model.DaySettings
import pl.bartek537.forest.settings.data.repository.SettingsRepositoryImpl
import pl.bartek537.forest.settings.domain.usecase.SettingsUseCases
import java.time.LocalDate

class SettingsViewModel(
    private val settingsUseCases: SettingsUseCases
) : ViewModel() {

    private var observeSettingsChangesJob: Job? = null

    fun observeSettingsChanges() {
        observeSettingsChangesJob?.cancel()
        observeSettingsChangesJob = settingsUseCases.getSettings().onEach {
            settingsUseCases.updateDaySettings(
                DaySettings(
                    date = LocalDate.now(),
                    goal = it.dailyGoal,
                    height = it.height,
                    weight = it.weight,
                    stepLength = it.stepLength,
                    pace = it.pace
                )
            )
        }.launchIn(viewModelScope)
    }

    companion object Factory : ViewModelProvider.Factory {

        @Suppress("UNCHECKED_CAST")
        override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {
            val application = checkNotNull(extras[APPLICATION_KEY]) as ForestApplication

            val settingsStore = application.settingsStore
            val settingsRepository = SettingsRepositoryImpl(settingsStore)
            val dayDatabase = application.forestDatabase
            val dayRepository = DayRepositoryImpl(dayDatabase.dayDao)

            val settingsUseCases = SettingsUseCases(settingsRepository, dayRepository)

            return SettingsViewModel(settingsUseCases) as T
        }
    }
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/settings/data/repository/SettingsRepositoryImpl.kt
================================================
package pl.bartek537.forest.settings.data.repository

import kotlinx.coroutines.flow.Flow
import pl.bartek537.forest.settings.data.source.SettingsStore
import pl.bartek537.forest.settings.domain.model.Settings
import pl.bartek537.forest.settings.domain.repository.SettingsRepository

class SettingsRepositoryImpl(
    private val settingsStore: SettingsStore
) : SettingsRepository {

    override fun getSettings(): Flow<Settings> {
        return settingsStore.getSettings()
    }
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/settings/data/source/SettingsStore.kt
================================================
package pl.bartek537.forest.settings.data.source

import kotlinx.coroutines.flow.Flow
import pl.bartek537.forest.settings.domain.model.Settings

interface SettingsStore {

    fun getSettings(): Flow<Settings>
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/settings/data/source/SettingsStoreImpl.kt
================================================
package pl.bartek537.forest.settings.data.source

import android.content.SharedPreferences
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import pl.bartek537.forest.settings.domain.model.Settings

class SettingsStoreImpl(
    private val sharedPreferences: SharedPreferences
) : SettingsStore, OnSharedPreferenceChangeListener {

    private val settings: MutableStateFlow<Settings>

    init {
        val parsedSettings = parseSettings(sharedPreferences)
        settings = MutableStateFlow(parsedSettings)
        sharedPreferences.registerOnSharedPreferenceChangeListener(this)
    }

    override fun getSettings(): Flow<Settings> {
        return settings.asStateFlow()
    }

    private fun parseSettings(sharedPreferences: SharedPreferences): Settings =
        sharedPreferences.run {
            Settings(
                dailyGoal = getNumericString("daily_goal", 0),
                stepLength = getNumericString("step_length", 0),
                height = getNumericString("height", 0),
                weight = getNumericString("weight", 0),
                pace = getNumericString("pace", 0.0)
            )
        }

    private fun SharedPreferences.getNumericString(key: String, defaultValue: Int): Int =
        getString(key, "")?.toIntOrNull() ?: defaultValue

    private fun SharedPreferences.getNumericString(key: String, defaultValue: Double): Double =
        getString(key, "")?.toDoubleOrNull() ?: defaultValue

    override fun onSharedPreferenceChanged(
        updatedSharedPreferences: SharedPreferences?,
        key: String?
    ) {
        settings.value = parseSettings(sharedPreferences)
    }
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/settings/domain/model/Settings.kt
================================================
package pl.bartek537.forest.settings.domain.model

data class Settings(
    val dailyGoal: Int,
    val stepLength: Int,
    val height: Int,
    val weight: Int,
    val pace: Double
)


================================================
FILE: app/src/main/java/pl/bartek537/forest/settings/domain/repository/SettingsRepository.kt
================================================
package pl.bartek537.forest.settings.domain.repository

import kotlinx.coroutines.flow.Flow
import pl.bartek537.forest.settings.domain.model.Settings

interface SettingsRepository {

    fun getSettings(): Flow<Settings>
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/settings/domain/usecase/GetSettings.kt
================================================
package pl.bartek537.forest.settings.domain.usecase

import kotlinx.coroutines.flow.Flow
import pl.bartek537.forest.settings.domain.model.Settings
import pl.bartek537.forest.settings.domain.repository.SettingsRepository

interface GetSettings {

    operator fun invoke(): Flow<Settings>
}

class GetSettingsImpl(
    private val repository: SettingsRepository
) : GetSettings {

    override fun invoke(): Flow<Settings> {
        return repository.getSettings()
    }
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/settings/domain/usecase/SettingsUseCases.kt
================================================
package pl.bartek537.forest.settings.domain.usecase

import pl.bartek537.forest.core.domain.repository.DayRepository
import pl.bartek537.forest.settings.domain.repository.SettingsRepository

class SettingsUseCases(
    settingsRepository: SettingsRepository,
    dayRepository: DayRepository,
) {

    val getSettings: GetSettings = GetSettingsImpl(settingsRepository)
    val updateDaySettings: UpdateDaySettings = UpdateDaySettingsImpl(dayRepository)
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/settings/domain/usecase/UpdateDaySettings.kt
================================================
package pl.bartek537.forest.settings.domain.usecase

import pl.bartek537.forest.core.domain.model.DaySettings
import pl.bartek537.forest.core.domain.repository.DayRepository

interface UpdateDaySettings {

    suspend operator fun invoke(daySettings: DaySettings)
}

class UpdateDaySettingsImpl(
    private val dayRepository: DayRepository
) : UpdateDaySettings {

    override suspend fun invoke(daySettings: DaySettings) {
        dayRepository.updateDaySettings(daySettings)
    }
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/stats/StatsFragment.kt
================================================
package pl.bartek537.forest.stats

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.viewpager2.adapter.FragmentStateAdapter
import com.google.android.material.tabs.TabLayoutMediator
import pl.bartek537.forest.R
import pl.bartek537.forest.databinding.FragmentStatsBinding
import pl.bartek537.forest.stats.presentation.StatsDetailsFragment
import pl.bartek537.forest.stats.presentation.StatsSummaryFragment

class StatsFragment : Fragment() {

    private lateinit var binding: FragmentStatsBinding

    companion object {

        private val fragments = listOf(
            R.string.details to { StatsDetailsFragment() },
            R.string.summary to { StatsSummaryFragment() },
        )
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        binding = FragmentStatsBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        val statsPageAdapter = StatsPageAdapter(this)
        binding.pager.apply {
            isUserInputEnabled = false
            adapter = statsPageAdapter
        }
        TabLayoutMediator(binding.tabLayout, binding.pager) { tab, position ->
            val tabTitleRes = fragments[position].first
            tab.text = getString(tabTitleRes)
        }.attach()
    }

    class StatsPageAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) {

        override fun getItemCount(): Int = fragments.size

        override fun createFragment(position: Int): Fragment {
            return fragments[position].second()
        }
    }
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/stats/domain/usecase/GetFirstDate.kt
================================================
package pl.bartek537.forest.stats.domain.usecase

import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import pl.bartek537.forest.core.domain.repository.DayRepository
import java.time.LocalDate

interface GetFirstDate {

    operator fun invoke(): Flow<LocalDate>
}

class GetFirstDateImpl(
    private val dayRepository: DayRepository
) : GetFirstDate {

    override fun invoke(): Flow<LocalDate> {
        return dayRepository.getFirstDay().map { it?.date ?: LocalDate.now() }
    }
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/stats/domain/usecase/GetSummary.kt
================================================
package pl.bartek537.forest.stats.domain.usecase

import pl.bartek537.forest.core.domain.model.StatsSummary
import pl.bartek537.forest.core.domain.model.of
import pl.bartek537.forest.core.domain.repository.DayRepository

interface GetSummary {
    suspend operator fun invoke(): StatsSummary
}

class GetSummaryImpl(
    private val dayRepository: DayRepository
) : GetSummary {

    override suspend operator fun invoke(): StatsSummary {
        val allDays = dayRepository.getAllDays()
        return StatsSummary.of(allDays)
    }
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/stats/domain/usecase/GetWeek.kt
================================================
package pl.bartek537.forest.stats.domain.usecase

import kotlinx.coroutines.flow.Flow
import pl.bartek537.forest.core.domain.model.Day
import pl.bartek537.forest.core.domain.repository.DayRepository
import java.time.LocalDate

interface GetWeek {

    operator fun invoke(startingAt: LocalDate): Flow<List<Day>>
}

class GetWeekImpl(
    private val dayRepository: DayRepository
) : GetWeek {

    override fun invoke(startingAt: LocalDate): Flow<List<Day>> {
        val endingAt = startingAt.plusDays(6)
        return dayRepository.getDays(startingAt..endingAt)
    }
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/stats/domain/usecase/StatsChartPageUseCases.kt
================================================
package pl.bartek537.forest.stats.domain.usecase

import pl.bartek537.forest.core.domain.repository.DayRepository

class StatsChartPageUseCases(
    dayRepository: DayRepository
) {

    val getWeek: GetWeek = GetWeekImpl(dayRepository)
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/stats/domain/usecase/StatsDetailsUseCases.kt
================================================
package pl.bartek537.forest.stats.domain.usecase

import pl.bartek537.forest.core.domain.repository.DayRepository

class StatsDetailsUseCases(
    dayRepository: DayRepository
) {

    val getFirstDate: GetFirstDate = GetFirstDateImpl(dayRepository)
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/stats/domain/usecase/StatsSummaryUseCases.kt
================================================
package pl.bartek537.forest.stats.domain.usecase

import pl.bartek537.forest.core.domain.repository.DayRepository

class StatsSummaryUseCases(
    dayRepository: DayRepository
) {

    val getSummary: GetSummary = GetSummaryImpl(dayRepository)
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/stats/presentation/ChartAdapter.kt
================================================
package pl.bartek537.forest.stats.presentation

import android.content.res.ColorStateList
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.annotation.AttrRes
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import pl.bartek537.forest.databinding.ItemChartBarBinding
import pl.bartek537.forest.stats.util.getThemeColor

class ChartAdapter<T>(
    private val listener: OnValueSelected<T>
) : ListAdapter<ChartAdapter.ChartValue<T>, ChartAdapter.ChartItemViewHolder<T>>(DiffCallback()) {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChartItemViewHolder<T> {
        val layoutInflater = LayoutInflater.from(parent.context)
        val binding = ItemChartBarBinding.inflate(layoutInflater, parent, false)
        return ChartItemViewHolder(binding)
    }

    override fun onBindViewHolder(holder: ChartItemViewHolder<T>, position: Int) {
        val value = getItem(position)
        holder.bind(value, listener)
    }

    class ChartItemViewHolder<T>(
        private val binding: ItemChartBarBinding
    ) : ViewHolder(binding.root) {

        fun bind(chartValue: ChartValue<T>, listener: OnValueSelected<T>) {
            binding.root.setOnClickListener { listener.onSelect(chartValue) }
            binding.textSupporting.apply {
                text = chartValue.label
                val color = context.getThemeColor(chartValue.textColor)
                setTextColor(color)
            }
            binding.barFilled.apply {
                val color = context.getThemeColor(chartValue.barColor)
                backgroundTintList = ColorStateList.valueOf(color)
                val params = layoutParams as ConstraintLayout.LayoutParams
                params.matchConstraintPercentHeight = chartValue.value.toFloat()
                requestLayout()
            }
        }
    }

    private class DiffCallback<T> : DiffUtil.ItemCallback<ChartValue<T>>() {

        override fun areItemsTheSame(oldItem: ChartValue<T>, newItem: ChartValue<T>): Boolean {
            return oldItem.id == newItem.id
        }

        override fun areContentsTheSame(oldItem: ChartValue<T>, newItem: ChartValue<T>): Boolean {
            return oldItem == newItem
        }
    }

    data class ChartValue<T>(

        val id: T,

        val value: Double,

        val label: String,

        @field:AttrRes
        val barColor: Int,

        @field:AttrRes
        val textColor: Int,
    )

    fun interface OnValueSelected<T> {
        fun onSelect(value: ChartValue<T>)
    }
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/stats/presentation/StatsChartFragment.kt
================================================
package pl.bartek537.forest.stats.presentation

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.viewpager2.adapter.FragmentStateAdapter
import kotlinx.coroutines.launch
import pl.bartek537.forest.databinding.FragmentStatsChartBinding
import java.time.LocalDate
import java.time.Period
import java.time.format.DateTimeFormatter

class StatsChartFragment : Fragment() {

    private val statsDetailsViewModel: StatsDetailsViewModel by activityViewModels { StatsDetailsViewModel.Factory }

    private lateinit var binding: FragmentStatsChartBinding
    private lateinit var chartPageAdapter: ChartPageAdapter

    private val dateFormatter = DateTimeFormatter.ofPattern("EEE, MMM dd")

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
    ): View {
        binding = FragmentStatsChartBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        chartPageAdapter = ChartPageAdapter(this)
        binding.viewPagerChart.adapter = chartPageAdapter

        binding.buttonPreviousDay.setOnClickListener { changeSelectedDate(offset = -1) }
        binding.buttonNextDay.setOnClickListener { changeSelectedDate(offset = 1) }

        lifecycleScope.launch {
            viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
                statsDetailsViewModel.day.collect {
                    updateUserInterface(it.date, it.chartDateRange)
                }
            }
        }
    }

    private fun changeSelectedDate(offset: Long) {
        val currentDate = statsDetailsViewModel.day.value.date
        statsDetailsViewModel.selectDay(currentDate.plusDays(offset))
    }

    private fun updateUserInterface(selectedDate: LocalDate, dateRange: ClosedRange<LocalDate>) {
        binding.apply {
            textSelectedDate.text = selectedDate.format(dateFormatter)
            buttonPreviousDay.isVisible = selectedDate.isAfter(dateRange.start)
            buttonNextDay.isVisible = selectedDate.isBefore(dateRange.endInclusive)
            chartPageAdapter.dateRange = dateRange
            scrollChartTo(selectedDate)
        }
    }

    private fun scrollChartTo(
        selectedDate: LocalDate,
    ) {
        val pageIndex = chartPageAdapter.getPageContaining(selectedDate)
        binding.viewPagerChart.currentItem = pageIndex
    }

    class ChartPageAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) {

        var dateRange = LocalDate.now()..LocalDate.now()

        fun getPageContaining(selectedDate: LocalDate): Int {
            val period = Period.between(selectedDate, dateRange.endInclusive)
            return (period.days / 7).coerceIn(0, itemCount)
        }

        override fun getItemCount(): Int = dateRange.run {
            val period = Period.between(start, endInclusive)
            return period.days / 7 + 1
        }

        override fun createFragment(position: Int): Fragment {
            val fragment = StatsChartPageFragment()
            fragment.arguments = Bundle().apply {
                putLong(StatsChartPageFragment.ARG_PAGE_NUMBER, position.toLong())
            }
            return fragment
        }
    }
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/stats/presentation/StatsChartPageFragment.kt
================================================
package pl.bartek537.forest.stats.presentation

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
import pl.bartek537.forest.core.domain.model.Day
import pl.bartek537.forest.databinding.FragmentStatsPageChartBinding
import pl.bartek537.forest.stats.util.toChartValues
import java.lang.Integer.max
import java.time.LocalDate

class StatsChartPageFragment : Fragment() {

    companion object {
        const val ARG_PAGE_NUMBER = "__page_number"
    }

    private lateinit var binding: FragmentStatsPageChartBinding

    private val statsChartPageViewModel: StatsChartPageViewModel by viewModels { StatsChartPageViewModel.Factory }
    private val statsDetailsViewModel: StatsDetailsViewModel by activityViewModels { StatsDetailsViewModel.Factory }

    private var pageNumber: Long = 0

    private val chartAdapter = ChartAdapter {
        statsDetailsViewModel.selectDay(it.id)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        pageNumber = arguments?.getLong(ARG_PAGE_NUMBER) ?: 0
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        binding = FragmentStatsPageChartBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        binding.recyclerViewChart.apply {
            adapter = chartAdapter
        }

        lifecycleScope.launch {
            val activeDayFlow = statsDetailsViewModel.day
            viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.CREATED) {
                launch {
                    val weekFlow = statsChartPageViewModel.week
                    weekFlow.combine(activeDayFlow) { week, activeDay ->
                        updateUserInterface(week, activeDay.date)
                    }.collect()
                }
                launch {
                    activeDayFlow.collect {
                        updateSelectedWeek(it.chartDateRange.endInclusive)
                    }
                }
            }
        }
    }

    private fun updateUserInterface(week: List<Day>, activeDate: LocalDate) {
        val highestChartValue = week.maxOfOrNull { max(it.steps, it.goal) } ?: 1
        val locale = resources.configuration.locales[0]
        val chartValues = week.toChartValues(highestChartValue, locale, activeDate)
        chartAdapter.submitList(chartValues)
    }

    private fun updateSelectedWeek(lastDate: LocalDate) {
        val daysToSubtract = 7 * pageNumber + 6
        val firstDate = lastDate.minusDays(daysToSubtract)
        statsChartPageViewModel.selectWeek(firstDate)
    }
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/stats/presentation/StatsChartPageViewModel.kt
================================================
package pl.bartek537.forest.stats.presentation

import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.CreationExtras
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import pl.bartek537.forest.ForestApplication
import pl.bartek537.forest.core.data.repository.DayRepositoryImpl
import pl.bartek537.forest.core.domain.model.Day
import pl.bartek537.forest.stats.domain.usecase.StatsChartPageUseCases
import pl.bartek537.forest.stats.util.alignWeek
import java.time.LocalDate

class StatsChartPageViewModel(
    private val statsChartPageUseCases: StatsChartPageUseCases
) : ViewModel() {

    private val _week = MutableStateFlow<List<Day>>(emptyList())
    val week: StateFlow<List<Day>> = _week.asStateFlow()

    private var getWeekJob: Job? = null
    fun selectWeek(firstDate: LocalDate) {
        getWeekJob?.cancel()
        getWeekJob = viewModelScope.launch {
            statsChartPageUseCases.getWeek(firstDate).collect { week ->
                _week.value = week.alignWeek(firstDate)
            }
        }
    }

    companion object Factory : ViewModelProvider.Factory {

        @Suppress("UNCHECKED_CAST")
        override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {
            val application =
                checkNotNull(extras[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY]) as ForestApplication

            val forestDatabase = application.forestDatabase
            val dayRepository = DayRepositoryImpl(forestDatabase.dayDao)
            val statsChartPageUseCases = StatsChartPageUseCases(dayRepository)

            return StatsChartPageViewModel(statsChartPageUseCases) as T
        }
    }
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/stats/presentation/StatsChartState.kt
================================================
package pl.bartek537.forest.stats.presentation

import pl.bartek537.forest.core.domain.model.Day
import java.time.LocalDate

data class StatsChartState(
    val week: List<Day>,
    val dateRange: ClosedRange<LocalDate>
) {
    companion object
}

fun StatsChartState.Companion.of(currentDate: LocalDate) = StatsChartState(
    week = emptyList(),
    dateRange = currentDate..currentDate
)

================================================
FILE: app/src/main/java/pl/bartek537/forest/stats/presentation/StatsDetailsFragment.kt
================================================
package pl.bartek537.forest.stats.presentation

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import kotlinx.coroutines.launch
import pl.bartek537.forest.R
import pl.bartek537.forest.databinding.FragmentStatsDetailsBinding

class StatsDetailsFragment : Fragment() {

    private val viewModel: StatsDetailsViewModel by activityViewModels { StatsDetailsViewModel }

    private lateinit var binding: FragmentStatsDetailsBinding

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        binding = FragmentStatsDetailsBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        lifecycleScope.launch {
            viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.CREATED) {
                viewModel.day.collect { updateUserInterface(it) }
            }
        }
    }

    private fun updateUserInterface(state: StatsDetailsState) = state.apply {
        val stepsText = resources.getQuantityString(
            R.plurals.step_count_format, stepsTaken, stepsTaken
        )
        val calorieText = getString(
            R.string.calorie_burned_format, calorieBurned
        )
        val distanceText = getString(
            R.string.distance_travelled_format, distanceTravelled
        )
        val carbonDioxideText = getString(
            R.string.carbon_dioxide_saved_format, carbonDioxideSaved
        )
        binding.apply {
            textStepCount.text = stepsText
            viewGroupTree.isVisible = treeCollected
            textCalorieBurned.text = calorieText
            textDistanceTravelled.text = distanceText
            textCarbonDioxideSaved.text = carbonDioxideText
        }
    }
}


================================================
FILE: app/src/main/java/pl/bartek537/forest/stats/presentation/StatsDetailsState.kt
================================================
package pl.bartek537.forest.stats.presentation

import java.time.LocalDate

data class StatsDetailsState(
    val date: LocalDate,
    val stepsTaken: Int,
    val treeCollected: Boolean,
    val calorieBurned: Int,
    val distanceTravelled: Double,
    val carbonDioxideSaved: Double,
    val chartDateRange: ClosedRange<LocalDate>
)

================================================
FILE: app/src/main/java/pl/bartek537/forest/stats/presentation/StatsDetailsViewModel.kt
================================================
package pl.bartek537.forest.stats.presentation

import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.CreationExtras
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import pl.bartek537.forest.ForestApplication
import pl.bartek537.forest.core.data.repository.DayRepositoryImpl
import pl.bartek537.forest.core.domain.usecase.DayUseCases
import pl.bartek537.forest.settings.data.repository.SettingsRepositoryImpl
import pl.bartek537.forest.stats.domain.usecase.StatsDetailsUseCases
import java.time.LocalDate
import kotlin.math.roundToInt

class StatsDetailsViewModel(
    private val dayUseCases: DayUseCases,
    statsDetailsUseCases: StatsDetailsUseCases,
    currentDateFlow: StateFlow<LocalDate>
) : ViewModel() {

    private val _day = MutableStateFlow(
        StatsDetailsState(
            date = LocalDate.MIN,
            stepsTaken = 0,
            treeCollected = false,
            calorieBurned = 0,
            distanceTravelled = 0.0,
            carbonDioxideSaved = 0.0,
            chartDateRange = currentDateFlow.value..currentDateFlow.value
        )
    )
    val day: StateFlow<StatsDetailsState> = _day.asStateFlow()

    init {
        selectDay(currentDateFlow.value)

        viewModelScope.launch {
            val firstDateFlow = statsDetailsUseCases.getFirstDate()
            firstDateFlow
                .combine(currentDateFlow) { firstDate, currentDate ->
                    firstDate..currentDate
                }.collect { dateRange ->
                    _day.value = day.value.copy(chartDateRange = dateRange)
                }
        }
    }

    private var selectDateJob: Job? = null

    fun selectDay(date: LocalDate) {
        selectDateJob?.cancel()
        selectDateJob = dayUseCases.getDay(date).onEach {
            _day.value = day.value.copy(
                date = it.date,
                stepsTaken = it.steps,
                treeCollected = it.steps >= it.goal,
                calorieBurned = it.calorieBurned.roundToInt(),
                distanceTravelled = it.distanceTravelled,
                carbonDioxideSaved = it.carbonDioxideSaved
            )
        }.launchIn(viewModelScope)
    }

    companion object Factory : ViewModelProvider.Factory {

        @Suppress("UNCHECKED_CAST")
        override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {
            val application = checkNotNull(extras[APPLICATION_KEY]) as ForestApplication

            val dayDatabase = application.forestDatabase
            val dayRepository = DayRepositoryImpl(dayDatabase.dayDao)
            val settingsStore = application.settingsStore
            val settingsRepository = SettingsRepositoryImpl(settingsStore)
            val dayUseCases = DayUseCases(dayRepository, settingsRepository)
            val statsDetailsUseCases = StatsDetailsUseCases(dayRepository)

            return StatsDetailsViewModel(
                dayUseCases,
                statsDetailsUseCases,
                application.currentDate
            ) as T
        }
    }
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/stats/presentation/StatsSummaryFragment.kt
================================================
package pl.bartek537.forest.stats.presentation

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import kotlinx.coroutines.launch
import pl.bartek537.forest.R
import pl.bartek537.forest.databinding.FragmentStatsSummaryBinding
import kotlin.math.roundToInt


class StatsSummaryFragment : Fragment() {

    private lateinit var binding: FragmentStatsSummaryBinding

    private val viewModel: StatsSummaryViewModel by viewModels { StatsSummaryViewModel }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        binding = FragmentStatsSummaryBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        binding.swipeRefreshContainer.setOnRefreshListener {
            viewModel.refreshStatsSummary()
        }

        lifecycleScope.launch {
            viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.CREATED) {
                viewModel.statsSummary.collect { updateUserInterface(it) }
            }
        }
    }

    private fun updateUserInterface(state: StatsSummaryState) = state.apply {
        val treesText = resources.getQuantityString(
            R.plurals.trees_collected_format, treesCollected, treesCollected
        )
        val stepsText = resources.getQuantityString(
            R.plurals.step_count_format, stepsTaken.toInt(), stepsTaken
        )
        val calorieText = getString(
            R.string.calorie_burned_format, calorieBurned.roundToInt()
        )
        val distanceText = getString(
            R.string.distance_travelled_format, distanceTravelled
        )
        val carbonDioxideText = getString(
            R.string.carbon_dioxide_saved_format, carbonDioxideSaved
        )
        binding.apply {
            swipeRefreshContainer.isRefreshing = state.isRefreshing
            textTreesCollected.text = treesText
            textStepCount.text = stepsText
            textCalorieBurned.text = calorieText
            textDistanceTravelled.text = distanceText
            textCarbonDioxideSaved.text = carbonDioxideText
        }
    }
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/stats/presentation/StatsSummaryState.kt
================================================
package pl.bartek537.forest.stats.presentation

data class StatsSummaryState(
    val isRefreshing: Boolean = false,
    val treesCollected: Int = 0,
    val stepsTaken: Long = 0L,
    val calorieBurned: Double = 0.0,
    val distanceTravelled: Double = 0.0,
    val carbonDioxideSaved: Double = 0.0,
)


================================================
FILE: app/src/main/java/pl/bartek537/forest/stats/presentation/StatsSummaryViewModel.kt
================================================
package pl.bartek537.forest.stats.presentation

import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.CreationExtras
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import pl.bartek537.forest.ForestApplication
import pl.bartek537.forest.core.data.repository.DayRepositoryImpl
import pl.bartek537.forest.stats.domain.usecase.StatsSummaryUseCases

class StatsSummaryViewModel(
    private val statsSummaryUseCases: StatsSummaryUseCases
) : ViewModel() {

    private val _statsStatsSummary = MutableStateFlow(StatsSummaryState())
    val statsSummary: StateFlow<StatsSummaryState> = _statsStatsSummary.asStateFlow()

    init {
        refreshStatsSummary()
    }

    private var refreshStatsSummaryJob: Job? = null

    fun refreshStatsSummary() {
        refreshStatsSummaryJob?.cancel()
        refreshStatsSummaryJob = viewModelScope.launch {
            _statsStatsSummary.value = statsSummary.value.copy(
                isRefreshing = true
            )
            val updatedSummary = statsSummaryUseCases.getSummary()
            updatedSummary.run {
                _statsStatsSummary.value = statsSummary.value.copy(
                    isRefreshing = false,
                    treesCollected = treesCollected,
                    stepsTaken = stepsTaken,
                    calorieBurned = calorieBurned,
                    distanceTravelled = distanceTravelled,
                    carbonDioxideSaved = carbonDioxideSaved,
                )
            }
        }
    }

    companion object Factory : ViewModelProvider.Factory {

        @Suppress("UNCHECKED_CAST")
        override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {
            val application =
                checkNotNull(extras[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY]) as ForestApplication
            val dayDatabase = application.forestDatabase
            val dayRepository = DayRepositoryImpl(dayDatabase.dayDao)
            val statsSummaryUseCases = StatsSummaryUseCases(dayRepository)
            return StatsSummaryViewModel(statsSummaryUseCases) as T
        }
    }
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/stats/util/ContextExtension.kt
================================================
package pl.bartek537.forest.stats.util

import android.content.Context
import android.util.TypedValue
import androidx.annotation.AttrRes

fun Context.getThemeColor(
    @AttrRes attrColor: Int
): Int {
    val typedValue = TypedValue()
    theme.resolveAttribute(attrColor, typedValue, true)
    return typedValue.data
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/stats/util/DayExtension.kt
================================================
package pl.bartek537.forest.stats.util

import pl.bartek537.forest.core.domain.model.Day
import java.time.LocalDate

fun List<Day>.alignWeek(
    firstDay: LocalDate,
    lastDay: LocalDate = firstDay.plusDays(6),
): List<Day> {
    val alignedWeek = mutableListOf<Day>()
    for (date in firstDay..lastDay) {
        val currentDay = singleOrNull { it.date == date }
        alignedWeek.add(currentDay ?: Day(date, goal = 0))
    }
    return alignedWeek
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/stats/util/LocalDateExtension.kt
================================================
package pl.bartek537.forest.stats.util

import com.google.android.material.R
import pl.bartek537.forest.core.domain.model.Day
import pl.bartek537.forest.stats.presentation.ChartAdapter
import java.time.LocalDate
import java.time.format.TextStyle
import java.util.*

operator fun ClosedRange<LocalDate>.iterator() = object : Iterator<LocalDate> {

    private var current = start.minusDays(1)

    override fun hasNext(): Boolean {
        return current.isBefore(endInclusive)
    }

    override fun next(): LocalDate {
        if (current.isBefore(endInclusive)) {
            current = current.plusDays(1)
        }
        return current
    }
}

fun List<Day>.toChartValues(
    max: Int,
    locale: Locale,
    activeDay: LocalDate
): List<ChartAdapter.ChartValue<LocalDate>> = map {
    val value = it.steps / max.toDouble()
    val weekdayName = it.date.dayOfWeek.getDisplayName(TextStyle.SHORT, locale)
    val isSelected = it.date.isEqual(activeDay)
    val barColor =
        if (isSelected) android.R.attr.colorPrimary
        else R.attr.colorPrimaryContainer
    val textColor =
        if (isSelected) android.R.attr.colorPrimary
        else R.attr.colorOnSurface
    ChartAdapter.ChartValue(
        it.date,
        value = value,
        label = weekdayName,
        barColor = barColor,
        textColor = textColor
    )
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/trees/ForestFragment.kt
================================================
package pl.bartek537.forest.trees

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.updateLayoutParams
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import kotlinx.coroutines.launch
import pl.bartek537.forest.R
import pl.bartek537.forest.databinding.FragmentForestBinding
import kotlin.random.Random

class ForestFragment : Fragment() {

    private val viewModel: ForestViewModel by viewModels { ForestViewModel.Factory }

    private lateinit var binding: FragmentForestBinding

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        binding = FragmentForestBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        lifecycleScope.launch {
            viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.CREATED) {
                viewModel.trees.collect { updateUserInterface(it) }
            }
        }
    }

    private fun updateUserInterface(forestState: ForestState) {
        val treeCount = forestState.treeCount
        binding.apply {
            textTreesCollected.text = treeCount.toString()
            textTreesCollectedLabel.text = resources.getQuantityString(R.plurals.trees, treeCount)
        }
        generateTrees(forestState.treeCount)
    }

    private fun generateTrees(treeCount: Int) {
        val parentLayout = binding.constraintLayoutTrees
        parentLayout.removeAllViews()
        val gapCount = treeCount + 1
        repeat(treeCount) {
            val fixedPosition = (it + 1.0) / gapCount
            val randomOffset = (Random.nextDouble() - 0.5) / 5
            val horizontalPosition = fixedPosition + randomOffset
            createTree(parentLayout, horizontalPosition)
        }
    }

    private fun createTree(parentLayout: ConstraintLayout, horizontalPosition: Double) {
        val treeImageView = ImageView(context)
        treeImageView.setImageResource(R.drawable.tree_collected)
        parentLayout.addView(treeImageView)
        treeImageView.updateLayoutParams<ConstraintLayout.LayoutParams> {
            startToStart = parentLayout.id
            endToEnd = parentLayout.id
            bottomToBottom = parentLayout.id
            horizontalBias = horizontalPosition.toFloat()
        }
    }
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/trees/ForestState.kt
================================================
package pl.bartek537.forest.trees

data class ForestState(
    val treeCount: Int
)

================================================
FILE: app/src/main/java/pl/bartek537/forest/trees/ForestViewModel.kt
================================================
package pl.bartek537.forest.trees

import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.CreationExtras
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import pl.bartek537.forest.ForestApplication
import pl.bartek537.forest.core.data.repository.DayRepositoryImpl
import pl.bartek537.forest.trees.domain.usecase.ForestUseCases

class ForestViewModel(
    forestUseCases: ForestUseCases
) : ViewModel() {

    private val _trees = MutableStateFlow(ForestState(treeCount = 0))
    val trees: StateFlow<ForestState> = _trees.asStateFlow()

    init {
        viewModelScope.launch {
            forestUseCases.getTreeCount().collect {
                _trees.value = _trees.value.copy(
                    treeCount = it
                )
            }
        }
    }

    object Factory : ViewModelProvider.Factory {

        @Suppress("UNCHECKED_CAST")
        override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {
            val application = checkNotNull(extras[APPLICATION_KEY]) as ForestApplication
            val forestDatabase = application.forestDatabase
            val dayRepository = DayRepositoryImpl(forestDatabase.dayDao)
            val forestUseCases = ForestUseCases(dayRepository)
            return ForestViewModel(forestUseCases) as T
        }
    }
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/trees/domain/usecase/ForestUseCases.kt
================================================
package pl.bartek537.forest.trees.domain.usecase

import pl.bartek537.forest.core.domain.repository.DayRepository

class ForestUseCases(
    dayRepository: DayRepository
) {

    val getTreeCount: GetTreeCount = GetTreeCountImpl(dayRepository)
}

================================================
FILE: app/src/main/java/pl/bartek537/forest/trees/domain/usecase/GetTreeCount.kt
================================================
package pl.bartek537.forest.trees.domain.usecase

import kotlinx.coroutines.flow.Flow
import pl.bartek537.forest.core.domain.repository.DayRepository

interface GetTreeCount {
    operator fun invoke(): Flow<Int>
}

class GetTreeCountImpl(
    private val dayRepository: DayRepository
) : GetTreeCount {

    override fun invoke(): Flow<Int> {
        return dayRepository.getTreeCount()
    }
}

================================================
FILE: app/src/main/res/drawable/bubble_chart_fill0_wght400_grad0_opsz24.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="960"
    android:viewportHeight="960">
  <path
      android:fillColor="#FF000000"
      android:pathData="M280,720q-66,0 -113,-47t-47,-113q0,-66 47,-113t113,-47q66,0 113,47t47,113q0,66 -47,113t-113,47ZM280,640q33,0 56.5,-23.5T360,560q0,-33 -23.5,-56.5T280,480q-33,0 -56.5,23.5T200,560q0,33 23.5,56.5T280,640ZM660,560q-92,0 -156,-64t-64,-156q0,-92 64,-156t156,-64q92,0 156,64t64,156q0,92 -64,156t-156,64ZM580,840q-50,0 -85,-35t-35,-85q0,-50 35,-85t85,-35q50,0 85,35t35,85q0,50 -35,85t-85,35ZM660,480q59,0 99.5,-40.5T800,340q0,-59 -40.5,-99.5T660,200q-59,0 -99.5,40.5T520,340q0,59 40.5,99.5T660,480ZM580,760q17,0 28.5,-11.5T620,720q0,-17 -11.5,-28.5T580,680q-17,0 -28.5,11.5T540,720q0,17 11.5,28.5T580,760ZM660,340ZM280,560ZM580,720Z"/>
</vector>


================================================
FILE: app/src/main/res/drawable/chevron_left_fill0_wght400_grad0_opsz24.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24">
  <path
      android:fillColor="#FF000000"
      android:pathData="m14,18 l-6,-6 6,-6 1.4,1.4 -4.6,4.6 4.6,4.6Z"/>
</vector>


================================================
FILE: app/src/main/res/drawable/chevron_right_fill0_wght400_grad0_opsz24.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24">
  <path
      android:fillColor="#FF000000"
      android:pathData="M9.4,18 L8,16.6l4.6,-4.6L8,7.4 9.4,6l6,6Z"/>
</vector>


================================================
FILE: app/src/main/res/drawable/conversion_path_fill0_wght400_grad0_opsz24.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24">
  <path
      android:fillColor="#FF000000"
      android:pathData="M19,21q-0.975,0 -1.75,-0.562 -0.775,-0.563 -1.075,-1.438H11q-1.65,0 -2.825,-1.175Q7,16.65 7,15q0,-1.65 1.175,-2.825Q9.35,11 11,11h2q0.825,0 1.413,-0.588Q15,9.825 15,9t-0.587,-1.413Q13.825,7 13,7H7.825q-0.325,0.875 -1.087,1.438Q5.975,9 5,9q-1.25,0 -2.125,-0.875T2,6q0,-1.25 0.875,-2.125T5,3q0.975,0 1.738,0.562Q7.5,4.125 7.825,5H13q1.65,0 2.825,1.175Q17,7.35 17,9q0,1.65 -1.175,2.825Q14.65,13 13,13h-2q-0.825,0 -1.412,0.587Q9,14.175 9,15q0,0.825 0.588,1.413Q10.175,17 11,17h5.175q0.325,-0.875 1.088,-1.438Q18.025,15 19,15q1.25,0 2.125,0.875T22,18q0,1.25 -0.875,2.125T19,21ZM5,7q0.425,0 0.713,-0.287Q6,6.425 6,6t-0.287,-0.713Q5.425,5 5,5t-0.713,0.287Q4,5.575 4,6t0.287,0.713Q4.575,7 5,7Z"/>
</vector>


================================================
FILE: app/src/main/res/drawable/directions_walk_fill0_wght400_grad0_opsz48.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="48dp"
    android:height="48dp"
    android:viewportWidth="48"
    android:viewportHeight="48">
  <path
      android:fillColor="#FF000000"
      android:pathData="m13.9,46 l5.8,-29.3 -5.05,2.15v6.65H11.6v-8.65l9.6,-4.05q0.7,-0.3 1.475,-0.375 0.775,-0.075 1.525,0.075 0.85,0.15 1.475,0.55 0.625,0.4 1.025,1l2.1,3.3q1.55,2.4 3.875,3.775T37.65,22.5v3q-3.5,-0.1 -6.175,-1.525Q28.8,22.55 26.65,19.6l-1.9,7.6 4.6,4.15V46h-3V34l-5.4,-4.9L17,46ZM27,10.3q-1.5,0 -2.575,-1.075Q23.35,8.15 23.35,6.65q0,-1.5 1.075,-2.575Q25.5,3 27,3q1.5,0 2.575,1.075Q30.65,5.15 30.65,6.65q0,1.5 -1.075,2.575Q28.5,10.3 27,10.3Z"/>
</vector>


================================================
FILE: app/src/main/res/drawable/do_not_disturb_on_fill0_wght400_grad0_opsz24.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24">
  <path
      android:fillColor="#FF000000"
      android:pathData="M7,13h10v-2L7,11ZM12,22q-2.075,0 -3.9,-0.788 -1.825,-0.787 -3.175,-2.137 -1.35,-1.35 -2.137,-3.175Q2,14.075 2,12t0.788,-3.9q0.787,-1.825 2.137,-3.175 1.35,-1.35 3.175,-2.138Q9.925,2 12,2t3.9,0.787q1.825,0.788 3.175,2.138 1.35,1.35 2.137,3.175Q22,9.925 22,12t-0.788,3.9q-0.787,1.825 -2.137,3.175 -1.35,1.35 -3.175,2.137Q14.075,22 12,22ZM12,20q3.35,0 5.675,-2.325Q20,15.35 20,12q0,-3.35 -2.325,-5.675Q15.35,4 12,4 8.65,4 6.325,6.325 4,8.65 4,12q0,3.35 2.325,5.675Q8.65,20 12,20ZM12,12Z"/>
</vector>


================================================
FILE: app/src/main/res/drawable/forest_fill0_wght400_grad0_opsz24.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24">
  <path
      android:fillColor="#FF000000"
      android:pathData="M7,22v-4L0,18l3.85,-6L2,12L9,2l3,4.3L15,2l7,10h-1.85L24,18h-7v4h-4v-4h-2v4ZM16.725,16h3.625l-3.875,-6h1.675L15,5.5l-1.775,2.525L16,12h-1.85ZM3.65,16h10.7l-3.875,-6h1.675L9,5.5 5.85,10h1.675ZM3.65,16h3.875L5.85,16h6.3,-1.675 3.875ZM16.725,16L14.15,16 16,16h-2.775,4.925 -1.675,3.875ZM13,18h4,-4ZM18.025,18Z"/>
</vector>


================================================
FILE: app/src/main/res/drawable/ic_launcher_background.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="108dp"
    android:height="108dp"
    android:viewportWidth="108"
    android:viewportHeight="108">
    <path
        android:fillColor="#3DDC84"
        android:pathData="M0,0h108v108h-108z" />
    <path
        android:fillColor="#00000000"
        android:pathData="M9,0L9,108"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M19,0L19,108"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M29,0L29,108"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M39,0L39,108"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M49,0L49,108"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M59,0L59,108"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M69,0L69,108"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M79,0L79,108"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M89,0L89,108"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M99,0L99,108"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,9L108,9"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,19L108,19"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,29L108,29"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,39L108,39"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,49L108,49"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,59L108,59"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,69L108,69"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,79L108,79"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,89L108,89"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,99L108,99"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M19,29L89,29"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M19,39L89,39"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M19,49L89,49"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M19,59L89,59"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M19,69L89,69"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M19,79L89,79"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M29,19L29,89"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M39,19L39,89"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M49,19L49,89"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M59,19L59,89"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M69,19L69,89"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M79,19L79,89"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
</vector>


================================================
FILE: app/src/main/res/drawable/ic_launcher_foreground.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="108dp"
    android:height="108dp"
    android:viewportWidth="319"
    android:viewportHeight="188">
  <group android:scaleX="0.64"
      android:scaleY="0.37717867"
      android:translateX="57.42"
      android:translateY="58.545204">
    <group>
      <clip-path
          android:pathData="M0,140h319v48h-319z"/>
      <path
          android:pathData="M159.5,188C247.59,188 319,177.26 319,164C319,150.74 247.59,140 159.5,140C71.41,140 0,150.74 0,164C0,177.26 71.41,188 159.5,188Z"
          android:fillColor="#60A625"/>
      <path
          android:pathData="M159.5,173C192.64,173 219.5,168.97 219.5,164C219.5,159.03 192.64,155 159.5,155C126.36,155 99.5,159.03 99.5,164C99.5,168.97 126.36,173 159.5,173Z"
          android:fillColor="#51901C"/>
    </group>
    <group>
      <clip-path
          android:pathData="M77,0h164v164h-164z"/>
      <path
          android:pathData="M161.97,90.48H156.6C156.6,90.48 151.8,149.84 150.83,161.88C150.78,162.43 150.97,162.97 151.34,163.37C151.71,163.77 152.23,164 152.78,164H165.22C165.76,164 166.29,163.77 166.65,163.37C167.03,162.97 167.21,162.44 167.18,161.9C166.3,149.88 161.97,90.48 161.97,90.48Z"
          android:fillColor="#413022"
          android:fillType="evenOdd"/>
      <path
          android:pathData="M77,22.85C77,22.85 98.82,24.71 119.36,26.45C139.86,28.19 156.12,44.45 157.86,64.95C159.3,81.84 160.8,99.6 161.3,105.37C161.34,105.86 161.16,106.33 160.82,106.67C160.48,107.01 160.01,107.19 159.52,107.15C153.75,106.65 135.99,105.15 119.1,103.71C98.6,101.97 82.34,85.71 80.6,65.21C78.85,44.67 77,22.85 77,22.85Z"
          android:fillColor="#60A625"
          android:fillType="evenOdd"/>
      <path
          android:pathData="M241,22.85C241,22.85 239.15,44.67 237.4,65.21C235.66,85.71 219.4,101.97 198.9,103.71C182.01,105.15 164.26,106.66 158.48,107.15C158,107.19 157.52,107.01 157.18,106.67C156.84,106.33 156.66,105.86 156.7,105.37C157.2,99.6 158.71,81.84 160.14,64.95C161.88,44.45 178.14,28.19 198.64,26.45C219.18,24.71 241,22.85 241,22.85Z"
          android:fillColor="#7EC046"
          android:fillType="evenOdd"/>
    </group>
  </group>
</vector>


================================================
FILE: app/src/main/res/drawable/local_fire_department_fill0_wght400_grad0_opsz24.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24">
  <path
      android:fillColor="#FF000000"
      android:pathData="M6,14q0,1.3 0.525,2.462 0.525,1.163 1.5,2.038Q8,18.375 8,18.275v-0.225q0,-0.8 0.3,-1.5t0.875,-1.275L12,12.5l2.825,2.775q0.575,0.575 0.875,1.275 0.3,0.7 0.3,1.5v0.225q0,0.1 -0.025,0.225 0.975,-0.875 1.5,-2.038Q18,15.3 18,14q0,-1.25 -0.462,-2.363 -0.463,-1.112 -1.338,-1.987 -0.5,0.325 -1.05,0.487 -0.55,0.163 -1.125,0.163 -1.55,0 -2.687,-1.025Q10.2,8.25 10.025,6.75 9.05,7.575 8.3,8.462q-0.75,0.888 -1.262,1.8 -0.513,0.913 -0.775,1.863Q6,13.075 6,14ZM12,15.3 L10.575,16.7q-0.275,0.275 -0.425,0.625 -0.15,0.35 -0.15,0.725 0,0.8 0.588,1.375Q11.175,20 12,20t1.413,-0.575Q14,18.85 14,18.05q0,-0.4 -0.15,-0.738 -0.15,-0.337 -0.425,-0.612ZM12,3v3.3q0,0.85 0.588,1.425 0.587,0.575 1.437,0.575 0.45,0 0.838,-0.187 0.387,-0.188 0.687,-0.563L16,7q1.85,1.05 2.925,2.925Q20,11.8 20,14q0,3.35 -2.325,5.675Q15.35,22 12,22q-3.35,0 -5.675,-2.325Q4,17.35 4,14q0,-3.225 2.163,-6.125Q8.325,4.975 12,3Z"/>
</vector>


================================================
FILE: app/src/main/res/drawable/nature_fill0_wght400_grad0_opsz24.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24">
  <path
      android:fillColor="#FF000000"
      android:pathData="M5,22v-2h6v-4L9,16q-2.075,0 -3.537,-1.463Q4,13.075 4,11q0,-1.5 0.825,-2.763Q5.65,6.975 7.05,6.4q0.225,-1.875 1.638,-3.138Q10.1,2 12,2t3.312,1.262Q16.725,4.525 16.95,6.4q1.4,0.575 2.225,1.837Q20,9.5 20,11q0,2.075 -1.462,3.537Q17.075,16 15,16h-2v4h6v2ZM9,14h6q1.25,0 2.125,-0.875T18,11q0,-0.9 -0.512,-1.65 -0.513,-0.75 -1.338,-1.1L15.1,7.8l-0.15,-1.15q-0.15,-1.125 -0.987,-1.888Q13.125,4 12,4t-1.962,0.762Q9.2,5.525 9.05,6.65L8.9,7.8l-1.05,0.45q-0.825,0.35 -1.337,1.1Q6,10.1 6,11q0,1.25 0.875,2.125T9,14ZM12,9Z"/>
</vector>


================================================
FILE: app/src/main/res/drawable/shape_chart_bar.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    tools:tint="?attr/colorSurfaceVariant">

    <corners
        android:topLeftRadius="4dp"
        android:topRightRadius="4dp" />

</shape>

================================================
FILE: app/src/main/res/drawable/shape_circle.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    tools:tint="?attr/colorSurface">

    <corners android:radius="999dp" />
</shape>

================================================
FILE: app/src/main/res/drawable/shape_divider.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<inset xmlns:android="http://schemas.android.com/apk/res/android"
    android:insetLeft="80dp">
    <shape>
        <size android:height="1dp" />
        <solid android:color="?attr/colorOutlineVariant" />
    </shape>
</inset>

================================================
FILE: app/src/main/res/drawable/shape_ground.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="?attr/colorSecondaryContainer" />
    <corners
        android:topLeftRadius="12dp"
        android:topRightRadius="12dp" />
</shape>

================================================
FILE: app/src/main/res/drawable/show_chart_fill0_wght400_grad0_opsz24.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24">
  <path
      android:fillColor="#FF000000"
      android:pathData="M3.5,18.5 L2,17l7.5,-7.5 4,4 7.1,-8L22,6.9l-8.5,9.6 -4,-4Z"/>
</vector>


================================================
FILE: app/src/main/res/drawable/stage_1.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="500dp"
    android:height="500dp"
    android:viewportWidth="500"
    android:viewportHeight="500">
  <group>
    <clip-path
        android:pathData="M91,452h319v48h-319z"/>
    <path
        android:pathData="M250.5,500C338.59,500 410,489.26 410,476C410,462.74 338.59,452 250.5,452C162.41,452 91,462.74 91,476C91,489.26 162.41,500 250.5,500Z"
        android:fillColor="#60A625"/>
    <path
        android:pathData="M250.5,485C283.64,485 310.5,480.97 310.5,476C310.5,471.03 283.64,467 250.5,467C217.36,467 190.5,471.03 190.5,476C190.5,480.97 217.36,485 250.5,485Z"
        android:fillColor="#51901C"/>
  </group>
  <group>
    <clip-path
        android:pathData="M168,312h164v164h-164z"/>
    <path
        android:pathData="M252.97,402.48H247.6C247.6,402.48 242.8,461.84 241.83,473.88C241.78,474.42 241.97,474.97 242.34,475.37C242.71,475.77 243.23,476 243.78,476H256.22C256.77,476 257.29,475.77 257.65,475.37C258.03,474.97 258.21,474.44 258.18,473.9C257.3,461.88 252.97,402.48 252.97,402.48Z"
        android:fillColor="#413022"
        android:fillType="evenOdd"/>
    <path
        android:pathData="M168,334.85C168,334.85 189.82,336.7 210.36,338.45C230.86,340.19 247.12,356.45 248.86,376.95C250.3,393.84 251.8,411.6 252.3,417.37C252.34,417.86 252.16,418.33 251.82,418.67C251.48,419.01 251.01,419.19 250.52,419.15C244.75,418.65 226.99,417.15 210.1,415.71C189.6,413.97 173.34,397.71 171.6,377.21C169.85,356.67 168,334.85 168,334.85Z"
        android:fillColor="#60A625"
        android:fillType="evenOdd"/>
    <path
        android:pathData="M332,334.85C332,334.85 330.15,356.67 328.4,377.21C326.66,397.71 310.4,413.97 289.9,415.71C273.01,417.15 255.26,418.66 249.48,419.15C249,419.19 248.52,419.01 248.18,418.67C247.84,418.33 247.66,417.86 247.7,417.37C248.2,411.6 249.71,393.84 251.14,376.95C252.88,356.45 269.14,340.19 289.64,338.45C310.18,336.71 332,334.85 332,334.85Z"
        android:fillColor="#7EC046"
        android:fillType="evenOdd"/>
  </group>
</vector>


================================================
FILE: app/src/main/res/drawable/stage_2.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="500dp"
    android:height="500dp"
    android:viewportWidth="500"
    android:viewportHeight="500">
  <group>
    <clip-path
        android:pathData="M91,452h319v48h-319z"/>
    <path
        android:pathData="M250.5,500C338.59,500 410,489.26 410,476C410,462.74 338.59,452 250.5,452C162.41,452 91,462.74 91,476C91,489.26 162.41,500 250.5,500Z"
        android:fillColor="#60A625"/>
    <path
        android:pathData="M250.5,485C283.64,485 310.5,480.97 310.5,476C310.5,471.03 283.64,467 250.5,467C217.36,467 190.5,471.03 190.5,476C190.5,480.97 217.36,485 250.5,485Z"
        android:fillColor="#51901C"/>
  </group>
  <group>
    <clip-path
        android:pathData="M125,226h250v250h-250z"/>
    <path
        android:pathData="M254.29,369.73H246.53C246.53,369.73 239.59,455.53 238.18,472.93C238.12,473.72 238.39,474.51 238.93,475.08C239.46,475.67 240.22,476 241.01,476H258.99C259.78,476 260.53,475.67 261.07,475.09C261.6,474.52 261.87,473.74 261.82,472.96C260.55,455.6 254.29,369.73 254.29,369.73Z"
        android:fillColor="#413022"
        android:fillType="evenOdd"/>
    <path
        android:pathData="M229.17,400.21L226.13,402.61C226.13,402.61 241.04,427.14 244.07,432.12C244.2,432.34 244.47,432.46 244.8,432.45C245.13,432.43 245.49,432.29 245.8,432.04L252.87,426.48C253.18,426.23 253.4,425.92 253.49,425.6C253.59,425.28 253.54,425 253.35,424.81C249.28,420.67 229.17,400.21 229.17,400.21Z"
        android:fillColor="#413022"
        android:fillType="evenOdd"/>
    <path
        android:pathData="M276.03,383.09L273.29,380.35C273.29,380.35 250.69,398.05 246.1,401.64C245.89,401.8 245.8,402.08 245.86,402.4C245.91,402.73 246.1,403.08 246.38,403.36L252.74,409.71C253.02,409.99 253.36,410.18 253.68,410.23C254.01,410.29 254.29,410.2 254.45,410C258.08,405.47 276.03,383.09 276.03,383.09Z"
        android:fillColor="#413022"
        android:fillType="evenOdd"/>
    <path
        android:pathData="M138.63,315.07C138.63,315.07 155.78,316.52 175.16,318.17C206.41,320.83 231.2,345.61 233.85,376.86C235.09,391.42 236.22,404.73 236.71,410.44C236.77,411.18 236.51,411.9 235.99,412.42C235.46,412.94 234.74,413.21 234.01,413.15C228.29,412.66 214.99,411.53 200.43,410.29C169.18,407.64 144.39,382.85 141.73,351.6C140.09,332.22 138.63,315.07 138.63,315.07Z"
        android:fillColor="#60A625"
        android:fillType="evenOdd"/>
    <path
        android:pathData="M239.54,227.93C239.54,227.93 254.64,243.27 270.73,259.62C292.73,281.98 295.42,316.93 277.1,342.39C266.67,356.88 256.75,370.66 252.86,376.08C252.43,376.67 251.76,377.05 251.02,377.11C250.29,377.16 249.56,376.89 249.05,376.37C244.37,371.62 232.46,359.51 219.93,346.79C197.93,324.43 195.24,289.48 213.56,264.02C226.97,245.4 239.54,227.93 239.54,227.93Z"
        android:fillColor="#7EC046"
        android:fillType="evenOdd"/>
    <path
        android:pathData="M375,281.69C375,281.69 373.18,303.13 371.23,326C368.58,357.25 343.79,382.04 312.54,384.7C294.75,386.21 277.82,387.65 271.18,388.21C270.45,388.27 269.72,388.01 269.2,387.49C268.68,386.97 268.42,386.24 268.48,385.51C269.04,378.87 270.48,361.94 271.99,344.15C274.65,312.9 299.44,288.11 330.69,285.46C353.56,283.51 375,281.69 375,281.69Z"
        android:fillColor="#6BA43C"
        android:fillType="evenOdd"/>
  </group>
</vector>


================================================
FILE: app/src/main/res/drawable/stage_3.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="500dp"
    android:height="500dp"
    android:viewportWidth="500"
    android:viewportHeight="500">
  <group>
    <clip-path
        android:pathData="M91,452h319v48h-319z"/>
    <path
        android:pathData="M250.5,500C338.59,500 410,489.26 410,476C410,462.74 338.59,452 250.5,452C162.41,452 91,462.74 91,476C91,489.26 162.41,500 250.5,500Z"
        android:fillColor="#60A625"/>
    <path
        android:pathData="M250.5,485C283.64,485 310.5,480.97 310.5,476C310.5,471.03 283.64,467 250.5,467C217.36,467 190.5,471.03 190.5,476C190.5,480.97 217.36,485 250.5,485Z"
        android:fillColor="#51901C"/>
  </group>
  <group>
    <clip-path
        android:pathData="M79,134h342v342h-342z"/>
    <path
        android:pathData="M257.44,172.57H243.99C243.99,172.57 231.95,417.55 229.5,467.25C229.4,469.5 229.86,471.73 230.79,473.39C231.72,475.05 233.03,476 234.4,476H265.59C266.96,476 268.27,475.07 269.19,473.41C270.13,471.77 270.6,469.56 270.5,467.33C268.3,417.74 257.44,172.57 257.44,172.57Z"
        android:fillColor="#413022"
        android:fillType="evenOdd"/>
    <path
        android:pathData="M218.93,335.34L213.62,338.03C213.62,338.03 232.19,386.35 235.96,396.15C236.13,396.6 236.53,396.92 237.05,397.05C237.58,397.17 238.18,397.09 238.73,396.81L251.03,390.56C251.57,390.29 252,389.85 252.2,389.35C252.41,388.86 252.39,388.35 252.14,387.95C246.55,379.1 218.93,335.34 218.93,335.34Z"
        android:fillColor="#413022"
        android:fillType="evenOdd"/>
    <path
        android:pathData="M333.21,309.07L328.44,305.51C328.44,305.51 266.02,380.17 253.36,395.31C252.79,396 252.42,396.83 252.36,397.61C252.29,398.38 252.53,399.03 253.02,399.39L264.08,407.65C264.56,408.01 265.24,408.06 265.97,407.78C266.69,407.51 267.38,406.93 267.87,406.19C278.86,389.86 333.21,309.07 333.21,309.07Z"
        android:fillColor="#413022"
        android:fillType="evenOdd"/>
    <path
        android:pathData="M241.67,138.94C213.53,149.71 176.44,159.61 162.27,217.41C148.1,275.21 176.01,293.32 191.12,302.77C204.82,311.34 293.97,344.73 331.16,268.75C371.28,186.8 354.54,172.39 331.13,152.81C309.65,134.84 268.38,128.72 241.67,138.94Z"
        android:fillColor="#6DAC38"
        android:fillType="evenOdd"/>
    <path
        android:pathData="M372.24,275.46C372.24,275.46 372.29,275.61 372.39,275.9C379.62,297.98 373.82,322.25 357.4,338.68C340.97,355.11 316.7,360.9 294.62,353.67L294.62,353.67C294.34,353.58 294.12,353.36 294.03,353.08L294.03,353.08C286.8,331.01 292.6,306.74 309.02,290.31C325.45,273.88 349.72,268.08 371.8,275.32C372.09,275.41 372.24,275.46 372.24,275.46Z"
        android:fillColor="#5B8F30"
        android:fillType="evenOdd"/>
    <path
        android:pathData="M205.1,263.3C206.3,263.3 207.28,262.33 207.28,261.12C207.28,259.92 206.3,258.94 205.1,258.94C203.89,258.94 202.92,259.92 202.92,261.12C202.92,262.33 203.89,263.3 205.1,263.3Z"
        android:fillColor="#9FE664"/>
    <path
        android:pathData="M253.02,187.16C253.81,187.16 254.44,186.53 254.44,185.74C254.44,184.96 253.81,184.32 253.02,184.32C252.24,184.32 251.6,184.96 251.6,185.74C251.6,186.53 252.24,187.16 253.02,187.16Z"
        android:fillColor="#9FE664"/>
    <path
        android:pathData="M224.59,212.31C225.16,212.31 225.63,211.85 225.63,211.28C225.63,210.71 225.16,210.25 224.59,210.25C224.02,210.25 223.56,210.71 223.56,211.28C223.56,211.85 224.02,212.31 224.59,212.31Z"
        android:fillColor="#9FE664"/>
    <path
        android:pathData="M196.86,242.46C197.69,242.46 198.36,241.78 198.36,240.95C198.36,240.12 197.69,239.45 196.86,239.45C196.02,239.45 195.35,240.12 195.35,240.95C195.35,241.78 196.02,242.46 196.86,242.46Z"
        android:fillColor="#9FE664"/>
    <path
        android:pathData="M209.6,179.43C212.02,179.43 213.97,177.48 213.97,175.07C213.97,172.65 212.02,170.7 209.6,170.7C207.19,170.7 205.24,172.65 205.24,175.07C205.24,177.48 207.19,179.43 209.6,179.43Z"
        android:fillColor="#9FE664"/>
    <path
        android:pathData="M285.17,247.47C286.56,247.47 287.68,246.35 287.68,244.96C287.68,243.58 286.56,242.46 285.17,242.46C283.79,242.46 282.67,243.58 282.67,244.96C282.67,246.35 283.79,247.47 285.17,247.47Z"
        android:fillColor="#9FE664"/>
  </group>
</vector>


================================================
FILE: app/src/main/res/drawable/stage_4.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="500dp"
    android:height="500dp"
    android:viewportWidth="500"
    android:viewportHeight="500">
  <group>
    <clip-path
        android:pathData="M91,452h319v48h-319z"/>
    <path
        android:pathData="M250.5,500C338.59,500 410,489.26 410,476C410,462.74 338.59,452 250.5,452C162.41,452 91,462.74 91,476C91,489.26 162.41,500 250.5,500Z"
        android:fillColor="#60A625"/>
    <path
        android:pathData="M250.5,485C283.64,485 310.5,480.97 310.5,476C310.5,471.03 283.64,467 250.5,467C217.36,467 190.5,471.03 190.5,476C190.5,480.97 217.36,485 250.5,485Z"
        android:fillColor="#51901C"/>
  </group>
  <group>
    <clip-path
        android:pathData="M68,78h398v398h-398z"/>
    <path
        android:pathData="M259.78,135.64H245.12C245.12,135.64 231.99,410.43 229.32,466.18C229.21,468.71 229.71,471.21 230.73,473.07C231.75,474.94 233.17,476 234.67,476H268.68C270.17,476 271.59,474.95 272.6,473.1C273.62,471.25 274.13,468.77 274.03,466.27C271.63,410.65 259.78,135.64 259.78,135.64Z"
        android:fillColor="#413022"
        android:fillType="evenOdd"/>
    <path
        android:pathData="M202.36,289.3L199.4,291.63C199.4,291.63 234.8,341.99 241.98,352.21C242.3,352.67 242.75,353.03 243.21,353.2C243.68,353.37 244.11,353.33 244.41,353.09L251.27,347.68C251.57,347.45 251.71,347.04 251.66,346.55C251.61,346.06 251.37,345.55 251,345.13C242.82,335.73 202.36,289.3 202.36,289.3Z"
        android:fillColor="#413022"
        android:fillType="evenOdd"/>
    <path
        android:pathData="M328.43,352.78L320.51,344.85C320.51,344.85 255.15,396.02 241.89,406.4C241.29,406.87 241.04,407.67 241.19,408.62C241.34,409.56 241.89,410.56 242.7,411.37L261.08,429.74C261.88,430.55 262.88,431.1 263.82,431.25C264.76,431.41 265.56,431.16 266.03,430.57C276.53,417.48 328.43,352.78 328.43,352.78Z"
        android:fillColor="#413022"
        android:fillType="evenOdd"/>
    <path
        android:pathData="M347.53,175C336.88,142.43 327.52,99.53 264.8,82.73C202.07,65.92 181.12,98 170.16,115.36C160.22,131.11 120.06,233.77 201.76,277.29C289.9,324.24 306.35,305.02 328.73,278.16C349.28,253.5 357.65,205.91 347.53,175Z"
        android:fillColor="#6DAC38"
        android:fillType="evenOdd"/>
    <path
        android:pathData="M380.39,336.61C375.19,320.72 370.63,299.8 340.03,291.6C309.44,283.41 299.22,299.05 293.88,307.52C289.03,315.2 269.44,365.27 309.29,386.5C352.27,409.39 360.3,400.02 371.21,386.92C381.23,374.89 385.32,351.68 380.39,336.61Z"
        android:fillColor="#507E28"
        android:fillType="evenOdd"/>
    <path
        android:pathData="M203.89,216.17C205.29,216.17 206.42,215.03 206.42,213.63C206.42,212.23 205.29,211.1 203.89,211.1C202.49,211.1 201.35,212.23 201.35,213.63C201.35,215.03 202.49,216.17 203.89,216.17Z"
        android:fillColor="#9FE664"/>
    <path
        android:pathData="M259.66,127.56C260.57,127.56 261.31,126.82 261.31,125.91C261.31,124.99 260.57,124.25 259.66,124.25C258.74,124.25 258,124.99 258,125.91C258,126.82 258.74,127.56 259.66,127.56Z"
        android:fillColor="#9FE664"/>
    <path
        android:pathData="M226.57,156.83C227.24,156.83 227.77,156.29 227.77,155.63C227.77,154.97 227.24,154.43 226.57,154.43C225.91,154.43 225.38,154.97 225.38,155.63C225.38,156.29 225.91,156.83 226.57,156.83Z"
        android:fillColor="#9FE664"/>
    <path
        android:pathData="M194.29,191.91C195.26,191.91 196.04,191.13 196.04,190.16C196.04,189.19 195.26,188.41 194.29,188.41C193.33,188.41 192.54,189.19 192.54,190.16C192.54,191.13 193.33,191.91 194.29,191.91Z"
        android:fillColor="#9FE664"/>
    <path
        android:pathData="M209.13,118.57C211.94,118.57 214.21,116.29 214.21,113.48C214.21,110.68 211.94,108.4 209.13,108.4C206.32,108.4 204.05,110.68 204.05,113.48C204.05,116.29 206.32,118.57 209.13,118.57Z"
        android:fillColor="#9FE664"/>
    <path
        android:pathData="M297.07,197.74C298.68,197.74 299.99,196.44 299.99,194.82C299.99,193.21 298.68,191.91 297.07,191.91C295.46,191.91 294.16,193.21 294.16,194.82C294.16,196.44 295.46,197.74 297.07,197.74Z"
        android:fillColor="#9FE664"/>
    <path
        android:pathData="M354.31,322.43C355.28,322.43 356.07,321.64 356.07,320.66C356.07,319.69 355.28,318.9 354.31,318.9C353.33,318.9 352.55,319.69 352.55,320.66C352.55,321.64 353.33,322.43 354.31,322.43Z"
        android:fillColor="#71BA33"/>
    <path
        android:pathData="M372.08,358.76C373.68,358.76 374.98,357.47 374.98,355.86C374.98,354.26 373.68,352.96 372.08,352.96C370.48,352.96 369.18,354.26 369.18,355.86C369.18,357.47 370.48,358.76 372.08,358.76Z"
        android:fillColor="#71BA33"/>
    <path
        android:pathData="M353.43,352.96C353.91,352.96 354.31,352.57 354.31,352.08C354.31,351.6 353.91,351.2 353.43,351.2C352.94,351.2 352.55,351.6 352.55,352.08C352.55,352.57 352.94,352.96 353.43,352.96Z"
        android:fillColor="#71BA33"/>
    <path
        android:pathData="M340.95,381C342.96,381 344.58,379.37 344.58,377.36C344.58,375.35 342.96,373.72 340.95,373.72C338.94,373.72 337.31,375.35 337.31,377.36C337.31,379.37 338.94,381 340.95,381Z"
        android:fillColor="#71BA33"/>
    <path
        android:pathData="M332.9,339.2C333.42,339.2 333.84,338.78 333.84,338.26C333.84,337.75 333.42,337.33 332.9,337.33C332.38,337.33 331.96,337.75 331.96,338.26C331.96,338.78 332.38,339.2 332.9,339.2Z"
        android:fillColor="#71BA33"/>
  </group>
</vector>


================================================
FILE: app/src/main/res/drawable/stage_5.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="500dp"
    android:height="500dp"
    android:viewportWidth="500"
    android:viewportHeight="500">
  <group>
    <clip-path
        android:pathData="M91,452h319v48h-319z"/>
    <path
        android:pathData="M250.5,500C338.59,500 410,489.26 410,476C410,462.74 338.59,452 250.5,452C162.41,452 91,462.74 91,476C91,489.26 162.41,500 250.5,500Z"
        android:fillColor="#60A625"/>
    <path
        android:pathData="M250.5,485C283.64,485 310.5,480.97 310.5,476C310.5,471.03 283.64,467 250.5,467C217.36,467 190.5,471.03 190.5,476C190.5,480.97 217.36,485 250.5,485Z"
        android:fillColor="#51901C"/>
  </group>
  <group>
    <clip-path
        android:pathData="M30,35h441v441h-441z"/>
    <path
        android:pathData="M258.45,227.57H244.07C244.07,227.57 231.19,428.14 228.58,468.83C228.46,470.68 228.96,472.51 229.95,473.86C230.95,475.23 232.35,476 233.82,476H267.18C268.64,476 270.04,475.24 271.03,473.88C272.03,472.53 272.53,470.72 272.43,468.9C270.08,428.3 258.45,227.57 258.45,227.57Z"
        android:fillColor="#413022"
        android:fillType="evenOdd"/>
    <path
        android:pathData="M207.76,303.24L201.33,308.3C201.33,308.3 232.8,360.07 239.19,370.57C239.48,371.05 240.04,371.3 240.74,371.27C241.43,371.24 242.2,370.93 242.86,370.41L257.76,358.67C258.41,358.15 258.9,357.48 259.09,356.82C259.28,356.15 259.17,355.54 258.79,355.15C250.2,346.42 207.76,303.24 207.76,303.24Z"
        android:fillColor="#413022"
        android:fillType="evenOdd"/>
    <path
        android:pathData="M309.46,285.38L303.67,279.6C303.67,279.6 255.97,316.94 246.29,324.52C245.86,324.86 245.67,325.45 245.78,326.14C245.89,326.83 246.29,327.55 246.88,328.14L260.3,341.56C260.89,342.15 261.61,342.55 262.3,342.66C262.98,342.77 263.57,342.59 263.91,342.16C271.58,332.61 309.46,285.38 309.46,285.38Z"
        android:fillColor="#413022"
        android:fillType="evenOdd"/>
    <path
        android:pathData="M182.14,184.93C153.03,179.96 116.9,169.78 75.06,211.62C33.21,253.47 47.13,282.77 54.85,298.34C61.84,312.46 119,385.37 189.52,339.65C265.58,290.35 259.07,269.76 249.68,241.47C241.06,215.5 209.76,189.65 182.14,184.93Z"
        android:fillColor="#4A8D12"
        android:fillType="evenOdd"/>
    <path
        android:pathData="M355.48,142.7C343.65,106.53 333.27,58.91 263.62,40.25C193.98,21.59 170.72,57.2 158.55,76.48C147.51,93.97 102.92,207.95 193.64,256.27C291.49,308.4 309.76,287.06 334.61,257.24C357.42,229.86 366.71,177.02 355.48,142.7Z"
        android:fillColor="#6DAC38"
        android:fillType="evenOdd"/>
    <path
        android:pathData="M348.73,224.19C333.49,221.59 314.57,216.26 292.67,238.16C270.76,260.07 278.05,275.42 282.08,283.56C285.75,290.95 315.67,329.13 352.59,305.2C392.42,279.38 389.01,268.6 384.09,253.79C379.58,240.2 363.19,226.66 348.73,224.19Z"
        android:fillColor="#4A8D12"
        android:fillType="evenOdd"/>
    <path
        android:pathData="M180.57,188.09C182.12,188.09 183.38,186.84 183.38,185.29C183.38,183.73 182.12,182.48 180.57,182.48C179.02,182.48 177.76,183.73 177.76,185.29C177.76,186.84 179.02,188.09 180.57,188.09Z"
        android:fillColor="#9FE664"/>
    <path
        android:pathData="M242.37,89.92C243.38,89.92 244.2,89.1 244.2,88.08C244.2,87.07 243.38,86.25 242.37,86.25C241.35,86.25 240.53,87.07 240.53,88.08C240.53,89.1 241.35,89.92 242.37,89.92Z"
        android:fillColor="#9FE664"/>
    <path
        android:pathData="M205.71,122.35C206.44,122.35 207.04,121.75 207.04,121.02C207.04,120.28 206.44,119.69 205.71,119.69C204.97,119.69 204.38,120.28 204.38,121.02C204.38,121.75 204.97,122.35 205.71,122.35Z"
        android:fillColor="#9FE664"/>
    <path
        android:pathData="M169.94,161.22C171.01,161.22 171.88,160.35 171.88,159.28C171.88,158.21 171.01,157.34 169.94,157.34C168.87,157.34 168,158.21 168,159.28C168,160.35 168.87,161.22 169.94,161.22Z"
        android:fillColor="#9FE664"/>
    <path
        android:pathData="M186.38,79.95C189.49,79.95 192.01,77.43 192.01,74.32C192.01,71.21 189.49,68.69 186.38,68.69C183.27,68.69 180.75,71.21 180.75,74.32C180.75,77.43 183.27,79.95 186.38,79.95Z"
        android:fillColor="#9FE664"/>
    <path
        android:pathData="M283.82,167.68C285.61,167.68 287.05,166.23 287.05,164.45C287.05,162.66 285.61,161.22 283.82,161.22C282.04,161.22 280.59,162.66 280.59,164.45C280.59,166.23 282.04,167.68 283.82,167.68Z"
        android:fillColor="#9FE664"/>
    <path
        android:pathData="M342.64,242.21C343.72,242.21 344.6,241.33 344.6,240.25C344.6,239.17 343.72,238.3 342.64,238.3C341.56,238.3 340.69,239.17 340.69,240.25C340.69,241.33 341.56,242.21 342.64,242.21Z"
        android:fillColor="#71BA33"/>
    <path
        android:pathData="M362.33,282.47C364.1,282.47 365.54,281.03 365.54,279.26C365.54,277.48 364.1,276.04 362.33,276.04C360.55,276.04 359.12,277.48 359.12,279.26C359.12,281.03 360.55,282.47 362.33,282.47Z"
        android:fillColor="#71BA33"/>
    <path
        android:pathData="M341.67,276.04C342.2,276.04 342.64,275.6 342.64,275.07C342.64,274.53 342.2,274.09 341.67,274.09C341.13,274.09 340.69,274.53 340.69,275.07C340.69,275.6 341.13,276.04 341.67,276.04Z"
        android:fillColor="#71BA33"/>
    <path
        android:pathData="M327.83,307.11C330.06,307.11 331.87,305.3 331.87,303.07C331.87,300.85 330.06,299.04 327.83,299.04C325.61,299.04 323.8,300.85 323.8,303.07C323.8,305.3 325.61,307.11 327.83,307.11Z"
        android:fillColor="#71BA33"/>
    <path
        android:pathData="M318.92,260.79C319.49,260.79 319.96,260.33 319.96,259.75C319.96,259.18 319.49,258.71 318.92,258.71C318.35,258.71 317.88,259.18 317.88,259.75C317.88,260.33 318.35,260.79 318.92,260.79Z"
        android:fillColor="#71BA33"/>
    <path
        android:pathData="M105.63,209.76C106.81,209.76 107.76,208.81 107.76,207.63C107.76,206.45 106.81,205.49 105.63,205.49C104.45,205.49 103.49,206.45 103.49,207.63C103.49,208.81 104.45,209.76 105.63,209.76Z"
        android:fillColor="#69B528"/>
    <path
        android:pathData="M99.16,290.48C99.96,290.48 100.61,289.83 100.61,289.04C100.61,288.24 99.96,287.59 99.16,287.59C98.36,287.59 97.72,288.24 97.72,289.04C97.72,289.83 98.36,290.48 99.16,290.48Z"
        android:fillColor="#69B528"/>
    <path
        android:pathData="M118.32,234.27C119.11,234.27 119.76,233.63 119.76,232.83C119.76,232.03 119.11,231.38 118.32,231.38C117.52,231.38 116.87,232.03 116.87,232.83C116.87,233.63 117.52,234.27 118.32,234.27Z"
        android:fillColor="#69B528"/>
    <path
        android:pathData="M70.27,279.18C71.07,279.18 71.72,278.53 71.72,277.73C71.72,276.93 71.07,276.29 70.27,276.29C69.47,276.29 68.83,276.93 68.83,277.73C68.83,278.53 69.47,279.18 70.27,279.18Z"
        android:fillColor="#69B528"/>
    <path
        android:pathData="M132.58,328.43C135.89,328.43 138.58,325.75 138.58,322.43C138.58,319.11 135.89,316.43 132.58,316.43C129.26,316.43 126.57,319.11 126.57,322.43C126.57,325.75 129.26,328.43 132.58,328.43Z"
        android:fillColor="#69B528"/>
    <path
        android:pathData="M87.1,247.46C88.7,247.46 89.99,246.17 89.99,244.57C89.99,242.98 88.7,241.68 87.1,241.68C85.51,241.68 84.21,242.98 84.21,244.57C84.21,246.17 85.51,247.46 87.1,247.46Z"
        android:fillColor="#69B528"/>
    <path
        android:pathData="M83.31,318.59C84.51,318.59 85.48,317.62 85.48,316.42C85.48,315.23 84.51,314.26 83.31,314.26C82.12,314.26 81.15,315.23 81.15,316.42C81.15,317.62 82.12,318.59 83.31,318.59Z"
        android:fillColor="#69B528"/>
  </group>
</vector>


================================================
FILE: app/src/main/res/drawable/stage_6.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="500dp"
    android:height="500dp"
    android:viewportWidth="500"
    android:viewportHeight="500">
  <group>
    <clip-path
        android:pathData="M91,452h319v48h-319z"/>
    <path
        android:pathData="M250.5,500C338.59,500 410,489.26 410,476C410,462.74 338.59,452 250.5,452C162.41,452 91,462.74 91,476C91,489.26 162.41,500 250.5,500Z"
        android:fillColor="#60A625"/>
    <path
        android:pathData="M250.5,485C283.64,485 310.5,480.97 310.5,476C310.5,471.03 283.64,467 250.5,467C217.36,467 190.5,471.03 190.5,476C190.5,480.97 217.36,485 250.5,485Z"
        android:fillColor="#51901C"/>
  </group>
  <group>
    <clip-path
        android:pathData="M25,24h452v452h-452z"/>
    <path
        android:pathData="M259.23,271.99H244.35C244.35,271.99 231.02,436.7 228.32,470.12C228.2,471.63 228.71,473.13 229.74,474.25C230.77,475.36 232.22,476 233.74,476H268.26C269.77,476 271.22,475.38 272.24,474.26C273.27,473.15 273.8,471.67 273.69,470.17C271.26,436.83 259.23,271.99 259.23,271.99Z"
        android:fillColor="#413022"
        android:fillType="evenOdd"/>
    <path
        android:pathData="M207.63,318.84L201.79,323.44C201.79,323.44 230.41,370.54 236.22,380.09C236.49,380.52 237,380.75 237.63,380.73C238.26,380.7 238.96,380.42 239.56,379.95L253.12,369.27C253.71,368.8 254.15,368.19 254.32,367.58C254.5,366.97 254.4,366.42 254.05,366.06C246.24,358.12 207.63,318.84 207.63,318.84Z"
        android:fillColor="#413022"
        android:fillType="evenOdd"/>
    <path
        android:pathData="M302.94,291.44L297.68,286.17C297.68,286.17 254.28,320.15 245.48,327.04C245.08,327.35 244.91,327.89 245.01,328.51C245.11,329.14 245.48,329.8 246.01,330.34L258.22,342.54C258.75,343.08 259.41,343.44 260.03,343.54C260.66,343.65 261.19,343.48 261.51,343.09C268.48,334.4 302.94,291.44 302.94,291.44Z"
        android:fillColor="#413022"
        android:fillType="evenOdd"/>
    <path
        android:pathData="M93.98,156.1C76.35,184.45 50.45,217.35 74.4,286.03C98.35,354.71 133.87,356.5 152.87,357.11C170.11,357.66 268.24,337.99 252.83,236.39C236.2,126.8 212.67,122.04 180.14,115.97C150.27,110.4 110.71,129.2 93.98,156.1Z"
        android:fillColor="#3F6B19"
        android:fillType="evenOdd"/>
    <path
        android:pathData="M258.49,156.1C240.86,184.45 214.96,217.35 238.91,286.03C262.86,354.71 298.38,356.5 317.38,357.11C334.61,357.66 432.74,337.99 417.33,236.39C400.71,126.8 377.18,122.04 344.65,115.97C314.78,110.4 275.21,129.2 258.49,156.1Z"
        android:fillColor="#7EC046"
        android:fillType="evenOdd"/>
    <path
        android:pathData="M294.79,30.18C257.35,23.78 210.86,10.69 157.03,64.52C103.2,118.35 121.11,156.05 131.03,176.08C140.03,194.24 213.56,288.04 304.28,229.23C402.14,165.8 393.76,139.31 381.68,102.92C370.59,69.51 330.33,36.26 294.79,30.18Z"
        android:fillColor="#60A625"
        android:fillType="evenOdd"/>
    <path
        android:pathData="M299.72,289.93L294.02,285.15C294.02,285.15 253.76,322.79 245.6,330.42C245.23,330.77 245.1,331.32 245.26,331.93C245.42,332.55 245.84,333.18 246.42,333.66L259.64,344.75C260.22,345.24 260.91,345.54 261.54,345.59C262.17,345.64 262.69,345.43 262.97,345.01C269.15,335.74 299.72,289.93 299.72,289.93Z"
        android:fillColor="#413022"
        android:fillType="evenOdd"/>
    <path
        android:pathData="M352.44,161.09C325.97,156.56 293.1,147.3 255.03,185.37C216.97,223.43 229.63,250.09 236.65,264.25C243.01,277.1 295.01,343.42 359.16,301.84C428.35,256.98 422.43,238.26 413.89,212.52C406.04,188.9 377.57,165.38 352.44,161.09Z"
        android:fillColor="#4A8D12"
        android:fillType="evenOdd"/>
    <path
        android:pathData="M253.73,385.73C258.45,385.73 262.27,380.74 262.27,374.6C262.27,368.45 258.45,363.47 253.73,363.47C249.02,363.47 245.19,368.45 245.19,374.6C245.19,380.74 249.02,385.73 253.73,385.73Z"
        android:fillColor="#2B1604"/>
    <path
        android:pathData="M206.82,171.48C208.41,171.48 209.7,170.2 209.7,168.6C209.7,167.01 208.41,165.73 206.82,165.73C205.23,165.73 203.95,167.01 203.95,168.6C203.95,170.2 205.23,171.48 206.82,171.48Z"
        android:fillColor="#9FE664"/>
    <path
        android:pathData="M270.16,70.86C271.2,70.86 272.04,70.02 272.04,68.98C272.04,67.94 271.2,67.1 270.16,67.1C269.13,67.1 268.28,67.94 268.28,68.98C268.28,70.02 269.13,70.86 270.16,70.86Z"
        android:fillColor="#9FE664"/>
    <path
        android:pathData="M232.59,104.1C233.34,104.1 233.95,103.49 233.95,102.73C233.95,101.98 233.34,101.37 232.59,101.37C231.84,101.37 231.23,101.98 231.23,102.73C231.23,103.49 231.84,104.1 232.59,104.1Z"
        android:fillColor="#9FE664"/>
    <path
        android:pathData="M195.93,143.94C197.03,143.94 197.92,143.05 197.92,141.95C197.92,140.85 197.03,139.96 195.93,139.96C194.83,139.96 193.94,140.85 193.94,141.95C193.94,143.05 194.83,143.94 195.93,143.94Z"
        android:fillColor="#9FE664"/>
    <path
        android:pathData="M212.78,60.64C215.97,60.64 218.55,58.06 218.55,54.87C218.55,51.68 215.97,49.1 212.78,49.1C209.59,49.1 207.01,51.68 207.01,54.87C207.01,58.06 209.59,60.64 212.78,60.64Z"
        android:fillColor="#9FE664"/>
    <path
        android:pathData="M312.65,150.56C314.48,150.56 315.97,149.08 315.97,147.25C315.97,145.42 314.48,143.94 312.65,143.94C310.83,143.94 309.34,145.42 309.34,147.25C309.34,149.08 310.83,150.56 312.65,150.56Z"
        android:fillColor="#9FE664"/>
    <path
        android:pathData="M358.01,207.12C359.11,207.12 360.01,206.22 360.01,205.12C360.01,204.01 359.11,203.11 358.01,203.11C356.9,203.11 356.01,204.01 356.01,205.12C356.01,206.22 356.9,207.12 358.01,207.12Z"
        android:fillColor="#71BA33"/>
    <path
        android:pathData="M378.19,248.39C380.01,248.39 381.48,246.91 381.48,245.09C381.48,243.27 380.01,241.8 378.19,241.8C376.37,241.8 374.89,243.27 374.89,245.09C374.89,246.91 376.37,248.39 378.19,248.39Z"
        android:fillColor="#71BA33"/>
    <path
        android:pathData="M357.01,241.8C357.56,241.8 358.01,241.35 358.01,240.8C358.01,240.25 357.56,239.8 357.01,239.8C356.45,239.8 356.01,240.25 356.01,240.8C356.01,241.35 356.45,241.8 357.01,241.8Z"
        android:fillColor="#71BA33"/>
    <path
        android:pathData="M342.83,273.64C345.11,273.64 346.96,271.79 346.96,269.51C346.96,267.22 345.11,265.37 342.83,265.37C340.55,265.37 338.7,267.22 338.7,269.51C338.7,271.79 340.55,273.64 342.83,273.64Z"
        android:fillColor="#71BA33"/>
    <path
        android:pathData="M333.7,226.17C334.28,226.17 334.76,225.69 334.76,225.1C334.76,224.52 334.28,224.04 333.7,224.04C333.11,224.04 332.63,224.52 332.63,225.1C332.63,225.69 333.11,226.17 333.7,226.17Z"
        android:fillColor="#71BA33"/>
    <path
        android:pathData="M108.84,195.37C110,195.03 110.66,193.81 110.32,192.65C109.98,191.49 108.76,190.83 107.6,191.17C106.44,191.52 105.78,192.73 106.12,193.89C106.46,195.05 107.68,195.71 108.84,195.37Z"
        android:fillColor="#69B528"/>
    <path
        android:pathData="M125.9,276.6C126.68,276.36 127.13,275.54 126.9,274.76C126.67,273.97 125.85,273.52 125.06,273.76C124.28,273.99 123.83,274.81 124.06,275.6C124.29,276.38 125.12,276.83 125.9,276.6Z"
        android:fillColor="#69B528"/>
    <path
        android:pathData="M128.42,215.79C129.21,215.55 129.65,214.73 129.42,213.95C129.19,213.16 128.37,212.71 127.58,212.95C126.8,213.18 126.35,214 126.58,214.78C126.82,215.57 127.64,216.02 128.42,215.79Z"
        android:fillColor="#69B528"/>
    <path
        android:pathData="M94.22,273.86C95.01,273.63 95.45,272.81 95.22,272.02C94.99,271.24 94.17,270.79 93.38,271.02C92.6,271.26 92.15,272.08 92.38,272.86C92.61,273.65 93.44,274.1 94.22,273.86Z"
        android:fillColor="#69B528"/>
    <path
        android:pathData="M169.76,304.21C173.02,303.25 174.88,299.83 173.92,296.57C172.96,293.31 169.54,291.44 166.27,292.41C163.01,293.37 161.15,296.79 162.11,300.05C163.08,303.31 166.5,305.17 169.76,304.21Z"
        android:fillColor="#69B528"/>
    <path
        android:pathData="M101.57,237.8C103.13,237.34 104.03,235.69 103.57,234.13C103.11,232.56 101.46,231.66 99.89,232.13C98.32,232.59 97.43,234.23 97.89,235.8C98.35,237.37 100,238.27 101.57,237.8Z"
        android:fillColor="#69B528"/>
    <path
        android:pathData="M118.48,308.83C119.65,308.48 120.32,307.24 119.98,306.07C119.63,304.89 118.4,304.22 117.22,304.57C116.05,304.92 115.38,306.15 115.72,307.33C116.07,308.5 117.3,309.17 118.48,308.83Z"
        android:fillColor="#69B528"/>
  </group>
</vector>


================================================
FILE: app/src/main/res/drawable/steps_fill0_wght400_grad0_opsz24.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24">
  <path
      android:fillColor="#FF000000"
      android:pathData="M5.4,9.5q0.975,0 1.85,0.35 0.875,0.35 1.6,1.025L18.4,20h0.6q0.425,0 0.712,-0.288Q20,19.425 20,19q0,-0.2 -0.038,-0.425 -0.037,-0.225 -0.262,-0.45l-4.575,-4.575L13.35,8.2l-1.85,0.45q-0.95,0.25 -1.725,-0.35Q9,7.7 9,6.725v-2.1l-0.7,-0.35 -3.85,5.15q-0.025,0.025 -0.025,0.037 0,0.013 -0.025,0.038ZM5.4,11.5L4.25,11.5q0.075,0.175 0.188,0.325 0.112,0.15 0.262,0.275l8.1,7.375q0.275,0.275 0.625,0.4t0.725,0.125h1.35l-8.025,-7.675q-0.425,-0.425 -0.962,-0.625 -0.538,-0.2 -1.113,-0.2ZM14.15,22q-0.75,0 -1.425,-0.275t-1.25,-0.775L3.35,13.575Q2.2,12.525 2.062,11q-0.137,-1.525 0.788,-2.775l3.85,-5.15q0.425,-0.575 1.138,-0.763 0.712,-0.187 1.362,0.163l0.7,0.35q0.525,0.275 0.813,0.75 0.287,0.475 0.287,1.05v2.1l1.85,-0.475q0.75,-0.2 1.45,0.188 0.7,0.387 0.95,1.112l1.625,4.9 4.25,4.25q0.5,0.5 0.687,1.075Q22,18.35 22,19q0,1.25 -0.875,2.125T19,22Z"/>
</vector>


================================================
FILE: app/src/main/res/drawable/tree_collected.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="150dp"
    android:height="200dp"
    android:viewportWidth="150"
    android:viewportHeight="200">
  <group>
    <clip-path
        android:pathData="M0,0h150v200h-150z"/>
    <path
        android:pathData="M75,200C108.14,200 135,195.97 135,191C135,186.03 108.14,182 75,182C41.86,182 15,186.03 15,191C15,195.97 41.86,200 75,200Z"
        android:fillColor="#000000"
        android:fillAlpha="0.1"/>
    <path
        android:pathData="M77.63,119.06H72.39C72.39,119.06 67.7,177 66.75,188.76C66.71,189.29 66.89,189.82 67.25,190.21C67.61,190.6 68.12,190.82 68.66,190.82H80.8C81.33,190.82 81.84,190.6 82.2,190.21C82.57,189.82 82.75,189.3 82.71,188.77C81.86,177.04 77.63,119.06 77.63,119.06Z"
        android:fillColor="#413022"
        android:fillType="evenOdd"/>
    <path
        android:pathData="M59.47,135.54L57.42,137.16C57.42,137.16 67.49,153.72 69.53,157.09C69.62,157.24 69.8,157.32 70.03,157.31C70.25,157.3 70.5,157.2 70.71,157.03L75.47,153.28C75.68,153.11 75.84,152.9 75.9,152.68C75.96,152.47 75.93,152.28 75.8,152.15C73.06,149.35 59.47,135.54 59.47,135.54Z"
        android:fillColor="#413022"
        android:fillType="evenOdd"/>
    <path
        android:pathData="M93,125.9L91.15,124.05C91.15,124.05 75.88,136 72.79,138.42C72.65,138.53 72.59,138.72 72.62,138.94C72.66,139.16 72.79,139.39 72.97,139.58L77.27,143.88C77.46,144.06 77.69,144.19 77.91,144.23C78.13,144.26 78.31,144.21 78.43,144.07C80.88,141.01 93,125.9 93,125.9Z"
        android:fillColor="#413022"
        android:fillType="evenOdd"/>
    <path
        android:pathData="M19.49,78.29C13.29,88.26 4.18,99.84 12.6,124C21.03,148.16 33.52,148.79 40.21,149C46.27,149.2 80.79,142.28 75.37,106.53C69.52,67.98 61.25,66.31 49.8,64.17C39.3,62.21 25.38,68.83 19.49,78.29Z"
        android:fillColor="#3F6B19"
        android:fillType="evenOdd"/>
    <path
        android:pathData="M77.36,78.29C71.16,88.26 62.05,99.84 70.48,124C78.9,148.16 91.4,148.79 98.08,149C104.14,149.2 138.66,142.28 133.24,106.53C127.4,67.98 119.12,66.31 107.67,64.17C97.17,62.21 83.25,68.83 77.36,78.29Z"
        android:fillColor="#7EC046"
        android:fillType="evenOdd"/>
    <path
        android:pathData="M90.13,33.99C76.96,31.74 60.61,27.13 41.67,46.07C22.74,65.01 29.04,78.27 32.53,85.32C35.69,91.71 61.56,124.7 93.47,104.01C127.9,81.7 124.95,72.38 120.7,59.58C116.8,47.83 102.64,36.13 90.13,33.99Z"
        android:fillColor="#60A625"
        android:fillType="evenOdd"/>
    <path
        android:pathData="M91.87,125.37L89.86,123.68C89.86,123.68 75.7,136.93 72.83,139.61C72.7,139.74 72.66,139.93 72.71,140.14C72.77,140.36 72.91,140.58 73.12,140.75L77.77,144.65C77.97,144.82 78.22,144.93 78.44,144.95C78.66,144.96 78.84,144.89 78.94,144.74C81.11,141.49 91.87,125.37 91.87,125.37Z"
        android:fillColor="#413022"
        android:fillType="evenOdd"/>
    <path
        android:pathData="M110.42,80.04C101.1,78.45 89.54,75.19 76.15,88.58C62.76,101.98 67.21,111.35 69.68,116.33C71.92,120.85 90.21,144.19 112.78,129.56C137.12,113.78 135.04,107.19 132.03,98.14C129.27,89.83 119.26,81.55 110.42,80.04Z"
        android:fillColor="#4A8D12"
        android:fillType="evenOdd"/>
    <path
        android:pathData="M75.69,159.07C77.35,159.07 78.69,157.31 78.69,155.15C78.69,152.99 77.35,151.24 75.69,151.24C74.03,151.24 72.69,152.99 72.69,155.15C72.69,157.31 74.03,159.07 75.69,159.07Z"
        android:fillColor="#2B1604"/>
    <path
        android:pathData="M59.19,83.7C59.75,83.7 60.2,83.25 60.2,82.69C60.2,82.13 59.75,81.67 59.19,81.67C58.63,81.67 58.18,82.13 58.18,82.69C58.18,83.25 58.63,83.7 59.19,83.7Z"
        android:fillColor="#9FE664"/>
    <path
        android:pathData="M81.47,48.3C81.84,48.3 82.13,48.01 82.13,47.64C82.13,47.28 81.84,46.98 81.47,46.98C81.11,46.98 80.81,47.28 80.81,47.64C80.81,48.01 81.11,48.3 81.47,48.3Z"
        android:fillColor="#9FE664"/>
    <path
        android:pathData="M68.25,59.99C68.52,59.99 68.73,59.78 68.73,59.52C68.73,59.25 68.52,59.04 68.25,59.04C67.99,59.04 67.77,59.25 67.77,59.52C67.77,59.78 67.99,59.99 68.25,59.99Z"
        android:fillColor="#9FE664"/>
    <path
        android:pathData="M55.36,74.01C55.74,74.01 56.06,73.7 56.06,73.31C56.06,72.92 55.74,72.61 55.36,72.61C54.97,72.61 54.66,72.92 54.66,73.31C54.66,73.7 54.97,74.01 55.36,74.01Z"
        android:fillColor="#9FE664"/>
    <path
        android:pathData="M61.28,44.71C62.41,44.71 63.31,43.8 63.31,42.68C63.31,41.56 62.41,40.65 61.28,40.65C60.16,40.65 59.25,41.56 59.25,42.68C59.25,43.8 60.16,44.71 61.28,44.71Z"
        android:fillColor="#9FE664"/>
    <path
        android:pathData="M96.42,76.34C97.06,76.34 97.58,75.82 97.58,75.17C97.58,74.53 97.06,74.01 96.42,74.01C95.78,74.01 95.25,74.53 95.25,75.17C95.25,75.82 95.78,76.34 96.42,76.34Z"
        android:fillColor="#9FE664"/>
    <path
        android:pathData="M112.37,96.24C112.76,96.24 113.08,95.92 113.08,95.53C113.08,95.14 112.76,94.83 112.37,94.83C111.98,94.83 111.67,95.14 111.67,95.53C111.67,95.92 111.98,96.24 112.37,96.24Z"
        android:fillColor="#71BA33"/>
    <path
        android:pathData="M119.47,110.75C120.11,110.75 120.63,110.24 120.63,109.6C120.63,108.96 120.11,108.44 119.47,108.44C118.83,108.44 118.31,108.96 118.31,109.6C118.31,110.24 118.83,110.75 119.47,110.75Z"
        android:fillColor="#71BA33"/>
    <path
        android:pathData="M112.02,108.44C112.22,108.44 112.37,108.28 112.37,108.08C112.37,107.89 112.22,107.73 112.02,107.73C111.83,107.73 111.67,107.89 111.67,108.08C111.67,108.28 111.83,108.44 112.02,108.44Z"
        android:fillColor="#71BA33"/>
    <path
        android:pathData="M107.03,119.64C107.84,119.64 108.49,118.99 108.49,118.18C108.49,117.38 107.84,116.73 107.03,116.73C106.23,116.73 105.58,117.38 105.58,118.18C105.58,118.99 106.23,119.64 107.03,119.64Z"
        android:fillColor="#71BA33"/>
    <path
        android:pathData="M103.82,102.94C104.03,102.94 104.19,102.77 104.19,102.56C104.19,102.36 104.03,102.19 103.82,102.19C103.61,102.19 103.45,102.36 103.45,102.56C103.45,102.77 103.61,102.94 103.82,102.94Z"
        android:fillColor="#71BA33"/>
    <path
        android:pathData="M24.72,92.1C25.13,91.98 25.36,91.56 25.24,91.15C25.12,90.74 24.69,90.51 24.29,90.63C23.88,90.75 23.64,91.18 23.76,91.58C23.88,91.99 24.31,92.22 24.72,92.1Z"
        android:fillColor="#69B528"/>
    <path
        android:pathData="M30.72,120.68C31,120.6 31.16,120.31 31.07,120.03C30.99,119.75 30.7,119.6 30.43,119.68C30.15,119.76 29.99,120.05 30.07,120.32C30.16,120.6 30.45,120.76 30.72,120.68Z"
        android:fillColor="#69B528"/>
    <path
        android:pathData="M31.61,99.28C31.89,99.2 32.04,98.91 31.96,98.64C31.88,98.36 31.59,98.2 31.31,98.29C31.04,98.37 30.88,98.66 30.96,98.93C31.04,99.21 31.33,99.37 31.61,99.28Z"
        android:fillColor="#69B528"/>
    <path
        android:pathData="M19.58,119.72C19.85,119.63 20.01,119.35 19.93,119.07C19.85,118.79 19.56,118.64 19.28,118.72C19.01,118.8 18.85,119.09 18.93,119.36C19.01,119.64 19.3,119.8 19.58,119.72Z"
        android:fillColor="#69B528"/>
    <path
        android:pathData="M46.15,130.39C47.3,130.05 47.95,128.85 47.61,127.7C47.28,126.56 46.07,125.9 44.92,126.24C43.78,126.58 43.12,127.78 43.46,128.93C43.8,130.07 45,130.73 46.15,130.39Z"
        android:fillColor="#69B528"/>
    <path
        android:pathData="M22.16,107.03C22.71,106.87 23.03,106.29 22.87,105.74C22.7,105.18 22.12,104.87 21.57,105.03C21.02,105.2 20.7,105.78 20.87,106.33C21.03,106.88 21.61,107.19 22.16,107.03Z"
        android:fillColor="#69B528"/>
    <path
        android:pathData="M28.11,132.01C28.52,131.89 28.76,131.46 28.64,131.04C28.52,130.63 28.08,130.4 27.67,130.52C27.26,130.64 27.02,131.07 27.14,131.49C27.26,131.9 27.7,132.14 28.11,132.01Z"
        android:fillColor="#69B528"/>
  </group>
</vector>


================================================
FILE: app/src/main/res/layout/activity_main.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".core.presentation.MainActivity">

    <androidx.coordinatorlayout.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1">

        <com.google.android.material.appbar.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:fitsSystemWindows="true">

            <com.google.android.material.appbar.MaterialToolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:title="@string/app_name" />

        </com.google.android.material.appbar.AppBarLayout>

        <androidx.fragment.app.FragmentContainerView
            android:id="@+id/nav_host_fragment"
            android:name="androidx.navigation.fragment.NavHostFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:defaultNavHost="true"
            app:layout_behavior="@string/appbar_scrolling_view_behavior"
            app:navGraph="@navigation/nav_graph" />

    </androidx.coordinatorlayout.widget.CoordinatorLayout>

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bottom_navigation"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:menu="@menu/bottom_navigation_menu" />

</LinearLayout>

================================================
FILE: app/src/main/res/layout/activity_onboarding.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".core.presentation.OnboardingActivity">

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true"
        app:navGraph="@navigation/onboarding_nav_graph" />

</FrameLayout>

================================================
FILE: app/src/main/res/layout/activity_settings.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".settings.SettingsActivity">

    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:fitsSystemWindows="true">

        <com.google.android.material.appbar.MaterialToolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            app:title="@string/settings" />

    </com.google.android.material.appbar.AppBarLayout>

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/settings_fragment"
        android:name="pl.bartek537.forest.settings.SettingsFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />

</androidx.coordinatorlayout.widget.CoordinatorLayout>

================================================
FILE: app/src/main/res/layout/fragment_activity_recognition_permission.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    tools:context=".core.presentation.ActivityRecognitionPermissionFragment">

    <ImageView
        android:id="@+id/image_walk"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="38dp"
        android:importantForAccessibility="no"
        android:src="@drawable/directions_walk_fill0_wght400_grad0_opsz48"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:tint="?android:textColorPrimary" />

    <TextView
        android:id="@+id/text_permission_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="24dp"
        android:text="@string/allow_activity_recognition"
        android:textAppearance="?attr/textAppearanceHeadline5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/image_walk" />

    <TextView
        android:id="@+id/text_permission_rationale"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="12dp"
        android:text="@string/activity_recognition_rationale"
        android:textAppearance="?attr/textAppearanceBodyMedium"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/text_permission_title" />

    <ImageView
        android:id="@+id/icon_revoke_permission"
        android:layout_width="24dp"
        android:layout_height="24dp"
        android:layout_marginTop="38dp"
        android:importantForAccessibility="no"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/text_permission_rationale"
        app:srcCompat="@drawable/do_not_disturb_on_fill0_wght400_grad0_opsz24"
        app:tint="?android:textColorPrimary" />

    <TextView
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="12dp"
        android:text="@string/revoke_permission_rationale"
        android:textAppearance="?attr/textAppearanceBodyMedium"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/icon_revoke_permission"
        app:layout_constraintTop_toTopOf="@+id/icon_revoke_permission" />

    <TextView
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginBottom="12dp"
        android:text="@string/data_consent"
        android:textAppearance="?attr/textAppearanceBodySmall"
        app:layout_constraintBottom_toTopOf="@+id/button_continue"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

    <com.google.android.material.button.MaterialButton
        android:id="@+id/button_continue"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/action_continue"
        app:layout_constraintBottom_toBottomOf="parent"
        tools:layout_editor_absoluteX="16dp" />

</androidx.constraintlayout.widget.ConstraintLayout>

================================================
FILE: app/src/main/res/layout/fragment_forest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".trees.ForestFragment">

    <TextView
        android:id="@+id/text_trees_collected"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="16dp"
        android:textAppearance="?attr/textAppearanceDisplayMedium"
        android:textColor="?attr/colorOnSurface"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:text="12" />

    <TextView
        android:id="@+id/text_trees_collected_label"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="4dp"
        android:textAppearance="?attr/textAppearanceTitleMedium"
        android:textColor="?attr/colorOnSurface"
        app:layout_constraintBaseline_toBaselineOf="@id/text_trees_collected"
        app:layout_constraintStart_toEndOf="@id/text_trees_collected"
        tools:text="trees" />

    <ImageView
        android:id="@+id/image_ground"
        android:layout_width="match_parent"
        android:layout_height="64dp"
        android:importantForAccessibility="no"
        android:src="@drawable/shape_ground"
        app:layout_constraintBottom_toBottomOf="parent" />

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id='@+id/constraint_layout_trees'
        android:layout_width="match_parent"
        android:layout_height="400dp"
        android:layout_marginBottom="-40dp"
        app:layout_constraintBottom_toTopOf="@id/image_ground" />

</androidx.constraintlayout.widget.ConstraintLayout>

================================================
FILE: app/src/main/res/layout/fragment_progress.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".progress.ProgressFragment">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="16dp">

        <TextView
            android:id="@+id/text_step_count"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="?attr/textAppearanceDisplayMedium"
            android:textColor="?attr/colorOnSurface"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:text="3,837" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="4dp"
            android:text="@string/steps"
            android:textAppearance="?attr/textAppearanceTitleMedium"
            android:textColor="?attr/colorOnSurface"
            app:layout_constraintBaseline_toBaselineOf="@id/text_step_count"
            app:layout_constraintStart_toEndOf="@id/text_step_count" />

        <com.google.android.material.progressindicator.LinearProgressIndicator
            android:id="@+id/progress_daily_goal"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="8dp"
            app:indicatorColor="?attr/colorPrimary"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/text_step_count"
            app:trackColor="?attr/colorSurfaceVariant"
            app:trackCornerRadius="4dp"
            app:trackThickness="8dp"
            tools:max="7500"
            tools:progress="3837" />

        <TextView
            android:id="@+id/text_daily_goal"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="8dp"
            android:textAppearance="?attr/textAppearanceTitleSmall"
            android:textColor="?attr/colorOnSurfaceVariant"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/progress_daily_goal"
            tools:text="Goal 7,500" />

        <ImageView
            android:id="@+id/image_tree"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="48dp"
            android:adjustViewBounds="true"
            android:importantForAccessibility="no"
            android:maxHeight="320dp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/text_daily_goal"
            tools:src="@drawable/stage_6" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginHorizontal="8dp"
            android:layout_marginTop="36dp"
            android:orientation="vertical"
            app:layout_constraintTop_toBottomOf="@+id/image_tree">

            <androidx.cardview.widget.CardView
                style="@style/Widget.Material3.CardView.Filled"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:cardBackgroundColor="?attr/colorTertiaryContainer"
                app:cardCornerRadius="24dp">

                <androidx.constraintlayout.widget.ConstraintLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:orientation="vertical"
                    android:paddingHorizontal="16dp"
                    android:paddingVertical="18dp">

                    <View
                        android:id="@+id/view_calorie_background"
                        android:layout_width="48dp"
                        android:layout_height="48dp"
                        android:background="@drawable/shape_circle"
                        android:backgroundTint="?attr/colorTertiary"
                        app:layout_constraintBottom_toBottomOf="parent"
                        app:layout_constraintStart_toStartOf="parent"
                        app:layout_constraintTop_toTopOf="parent" />

                    <ImageView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:importantForAccessibility="no"
                        app:layout_constraintBottom_toBottomOf="@+id/view_calorie_background"
                        app:layout_constraintEnd_toEndOf="@+id/view_calorie_background"
                        app:layout_constraintStart_toStartOf="@+id/view_calorie_background"
                        app:layout_constraintTop_toTopOf="@+id/view_calorie_background"
                        app:srcCompat="@drawable/local_fire_department_fill0_wght400_grad0_opsz24"
                        app:tint="?attr/colorOnTertiary" />

                    <TextView
                        android:id="@+id/text_calorie_burned"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginStart="16dp"
                        android:layout_marginBottom="2dp"
                        android:textAppearance="?attr/textAppearanceTitleMedium"
                        app:layout_constraintBottom_toTopOf="@+id/text_calorie_burned_label"
                        app:layout_constraintStart_toEndOf="@+id/view_calorie_background"
                        app:layout_constraintTop_toTopOf="@+id/view_calorie_background"
                        app:layout_constraintVertical_chainStyle="packed"
                        tools:text="210 kcal" />

                    <TextView
                        android:id="@+id/text_calorie_burned_label"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="@string/calorie_burned"
                        android:textAppearance="?attr/textAppearanceBodyMedium"
                        app:layout_constraintBottom_toBottomOf="@+id/view_calorie_background"
                        app:layout_constraintStart_toStartOf="@+id/text_calorie_burned"
                        app:layout_constraintTop_toBottomOf="@+id/text_calorie_burned"
                        app:layout_constraintVertical_chainStyle="packed" />

                </androidx.constraintlayout.widget.ConstraintLayout>

            </androidx.cardview.widget.CardView>

            <androidx.cardview.widget.CardView
                style="@style/Widget.Material3.CardView.Filled"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="12dp"
                app:cardBackgroundColor="?attr/colorPrimaryContainer"
                app:cardCornerRadius="24dp">

                <androidx.constraintlayout.widget.ConstraintLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:orientation="vertical"
                    android:paddingHorizontal="16dp"
                    android:paddingVertical="18dp">

                    <View
                        android:id="@+id/view_distance_background"
                        android:layout_width="48dp"
                        android:layout_height="48dp"
                        android:background="@drawable/shape_circle"
                        android:backgroundTint="?attr/colorPrimary"
                        app:layout_constraintBottom_toBottomOf="parent"
                        app:layout_constraintStart_toStartOf="parent"
                        app:layout_constraintTop_toTopOf="parent" />

                    <ImageView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:importantForAccessibility="no"
                        app:layout_constraintBottom_toBottomOf="@+id/view_distance_background"
                        app:layout_constraintEnd_toEndOf="@+id/view_distance_background"
                        app:layout_constraintStart_toStartOf="@+id/view_distance_background"
                        app:layout_constraintTop_toTopOf="@+id/view_distance_background"
                        app:srcCompat="@drawable/conversion_path_fill0_wght400_grad0_opsz24"
                        app:tint="?attr/colorOnPrimary" />

                    <TextView
                        android:id="@+id/text_distance_travelled"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginStart="16dp"
                        android:layout_marginBottom="2dp"
                        android:textAppearance="?attr/textAppearanceTitleMedium"
                        app:layout_constraintBottom_toTopOf="@+id/text_distance_travelled_label"
                        app:layout_constraintStart_toEndOf="@+id/view_distance_background"
                        app:layout_constraintTop_toTopOf="@+id/view_distance_background"
                        app:layout_constraintVertical_chainStyle="packed"
                        tools:text="4.1 km" />

                    <TextView
                        android:id="@+id/text_distance_travelled_label"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="@string/distance_travelled"
                        android:textAppearance="?attr/textAppearanceBodyMedium"
                        app:layout_constraintBottom_toBottomOf="@+id/view_distance_background"
                        app:layout_constraintStart_toStartOf="@+id/text_distance_travelled"
                        app:layout_constraintTop_toBottomOf="@+id/text_distance_travelled"
                        app:layout_constraintVertical_chainStyle="packed" />

                </androidx.constraintlayout.widget.ConstraintLayout>

            </androidx.cardview.widget.CardView>

            <androidx.cardview.widget.CardView
                style="@style/Widget.Material3.CardView.Filled"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="12dp"
                app:cardBackgroundColor="?attr/colorSecondaryContainer"
                app:cardCornerRadius="24dp">

                <androidx.constraintlayout.widget.ConstraintLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:orientation="vertical"
                    android:paddingHorizontal="16dp"
                    android:paddingVertical="18dp">

                    <View
                        android:id="@+id/view_carbon_dioxide_background"
                        android:layout_width="48dp"
                        android:layout_height="48dp"
                        android:background="@drawable/shape_circle"
                        android:backgroundTint="?attr/colorSecondary"
                        app:layout_constraintBottom_toBottomOf="parent"
                        app:layout_constraintStart_toStartOf="parent"
                        app:layout_constraintTop_toTopOf="parent" />

                    <ImageView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:importantForAccessibility="no"
                        app:layout_constraintBottom_toBottomOf="@+id/view_carbon_dioxide_background"
                        app:layout_constraintEnd_toEndOf="@+id/view_carbon_dioxide_background"
                        app:layout_constraintStart_toStartOf="@+id/view_carbon_dioxide_background"
                        app:layout_constraintTop_toTopOf="@+id/view_carbon_dioxide_background"
                        app:srcCompat="@drawable/bubble_chart_fill0_wght400_grad0_opsz24"
                        app:tint="?attr/colorOnSecondary" />

                    <TextView
                        android:id="@+id/text_carbon_dioxide_saved"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginStart="16dp"
                        android:layout_marginBottom="2dp"
                        android:textAppearance="?attr/textAppearanceTitleMedium"
                        app:layout_constraintBottom_toTopOf="@+id/text_carbon_dioxide_saved_label"
                        app:layout_constraintStart_toEndOf="@+id/view_carbon_dioxide_background"
                        app:layout_constraintTop_toTopOf="@+id/view_carbon_dioxide_background"
                        app:layout_constraintVertical_chainStyle="packed"
                        tools:text="1.43 kg" />

                    <TextView
                        android:id="@+id/text_carbon_dioxide_saved_label"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="@string/carbon_dioxide_saved"
                        android:textAppearance="?attr/textAppearanceBodyMedium"
                        app:layout_constraintBottom_toBottomOf="@+id/view_carbon_dioxide_background"
                        app:layout_constraintStart_toStartOf="@+id/text_carbon_dioxide_saved"
                        app:layout_constraintTop_toBottomOf="@+id/text_carbon_dioxide_saved"
                        app:layout_constraintVertical_chainStyle="packed" />

                </androidx.constraintlayout.widget.ConstraintLayout>

            </androidx.cardview.widget.CardView>

        </LinearLayout>

    </androidx.constraintlayout.widget.ConstraintLayout>

</androidx.core.widget.NestedScrollView>

================================================
FILE: app/src/main/res/layout/fragment_stats.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".stats.StatsFragment">

    <com.google.android.material.tabs.TabLayout
        android:id="@+id/tab_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

================================================
FILE: app/src/main/res/layout/fragment_stats_chart.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".stats.presentation.StatsChartFragment"
    tools:layout_margin="24dp">

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/view_pager_chart"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:layoutDirection="rtl"
        tools:background="?attr/colorPrimaryContainer" />

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="24dp"
        android:minHeight="48dp">

        <ImageButton
            android:id="@+id/button_previous_day"
            android:layout_width="48dp"
            android:layout_height="48dp"
            android:background="@null"
            android:clickable="true"
            android:contentDescription="@string/previous_day"
            android:focusable="true"
            android:src="@drawable/chevron_left_fill0_wght400_grad0_opsz24"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:tint="?attr/colorOnBackground" />

        <TextView
            android:id="@+id/text_selected_date"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="?attr/textAppearanceLabelLarge"
            android:textColor="?attr/colorOnBackground"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:text="Fri, 12 Jun" />

        <ImageButton
            android:id="@+id/button_next_day"
            android:layout_width="48dp"
            android:layout_height="48dp"
            android:background="@null"
            android:clickable="true"
            android:contentDescription="@string/next_day"
            android:focusable="true"
            android:src="@drawable/chevron_right_fill0_wght400_grad0_opsz24"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:tint="?attr/colorOnBackground" />

    </androidx.constraintlayout.widget.ConstraintLayout>

</LinearLayout>

================================================
FILE: app/src/main/res/layout/fragment_stats_details.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clipToPadding="false"
    android:padding="24dp"
    tools:context=".stats.presentation.StatsDetailsFragment">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <androidx.fragment.app.FragmentContainerView
            android:id="@+id/fragment_chart"
            android:name="pl.bartek537.forest.stats.presentation.StatsChartFragment"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            tools:layout="@layout/fragment_stats_chart" />

        <androidx.cardview.widget.CardView
            style="@style/Widget.Material3.CardView.Filled"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="36dp"
            app:cardBackgroundColor="?attr/colorSecondaryContainer"
            app:cardCornerRadius="24dp">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:divider="@drawable/shape_divider"
                android:orientation="vertical"
                android:showDividers="middle">

                <androidx.constraintlayout.widget.ConstraintLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:orientation="vertical"
                    android:paddingHorizontal="16dp"
                    android:paddingVertical="18dp">

                    <View
                        android:id="@+id/view_step_background"
                        android:layout_width="48dp"
                        android:layout_height="48dp"
                        android:background="@drawable/shape_circle"
                        android:backgroundTint="?attr/colorSecondary"
                        app:layout_constraintBottom_toBottomOf="parent"
                        app:layout_constraintStart_toStartOf="parent"
                        app:layout_constraintTop_toTopOf="parent" />

                    <ImageView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:importantForAccessibility="no"
                        app:layout_constraintBottom_toBottomOf="@+id/view_step_background"
                        app:layout_constraintEnd_toEndOf="@+id/view_step_background"
                        app:layout_constraintStart_toStartOf="@+id/view_step_background"
                        app:layout_constraintTop_toTopOf="@+id/view_step_background"
                        app:srcCompat="@drawable/steps_fill0_wght400_grad0_opsz24
Download .txt
gitextract__9jakuws/

├── .gitignore
├── .idea/
│   ├── .gitignore
│   ├── .name
│   ├── AndroidProjectSystem.xml
│   ├── codeStyles/
│   │   ├── Project.xml
│   │   └── codeStyleConfig.xml
│   ├── compiler.xml
│   ├── deploymentTargetSelector.xml
│   ├── deviceManager.xml
│   ├── gradle.xml
│   ├── kotlinc.xml
│   ├── migrations.xml
│   ├── misc.xml
│   ├── runConfigurations.xml
│   └── vcs.xml
├── README.md
├── app/
│   ├── .gitignore
│   ├── build.gradle
│   ├── proguard-rules.pro
│   └── src/
│       ├── androidTest/
│       │   └── java/
│       │       └── pl/
│       │           └── bartek537/
│       │               └── forest/
│       │                   └── ExampleInstrumentedTest.kt
│       ├── main/
│       │   ├── AndroidManifest.xml
│       │   ├── java/
│       │   │   └── pl/
│       │   │       └── bartek537/
│       │   │           └── forest/
│       │   │               ├── ForestApplication.kt
│       │   │               ├── core/
│       │   │               │   ├── data/
│       │   │               │   │   ├── repository/
│       │   │               │   │   │   └── DayRepositoryImpl.kt
│       │   │               │   │   └── source/
│       │   │               │   │       ├── DayDao.kt
│       │   │               │   │       ├── ForestDatabase.kt
│       │   │               │   │       └── util/
│       │   │               │   │           └── Converters.kt
│       │   │               │   ├── domain/
│       │   │               │   │   ├── model/
│       │   │               │   │   │   ├── Day.kt
│       │   │               │   │   │   ├── DaySettings.kt
│       │   │               │   │   │   └── StatsSummary.kt
│       │   │               │   │   ├── repository/
│       │   │               │   │   │   └── DayRepository.kt
│       │   │               │   │   └── usecase/
│       │   │               │   │       ├── DayUseCases.kt
│       │   │               │   │       ├── GetDay.kt
│       │   │               │   │       ├── GetDayImpl.kt
│       │   │               │   │       ├── IncrementStepCount.kt
│       │   │               │   │       └── IncrementStepCountImpl.kt
│       │   │               │   └── presentation/
│       │   │               │       ├── ActivityRecognitionPermissionFragment.kt
│       │   │               │       ├── MainActivity.kt
│       │   │               │       ├── OnboardingActivity.kt
│       │   │               │       └── SplashActivity.kt
│       │   │               ├── progress/
│       │   │               │   ├── ProgressFragment.kt
│       │   │               │   ├── ProgressState.kt
│       │   │               │   └── ProgressViewModel.kt
│       │   │               ├── service/
│       │   │               │   ├── StepCounterController.kt
│       │   │               │   ├── StepCounterEvent.kt
│       │   │               │   ├── StepCounterService.kt
│       │   │               │   ├── StepCounterServiceLauncher.kt
│       │   │               │   └── StepCounterState.kt
│       │   │               ├── settings/
│       │   │               │   ├── SettingsActivity.kt
│       │   │               │   ├── SettingsFragment.kt
│       │   │               │   ├── SettingsViewModel.kt
│       │   │               │   ├── data/
│       │   │               │   │   ├── repository/
│       │   │               │   │   │   └── SettingsRepositoryImpl.kt
│       │   │               │   │   └── source/
│       │   │               │   │       ├── SettingsStore.kt
│       │   │               │   │       └── SettingsStoreImpl.kt
│       │   │               │   └── domain/
│       │   │               │       ├── model/
│       │   │               │       │   └── Settings.kt
│       │   │               │       ├── repository/
│       │   │               │       │   └── SettingsRepository.kt
│       │   │               │       └── usecase/
│       │   │               │           ├── GetSettings.kt
│       │   │               │           ├── SettingsUseCases.kt
│       │   │               │           └── UpdateDaySettings.kt
│       │   │               ├── stats/
│       │   │               │   ├── StatsFragment.kt
│       │   │               │   ├── domain/
│       │   │               │   │   └── usecase/
│       │   │               │   │       ├── GetFirstDate.kt
│       │   │               │   │       ├── GetSummary.kt
│       │   │               │   │       ├── GetWeek.kt
│       │   │               │   │       ├── StatsChartPageUseCases.kt
│       │   │               │   │       ├── StatsDetailsUseCases.kt
│       │   │               │   │       └── StatsSummaryUseCases.kt
│       │   │               │   ├── presentation/
│       │   │               │   │   ├── ChartAdapter.kt
│       │   │               │   │   ├── StatsChartFragment.kt
│       │   │               │   │   ├── StatsChartPageFragment.kt
│       │   │               │   │   ├── StatsChartPageViewModel.kt
│       │   │               │   │   ├── StatsChartState.kt
│       │   │               │   │   ├── StatsDetailsFragment.kt
│       │   │               │   │   ├── StatsDetailsState.kt
│       │   │               │   │   ├── StatsDetailsViewModel.kt
│       │   │               │   │   ├── StatsSummaryFragment.kt
│       │   │               │   │   ├── StatsSummaryState.kt
│       │   │               │   │   └── StatsSummaryViewModel.kt
│       │   │               │   └── util/
│       │   │               │       ├── ContextExtension.kt
│       │   │               │       ├── DayExtension.kt
│       │   │               │       └── LocalDateExtension.kt
│       │   │               └── trees/
│       │   │                   ├── ForestFragment.kt
│       │   │                   ├── ForestState.kt
│       │   │                   ├── ForestViewModel.kt
│       │   │                   └── domain/
│       │   │                       └── usecase/
│       │   │                           ├── ForestUseCases.kt
│       │   │                           └── GetTreeCount.kt
│       │   └── res/
│       │       ├── drawable/
│       │       │   ├── bubble_chart_fill0_wght400_grad0_opsz24.xml
│       │       │   ├── chevron_left_fill0_wght400_grad0_opsz24.xml
│       │       │   ├── chevron_right_fill0_wght400_grad0_opsz24.xml
│       │       │   ├── conversion_path_fill0_wght400_grad0_opsz24.xml
│       │       │   ├── directions_walk_fill0_wght400_grad0_opsz48.xml
│       │       │   ├── do_not_disturb_on_fill0_wght400_grad0_opsz24.xml
│       │       │   ├── forest_fill0_wght400_grad0_opsz24.xml
│       │       │   ├── ic_launcher_background.xml
│       │       │   ├── ic_launcher_foreground.xml
│       │       │   ├── local_fire_department_fill0_wght400_grad0_opsz24.xml
│       │       │   ├── nature_fill0_wght400_grad0_opsz24.xml
│       │       │   ├── shape_chart_bar.xml
│       │       │   ├── shape_circle.xml
│       │       │   ├── shape_divider.xml
│       │       │   ├── shape_ground.xml
│       │       │   ├── show_chart_fill0_wght400_grad0_opsz24.xml
│       │       │   ├── stage_1.xml
│       │       │   ├── stage_2.xml
│       │       │   ├── stage_3.xml
│       │       │   ├── stage_4.xml
│       │       │   ├── stage_5.xml
│       │       │   ├── stage_6.xml
│       │       │   ├── steps_fill0_wght400_grad0_opsz24.xml
│       │       │   └── tree_collected.xml
│       │       ├── layout/
│       │       │   ├── activity_main.xml
│       │       │   ├── activity_onboarding.xml
│       │       │   ├── activity_settings.xml
│       │       │   ├── fragment_activity_recognition_permission.xml
│       │       │   ├── fragment_forest.xml
│       │       │   ├── fragment_progress.xml
│       │       │   ├── fragment_stats.xml
│       │       │   ├── fragment_stats_chart.xml
│       │       │   ├── fragment_stats_details.xml
│       │       │   ├── fragment_stats_page_chart.xml
│       │       │   ├── fragment_stats_summary.xml
│       │       │   └── item_chart_bar.xml
│       │       ├── menu/
│       │       │   ├── bottom_navigation_menu.xml
│       │       │   └── main_menu.xml
│       │       ├── mipmap-anydpi-v26/
│       │       │   ├── ic_launcher.xml
│       │       │   └── ic_launcher_round.xml
│       │       ├── navigation/
│       │       │   ├── nav_graph.xml
│       │       │   └── onboarding_nav_graph.xml
│       │       ├── values/
│       │       │   ├── colors.xml
│       │       │   ├── ic_launcher_background.xml
│       │       │   ├── strings.xml
│       │       │   └── themes.xml
│       │       ├── values-night/
│       │       │   └── themes.xml
│       │       ├── values-v29/
│       │       │   └── themes.xml
│       │       └── xml/
│       │           ├── backup_rules.xml
│       │           ├── data_extraction_rules.xml
│       │           └── settings.xml
│       └── test/
│           └── java/
│               └── pl/
│                   └── bartek537/
│                       └── forest/
│                           └── ExampleUnitTest.kt
├── build.gradle
├── gradle/
│   ├── libs.versions.toml
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
Condensed preview — 144 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (276K chars).
[
  {
    "path": ".gitignore",
    "chars": 225,
    "preview": "*.iml\n.gradle\n/local.properties\n/.idea/caches\n/.idea/libraries\n/.idea/modules.xml\n/.idea/workspace.xml\n/.idea/navEditor."
  },
  {
    "path": ".idea/.gitignore",
    "chars": 47,
    "preview": "# Default ignored files\n/shelf/\n/workspace.xml\n"
  },
  {
    "path": ".idea/.name",
    "chars": 6,
    "preview": "Forest"
  },
  {
    "path": ".idea/AndroidProjectSystem.xml",
    "chars": 212,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"AndroidProjectSystem\">\n    <option name="
  },
  {
    "path": ".idea/codeStyles/Project.xml",
    "chars": 5383,
    "preview": "<component name=\"ProjectCodeStyleConfiguration\">\n  <code_scheme name=\"Project\" version=\"173\">\n    <JavaCodeStyleSettings"
  },
  {
    "path": ".idea/codeStyles/codeStyleConfig.xml",
    "chars": 142,
    "preview": "<component name=\"ProjectCodeStyleConfiguration\">\n  <state>\n    <option name=\"USE_PER_PROJECT_SETTINGS\" value=\"true\" />\n "
  },
  {
    "path": ".idea/compiler.xml",
    "chars": 169,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"CompilerConfiguration\">\n    <bytecodeTar"
  },
  {
    "path": ".idea/deploymentTargetSelector.xml",
    "chars": 301,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"deploymentTargetSelector\">\n    <selectio"
  },
  {
    "path": ".idea/deviceManager.xml",
    "chars": 351,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"DeviceTable\">\n    <option name=\"columnSo"
  },
  {
    "path": ".idea/gradle.xml",
    "chars": 690,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"GradleMigrationSettings\" migrationVersio"
  },
  {
    "path": ".idea/kotlinc.xml",
    "chars": 175,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"KotlinJpsPluginSettings\">\n    <option na"
  },
  {
    "path": ".idea/migrations.xml",
    "chars": 254,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"ProjectMigrations\">\n    <option name=\"Mi"
  },
  {
    "path": ".idea/misc.xml",
    "chars": 448,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"ExternalStorageConfigurationManager\" ena"
  },
  {
    "path": ".idea/runConfigurations.xml",
    "chars": 964,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"RunConfigurationProducerService\">\n    <o"
  },
  {
    "path": ".idea/vcs.xml",
    "chars": 180,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"VcsDirectoryMappings\">\n    <mapping dire"
  },
  {
    "path": "README.md",
    "chars": 2782,
    "preview": "# Forest\n\nTrack your daily step count, stay healthy and fight the climate change, one step at a time.\n\n![banner](https:/"
  },
  {
    "path": "app/.gitignore",
    "chars": 6,
    "preview": "/build"
  },
  {
    "path": "app/build.gradle",
    "chars": 1979,
    "preview": "import org.jetbrains.kotlin.gradle.dsl.JvmTarget\n\nplugins {\n    alias libs.plugins.android.application\n    alias libs.pl"
  },
  {
    "path": "app/proguard-rules.pro",
    "chars": 750,
    "preview": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguar"
  },
  {
    "path": "app/src/androidTest/java/pl/bartek537/forest/ExampleInstrumentedTest.kt",
    "chars": 660,
    "preview": "package pl.bartek537.forest\n\nimport androidx.test.platform.app.InstrumentationRegistry\nimport androidx.test.ext.junit.ru"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "chars": 2276,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:to"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/ForestApplication.kt",
    "chars": 2330,
    "preview": "package pl.bartek537.forest\n\nimport android.app.Application\nimport android.content.BroadcastReceiver\nimport android.cont"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/core/data/repository/DayRepositoryImpl.kt",
    "chars": 1108,
    "preview": "package pl.bartek537.forest.core.data.repository\n\nimport kotlinx.coroutines.flow.Flow\nimport pl.bartek537.forest.core.da"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/core/data/source/DayDao.kt",
    "chars": 908,
    "preview": "package pl.bartek537.forest.core.data.source\n\nimport androidx.room.*\nimport kotlinx.coroutines.flow.Flow\nimport pl.barte"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/core/data/source/ForestDatabase.kt",
    "chars": 504,
    "preview": "package pl.bartek537.forest.core.data.source\n\nimport androidx.room.Database\nimport androidx.room.RoomDatabase\nimport and"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/core/data/source/util/Converters.kt",
    "chars": 398,
    "preview": "package pl.bartek537.forest.core.data.source.util\n\nimport androidx.room.TypeConverter\nimport java.time.LocalDate\n\n@Suppr"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/core/domain/model/Day.kt",
    "chars": 1225,
    "preview": "package pl.bartek537.forest.core.domain.model\n\nimport androidx.room.Entity\nimport androidx.room.PrimaryKey\nimport pl.bar"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/core/domain/model/DaySettings.kt",
    "chars": 239,
    "preview": "package pl.bartek537.forest.core.domain.model\n\nimport java.time.LocalDate\n\ndata class DaySettings(\n\n    val date: LocalD"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/core/domain/model/StatsSummary.kt",
    "chars": 887,
    "preview": "package pl.bartek537.forest.core.domain.model\n\ndata class StatsSummary(\n    val treesCollected: Int = 0,\n    val stepsTa"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/core/domain/repository/DayRepository.kt",
    "chars": 568,
    "preview": "package pl.bartek537.forest.core.domain.repository\n\nimport kotlinx.coroutines.flow.Flow\nimport pl.bartek537.forest.core."
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/core/domain/usecase/DayUseCases.kt",
    "chars": 455,
    "preview": "package pl.bartek537.forest.core.domain.usecase\n\nimport pl.bartek537.forest.core.domain.repository.DayRepository\nimport "
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/core/domain/usecase/GetDay.kt",
    "chars": 235,
    "preview": "package pl.bartek537.forest.core.domain.usecase\n\nimport kotlinx.coroutines.flow.Flow\nimport pl.bartek537.forest.core.dom"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/core/domain/usecase/GetDayImpl.kt",
    "chars": 818,
    "preview": "package pl.bartek537.forest.core.domain.usecase\n\nimport kotlinx.coroutines.flow.Flow\nimport kotlinx.coroutines.flow.comb"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/core/domain/usecase/IncrementStepCount.kt",
    "chars": 168,
    "preview": "package pl.bartek537.forest.core.domain.usecase\n\nimport java.time.LocalDate\n\ninterface IncrementStepCount {\n\n    suspend"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/core/domain/usecase/IncrementStepCountImpl.kt",
    "chars": 527,
    "preview": "package pl.bartek537.forest.core.domain.usecase\n\nimport kotlinx.coroutines.flow.first\nimport pl.bartek537.forest.core.do"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/core/presentation/ActivityRecognitionPermissionFragment.kt",
    "chars": 2501,
    "preview": "package pl.bartek537.forest.core.presentation\n\nimport android.Manifest\nimport android.content.Intent\nimport android.cont"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/core/presentation/MainActivity.kt",
    "chars": 3282,
    "preview": "package pl.bartek537.forest.core.presentation\n\nimport android.content.Intent\nimport android.content.pm.PackageManager\nim"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/core/presentation/OnboardingActivity.kt",
    "chars": 941,
    "preview": "package pl.bartek537.forest.core.presentation\n\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatActivity\n"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/core/presentation/SplashActivity.kt",
    "chars": 1497,
    "preview": "package pl.bartek537.forest.core.presentation\n\nimport android.Manifest\nimport android.annotation.SuppressLint\nimport and"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/progress/ProgressFragment.kt",
    "chars": 3229,
    "preview": "package pl.bartek537.forest.progress\n\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.Vi"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/progress/ProgressState.kt",
    "chars": 266,
    "preview": "package pl.bartek537.forest.progress\n\nimport java.time.LocalDate\n\ndata class ProgressState(\n    val date: LocalDate,\n   "
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/progress/ProgressViewModel.kt",
    "chars": 2707,
    "preview": "package pl.bartek537.forest.progress\n\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.ViewModelProvider\nim"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/service/StepCounterController.kt",
    "chars": 1909,
    "preview": "package pl.bartek537.forest.service\n\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.Job\nimport kotli"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/service/StepCounterEvent.kt",
    "chars": 149,
    "preview": "package pl.bartek537.forest.service\n\nimport java.time.LocalDate\n\ndata class StepCounterEvent(\n    val stepCount: Int,\n  "
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/service/StepCounterService.kt",
    "chars": 5482,
    "preview": "package pl.bartek537.forest.service\n\nimport android.app.Notification\nimport android.app.NotificationChannel\nimport andro"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/service/StepCounterServiceLauncher.kt",
    "chars": 1267,
    "preview": "package pl.bartek537.forest.service\n\nimport android.Manifest\nimport android.content.BroadcastReceiver\nimport android.con"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/service/StepCounterState.kt",
    "chars": 221,
    "preview": "package pl.bartek537.forest.service\n\nimport java.time.LocalDate\n\ndata class StepCounterState(\n    val date: LocalDate,\n "
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/settings/SettingsActivity.kt",
    "chars": 748,
    "preview": "package pl.bartek537.forest.settings\n\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatActivity\nimport an"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/settings/SettingsFragment.kt",
    "chars": 1483,
    "preview": "package pl.bartek537.forest.settings\n\nimport android.os.Bundle\nimport android.text.InputType\nimport androidx.fragment.ap"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/settings/SettingsViewModel.kt",
    "chars": 2163,
    "preview": "package pl.bartek537.forest.settings\n\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.ViewModelProvider\nim"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/settings/data/repository/SettingsRepositoryImpl.kt",
    "chars": 484,
    "preview": "package pl.bartek537.forest.settings.data.repository\n\nimport kotlinx.coroutines.flow.Flow\nimport pl.bartek537.forest.set"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/settings/data/source/SettingsStore.kt",
    "chars": 211,
    "preview": "package pl.bartek537.forest.settings.data.source\n\nimport kotlinx.coroutines.flow.Flow\nimport pl.bartek537.forest.setting"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/settings/data/source/SettingsStoreImpl.kt",
    "chars": 1788,
    "preview": "package pl.bartek537.forest.settings.data.source\n\nimport android.content.SharedPreferences\nimport android.content.Shared"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/settings/domain/model/Settings.kt",
    "chars": 186,
    "preview": "package pl.bartek537.forest.settings.domain.model\n\ndata class Settings(\n    val dailyGoal: Int,\n    val stepLength: Int,"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/settings/domain/repository/SettingsRepository.kt",
    "chars": 222,
    "preview": "package pl.bartek537.forest.settings.domain.repository\n\nimport kotlinx.coroutines.flow.Flow\nimport pl.bartek537.forest.s"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/settings/domain/usecase/GetSettings.kt",
    "chars": 471,
    "preview": "package pl.bartek537.forest.settings.domain.usecase\n\nimport kotlinx.coroutines.flow.Flow\nimport pl.bartek537.forest.sett"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/settings/domain/usecase/SettingsUseCases.kt",
    "chars": 454,
    "preview": "package pl.bartek537.forest.settings.domain.usecase\n\nimport pl.bartek537.forest.core.domain.repository.DayRepository\nimp"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/settings/domain/usecase/UpdateDaySettings.kt",
    "chars": 486,
    "preview": "package pl.bartek537.forest.settings.domain.usecase\n\nimport pl.bartek537.forest.core.domain.model.DaySettings\nimport pl."
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/stats/StatsFragment.kt",
    "chars": 1798,
    "preview": "package pl.bartek537.forest.stats\n\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.View\n"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/stats/domain/usecase/GetFirstDate.kt",
    "chars": 505,
    "preview": "package pl.bartek537.forest.stats.domain.usecase\n\nimport kotlinx.coroutines.flow.Flow\nimport kotlinx.coroutines.flow.map"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/stats/domain/usecase/GetSummary.kt",
    "chars": 535,
    "preview": "package pl.bartek537.forest.stats.domain.usecase\n\nimport pl.bartek537.forest.core.domain.model.StatsSummary\nimport pl.ba"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/stats/domain/usecase/GetWeek.kt",
    "chars": 572,
    "preview": "package pl.bartek537.forest.stats.domain.usecase\n\nimport kotlinx.coroutines.flow.Flow\nimport pl.bartek537.forest.core.do"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/stats/domain/usecase/StatsChartPageUseCases.kt",
    "chars": 238,
    "preview": "package pl.bartek537.forest.stats.domain.usecase\n\nimport pl.bartek537.forest.core.domain.repository.DayRepository\n\nclass"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/stats/domain/usecase/StatsDetailsUseCases.kt",
    "chars": 251,
    "preview": "package pl.bartek537.forest.stats.domain.usecase\n\nimport pl.bartek537.forest.core.domain.repository.DayRepository\n\nclass"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/stats/domain/usecase/StatsSummaryUseCases.kt",
    "chars": 245,
    "preview": "package pl.bartek537.forest.stats.domain.usecase\n\nimport pl.bartek537.forest.core.domain.repository.DayRepository\n\nclass"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/stats/presentation/ChartAdapter.kt",
    "chars": 2699,
    "preview": "package pl.bartek537.forest.stats.presentation\n\nimport android.content.res.ColorStateList\nimport android.view.LayoutInfl"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/stats/presentation/StatsChartFragment.kt",
    "chars": 3636,
    "preview": "package pl.bartek537.forest.stats.presentation\n\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport andro"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/stats/presentation/StatsChartPageFragment.kt",
    "chars": 3143,
    "preview": "package pl.bartek537.forest.stats.presentation\n\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport andro"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/stats/presentation/StatsChartPageViewModel.kt",
    "chars": 1915,
    "preview": "package pl.bartek537.forest.stats.presentation\n\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.ViewModelP"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/stats/presentation/StatsChartState.kt",
    "chars": 390,
    "preview": "package pl.bartek537.forest.stats.presentation\n\nimport pl.bartek537.forest.core.domain.model.Day\nimport java.time.LocalD"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/stats/presentation/StatsDetailsFragment.kt",
    "chars": 2128,
    "preview": "package pl.bartek537.forest.stats.presentation\n\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport andro"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/stats/presentation/StatsDetailsState.kt",
    "chars": 335,
    "preview": "package pl.bartek537.forest.stats.presentation\n\nimport java.time.LocalDate\n\ndata class StatsDetailsState(\n    val date: "
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/stats/presentation/StatsDetailsViewModel.kt",
    "chars": 3282,
    "preview": "package pl.bartek537.forest.stats.presentation\n\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.ViewModelP"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/stats/presentation/StatsSummaryFragment.kt",
    "chars": 2456,
    "preview": "package pl.bartek537.forest.stats.presentation\n\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport andro"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/stats/presentation/StatsSummaryState.kt",
    "chars": 303,
    "preview": "package pl.bartek537.forest.stats.presentation\n\ndata class StatsSummaryState(\n    val isRefreshing: Boolean = false,\n   "
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/stats/presentation/StatsSummaryViewModel.kt",
    "chars": 2363,
    "preview": "package pl.bartek537.forest.stats.presentation\n\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.ViewModelP"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/stats/util/ContextExtension.kt",
    "chars": 320,
    "preview": "package pl.bartek537.forest.stats.util\n\nimport android.content.Context\nimport android.util.TypedValue\nimport androidx.an"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/stats/util/DayExtension.kt",
    "chars": 457,
    "preview": "package pl.bartek537.forest.stats.util\n\nimport pl.bartek537.forest.core.domain.model.Day\nimport java.time.LocalDate\n\nfun"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/stats/util/LocalDateExtension.kt",
    "chars": 1345,
    "preview": "package pl.bartek537.forest.stats.util\n\nimport com.google.android.material.R\nimport pl.bartek537.forest.core.domain.mode"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/trees/ForestFragment.kt",
    "chars": 2694,
    "preview": "package pl.bartek537.forest.trees\n\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.View\n"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/trees/ForestState.kt",
    "chars": 83,
    "preview": "package pl.bartek537.forest.trees\n\ndata class ForestState(\n    val treeCount: Int\n)"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/trees/ForestViewModel.kt",
    "chars": 1618,
    "preview": "package pl.bartek537.forest.trees\n\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.ViewModelProvider\nimpor"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/trees/domain/usecase/ForestUseCases.kt",
    "chars": 245,
    "preview": "package pl.bartek537.forest.trees.domain.usecase\n\nimport pl.bartek537.forest.core.domain.repository.DayRepository\n\nclass"
  },
  {
    "path": "app/src/main/java/pl/bartek537/forest/trees/domain/usecase/GetTreeCount.kt",
    "chars": 395,
    "preview": "package pl.bartek537.forest.trees.domain.usecase\n\nimport kotlinx.coroutines.flow.Flow\nimport pl.bartek537.forest.core.do"
  },
  {
    "path": "app/src/main/res/drawable/bubble_chart_fill0_wght400_grad0_opsz24.xml",
    "chars": 891,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n  "
  },
  {
    "path": "app/src/main/res/drawable/chevron_left_fill0_wght400_grad0_opsz24.xml",
    "chars": 308,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n  "
  },
  {
    "path": "app/src/main/res/drawable/chevron_right_fill0_wght400_grad0_opsz24.xml",
    "chars": 305,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n  "
  },
  {
    "path": "app/src/main/res/drawable/conversion_path_fill0_wght400_grad0_opsz24.xml",
    "chars": 949,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n  "
  },
  {
    "path": "app/src/main/res/drawable/directions_walk_fill0_wght400_grad0_opsz48.xml",
    "chars": 699,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"48dp\"\n    android:height=\"48dp\"\n  "
  },
  {
    "path": "app/src/main/res/drawable/do_not_disturb_on_fill0_wght400_grad0_opsz24.xml",
    "chars": 747,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n  "
  },
  {
    "path": "app/src/main/res/drawable/forest_fill0_wght400_grad0_opsz24.xml",
    "chars": 569,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n  "
  },
  {
    "path": "app/src/main/res/drawable/ic_launcher_background.xml",
    "chars": 5606,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:wi"
  },
  {
    "path": "app/src/main/res/drawable/ic_launcher_foreground.xml",
    "chars": 2209,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n"
  },
  {
    "path": "app/src/main/res/drawable/local_fire_department_fill0_wght400_grad0_opsz24.xml",
    "chars": 1145,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n  "
  },
  {
    "path": "app/src/main/res/drawable/nature_fill0_wght400_grad0_opsz24.xml",
    "chars": 772,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n  "
  },
  {
    "path": "app/src/main/res/drawable/shape_chart_bar.xml",
    "chars": 299,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools"
  },
  {
    "path": "app/src/main/res/drawable/shape_circle.xml",
    "chars": 241,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools"
  },
  {
    "path": "app/src/main/res/drawable/shape_divider.xml",
    "chars": 266,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<inset xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:ins"
  },
  {
    "path": "app/src/main/res/drawable/shape_ground.xml",
    "chars": 265,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <solid and"
  },
  {
    "path": "app/src/main/res/drawable/show_chart_fill0_wght400_grad0_opsz24.xml",
    "chars": 322,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n  "
  },
  {
    "path": "app/src/main/res/drawable/stage_1.xml",
    "chars": 2062,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"500dp\"\n    android:height=\"500dp\"\n"
  },
  {
    "path": "app/src/main/res/drawable/stage_2.xml",
    "chars": 3340,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"500dp\"\n    android:height=\"500dp\"\n"
  },
  {
    "path": "app/src/main/res/drawable/stage_3.xml",
    "chars": 4290,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"500dp\"\n    android:height=\"500dp\"\n"
  },
  {
    "path": "app/src/main/res/drawable/stage_4.xml",
    "chars": 5436,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"500dp\"\n    android:height=\"500dp\"\n"
  },
  {
    "path": "app/src/main/res/drawable/stage_5.xml",
    "chars": 7517,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"500dp\"\n    android:height=\"500dp\"\n"
  },
  {
    "path": "app/src/main/res/drawable/stage_6.xml",
    "chars": 8505,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"500dp\"\n    android:height=\"500dp\"\n"
  },
  {
    "path": "app/src/main/res/drawable/steps_fill0_wght400_grad0_opsz24.xml",
    "chars": 1098,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n  "
  },
  {
    "path": "app/src/main/res/drawable/tree_collected.xml",
    "chars": 7813,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"150dp\"\n    android:height=\"200dp\"\n"
  },
  {
    "path": "app/src/main/res/layout/activity_main.xml",
    "chars": 1855,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmln"
  },
  {
    "path": "app/src/main/res/layout/activity_onboarding.xml",
    "chars": 717,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns"
  },
  {
    "path": "app/src/main/res/layout/activity_settings.xml",
    "chars": 1254,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schema"
  },
  {
    "path": "app/src/main/res/layout/fragment_activity_recognition_permission.xml",
    "chars": 3532,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas."
  },
  {
    "path": "app/src/main/res/layout/fragment_forest.xml",
    "chars": 1988,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas."
  },
  {
    "path": "app/src/main/res/layout/fragment_progress.xml",
    "chars": 14631,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.core.widget.NestedScrollView xmlns:android=\"http://schemas.android.com/"
  },
  {
    "path": "app/src/main/res/layout/fragment_stats.xml",
    "chars": 683,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmln"
  },
  {
    "path": "app/src/main/res/layout/fragment_stats_chart.xml",
    "chars": 2780,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmln"
  },
  {
    "path": "app/src/main/res/layout/fragment_stats_details.xml",
    "chars": 18430,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.core.widget.NestedScrollView xmlns:android=\"http://schemas.android.com/"
  },
  {
    "path": "app/src/main/res/layout/fragment_stats_page_chart.xml",
    "chars": 900,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns"
  },
  {
    "path": "app/src/main/res/layout/fragment_stats_summary.xml",
    "chars": 19099,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.swiperefreshlayout.widget.SwipeRefreshLayout xmlns:android=\"http://sche"
  },
  {
    "path": "app/src/main/res/layout/item_chart_bar.xml",
    "chars": 1416,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmln"
  },
  {
    "path": "app/src/main/res/menu/bottom_navigation_menu.xml",
    "chars": 598,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item\n    "
  },
  {
    "path": "app/src/main/res/menu/main_menu.xml",
    "chars": 292,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"h"
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "chars": 267,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <b"
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "chars": 267,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <b"
  },
  {
    "path": "app/src/main/res/navigation/nav_graph.xml",
    "chars": 1113,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<navigation xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:"
  },
  {
    "path": "app/src/main/res/navigation/onboarding_nav_graph.xml",
    "chars": 1045,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<navigation xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:"
  },
  {
    "path": "app/src/main/res/values/colors.xml",
    "chars": 3790,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"seed\">#646106</color>\n    <color name=\"md_theme_ligh"
  },
  {
    "path": "app/src/main/res/values/ic_launcher_background.xml",
    "chars": 120,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"ic_launcher_background\">#EDEDE4</color>\n</resources>"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "chars": 3585,
    "preview": "<resources>\n    <string name=\"app_name\">Forest</string>\n    <string name=\"step_counter_channel\">Step counter</string>\n  "
  },
  {
    "path": "app/src/main/res/values/themes.xml",
    "chars": 2657,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Base application theme. -->\n    <style name=\"Base.Theme.Fore"
  },
  {
    "path": "app/src/main/res/values-night/themes.xml",
    "chars": 2392,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Base application theme. -->\n    <style name=\"Base.Theme.Fore"
  },
  {
    "path": "app/src/main/res/values-v29/themes.xml",
    "chars": 431,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <style name=\"Theme.Forest\" parent=\"Base.Theme.Forest\">\n        <"
  },
  {
    "path": "app/src/main/res/xml/backup_rules.xml",
    "chars": 478,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n   Sample backup rules file; uncomment and customize as necessary.\n   See htt"
  },
  {
    "path": "app/src/main/res/xml/data_extraction_rules.xml",
    "chars": 551,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n   Sample data extraction rules file; uncomment and customize as necessary.\n "
  },
  {
    "path": "app/src/main/res/xml/settings.xml",
    "chars": 1500,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<PreferenceScreen xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    "
  },
  {
    "path": "app/src/test/java/pl/bartek537/forest/ExampleUnitTest.kt",
    "chars": 343,
    "preview": "package pl.bartek537.forest\n\nimport org.junit.Test\n\nimport org.junit.Assert.*\n\n/**\n * Example local unit test, which wil"
  },
  {
    "path": "build.gradle",
    "chars": 164,
    "preview": "plugins {\n    alias libs.plugins.android.application apply false\n    alias libs.plugins.kotlin.android apply false\n    a"
  },
  {
    "path": "gradle/libs.versions.toml",
    "chars": 2836,
    "preview": "[versions]\nactivity = \"1.11.0\"\nagp = \"8.13.0\"\nappcompat = \"1.7.1\"\nconstraintLayout = \"2.2.1\"\ncoreKtx = \"1.17.0\"\ndesugarJ"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "chars": 253,
    "preview": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributi"
  },
  {
    "path": "gradle.properties",
    "chars": 1387,
    "preview": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will ov"
  },
  {
    "path": "gradlew",
    "chars": 8669,
    "preview": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "gradlew.bat",
    "chars": 2776,
    "preview": "@rem\n@rem Copyright 2015 the original author or authors.\n@rem\n@rem Licensed under the Apache License, Version 2.0 (the \""
  },
  {
    "path": "settings.gradle",
    "chars": 323,
    "preview": "pluginManagement {\n    repositories {\n        google()\n        mavenCentral()\n        gradlePluginPortal()\n    }\n}\ndepen"
  }
]

// ... and 1 more files (download for full content)

About this extraction

This page contains the full source code of the bk20dev/forest GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 144 files (247.3 KB), approximately 73.7k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!