Repository: igorwojda/android-showcase Branch: main Commit: a09e435e70b5 Files: 206 Total size: 1.5 MB Directory structure: gitextract_jwuhl568/ ├── .editorconfig ├── .github/ │ ├── stale.yml │ └── workflows/ │ ├── auto-approve.yml │ ├── check.yml │ ├── claude-code-review.yml │ └── claude.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── DeveloperReadme.md ├── README.md ├── app/ │ ├── build.gradle.kts │ ├── proguard-rules.pro │ └── src/ │ ├── debug/ │ │ ├── AndroidManifest.xml │ │ └── res/ │ │ └── xml/ │ │ └── network_security_config.xml │ └── main/ │ ├── AndroidManifest.xml │ ├── kotlin/ │ │ └── com/ │ │ └── igorwojda/ │ │ └── showcase/ │ │ └── app/ │ │ ├── AppKoinModule.kt │ │ ├── ShowcaseApplication.kt │ │ ├── data/ │ │ │ └── api/ │ │ │ ├── AuthenticationInterceptor.kt │ │ │ └── UserAgentInterceptor.kt │ │ └── presentation/ │ │ ├── BottomNavigationBar.kt │ │ ├── MainShowcaseActivity.kt │ │ ├── MainShowcaseScreen.kt │ │ ├── NavigationRoute.kt │ │ └── util/ │ │ └── NavigationDestinationLogger.kt │ └── res/ │ ├── drawable/ │ │ ├── ic_favorite.xml │ │ ├── ic_launcher_foreground.xml │ │ ├── ic_launcher_foreground_themed.xml │ │ ├── ic_music_library.xml │ │ └── ic_settings.xml │ ├── mipmap-anydpi/ │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ ├── mipmap-anydpi-v33/ │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ ├── values/ │ │ ├── colors.xml │ │ ├── ic_launcher_background.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── xml/ │ └── data_extraction_rules.xml ├── build-logic/ │ ├── build.gradle.kts │ ├── settings.gradle.kts │ └── src/ │ └── main/ │ └── kotlin/ │ └── com/ │ └── igorwojda/ │ └── showcase/ │ └── buildlogic/ │ ├── AboutLibrariesConventionPlugin.kt │ ├── ApplicationConventionPlugin.kt │ ├── DetektConventionPlugin.kt │ ├── EasyLauncherConventionPlugin.kt │ ├── FeatureConventionPlugin.kt │ ├── KotlinConventionPlugin.kt │ ├── LibraryConventionPlugin.kt │ ├── SpotlessConventionPlugin.kt │ ├── TestConventionLibraryPlugin.kt │ ├── TestConventionPlugin.kt │ ├── config/ │ │ └── JavaBuildConfig.kt │ └── ext/ │ ├── BuildConfigExt.kt │ ├── DependencyHandlerExt.kt │ ├── PackagingExt.kt │ └── ProjectExt.kt ├── build.gradle.kts ├── detekt.yml ├── feature/ │ ├── album/ │ │ ├── build.gradle.kts │ │ ├── proguard-rules.pro │ │ └── src/ │ │ ├── main/ │ │ │ ├── AndroidManifest.xml │ │ │ ├── kotlin/ │ │ │ │ └── com/ │ │ │ │ └── igorwojda/ │ │ │ │ └── showcase/ │ │ │ │ └── feature/ │ │ │ │ └── album/ │ │ │ │ ├── AlbumKoinModule.kt │ │ │ │ ├── data/ │ │ │ │ │ ├── DataModule.kt │ │ │ │ │ ├── datasource/ │ │ │ │ │ │ ├── api/ │ │ │ │ │ │ │ ├── model/ │ │ │ │ │ │ │ │ ├── AlbumApiModel.kt │ │ │ │ │ │ │ │ ├── AlbumListApiModel.kt │ │ │ │ │ │ │ │ ├── ImageApiModel.kt │ │ │ │ │ │ │ │ ├── ImageSizeApiModel.kt │ │ │ │ │ │ │ │ ├── SearchAlbumResultsApiModel.kt │ │ │ │ │ │ │ │ ├── TagApiModel.kt │ │ │ │ │ │ │ │ ├── TagListApiModel.kt │ │ │ │ │ │ │ │ ├── TrackApiModel.kt │ │ │ │ │ │ │ │ └── TrackListApiModel.kt │ │ │ │ │ │ │ ├── response/ │ │ │ │ │ │ │ │ ├── GetAlbumInfoResponse.kt │ │ │ │ │ │ │ │ └── SearchAlbumResponse.kt │ │ │ │ │ │ │ └── service/ │ │ │ │ │ │ │ └── AlbumRetrofitService.kt │ │ │ │ │ │ └── database/ │ │ │ │ │ │ ├── AlbumDao.kt │ │ │ │ │ │ ├── AlbumDatabase.kt │ │ │ │ │ │ └── model/ │ │ │ │ │ │ ├── AlbumRoomModel.kt │ │ │ │ │ │ ├── ImageRoomModel.kt │ │ │ │ │ │ ├── ImageSizeRoomModel.kt │ │ │ │ │ │ ├── TagRoomModel.kt │ │ │ │ │ │ └── TrackRoomModel.kt │ │ │ │ │ ├── mapper/ │ │ │ │ │ │ ├── AlbumMapper.kt │ │ │ │ │ │ ├── ImageMapper.kt │ │ │ │ │ │ ├── ImageSizeMapper.kt │ │ │ │ │ │ ├── TagMapper.kt │ │ │ │ │ │ └── TrackMapper.kt │ │ │ │ │ └── repository/ │ │ │ │ │ └── AlbumRepositoryImpl.kt │ │ │ │ ├── domain/ │ │ │ │ │ ├── DomainModule.kt │ │ │ │ │ ├── enum/ │ │ │ │ │ │ └── ImageSize.kt │ │ │ │ │ ├── model/ │ │ │ │ │ │ ├── Album.kt │ │ │ │ │ │ ├── Image.kt │ │ │ │ │ │ ├── Tag.kt │ │ │ │ │ │ └── Track.kt │ │ │ │ │ ├── repository/ │ │ │ │ │ │ └── AlbumRepository.kt │ │ │ │ │ └── usecase/ │ │ │ │ │ ├── GetAlbumListUseCase.kt │ │ │ │ │ └── GetAlbumUseCase.kt │ │ │ │ └── presentation/ │ │ │ │ ├── PresentationModule.kt │ │ │ │ ├── composable/ │ │ │ │ │ └── SearchBarComposable.kt │ │ │ │ ├── screen/ │ │ │ │ │ ├── albumdetail/ │ │ │ │ │ │ ├── AlbumDetailAction.kt │ │ │ │ │ │ ├── AlbumDetailScreen.kt │ │ │ │ │ │ ├── AlbumDetailUiState.kt │ │ │ │ │ │ └── AlbumDetailViewModel.kt │ │ │ │ │ └── albumlist/ │ │ │ │ │ ├── AlbumListAction.kt │ │ │ │ │ ├── AlbumListScreen.kt │ │ │ │ │ ├── AlbumListUiState.kt │ │ │ │ │ └── AlbumListViewModel.kt │ │ │ │ └── util/ │ │ │ │ └── TimeUtil.kt │ │ │ └── res/ │ │ │ └── values/ │ │ │ └── strings.xml │ │ └── test/ │ │ └── kotlin/ │ │ └── com/ │ │ └── igorwojda/ │ │ └── showcase/ │ │ └── feature/ │ │ └── album/ │ │ ├── data/ │ │ │ ├── DataFixtures.kt │ │ │ ├── datasource/ │ │ │ │ └── api/ │ │ │ │ └── model/ │ │ │ │ ├── AlbumApiModelTest.kt │ │ │ │ ├── ImageApiModelTest.kt │ │ │ │ └── ImageSizeApiModelTest.kt │ │ │ ├── mapper/ │ │ │ │ ├── AlbumMapperTest.kt │ │ │ │ ├── ImageMapperTest.kt │ │ │ │ ├── ImageSizeMapperTest.kt │ │ │ │ ├── TagMapperTest.kt │ │ │ │ └── TrackMapperTest.kt │ │ │ └── repository/ │ │ │ └── AlbumRepositoryImplTest.kt │ │ ├── domain/ │ │ │ ├── DomainFixtures.kt │ │ │ ├── model/ │ │ │ │ └── AlbumTest.kt │ │ │ └── usecase/ │ │ │ ├── GetAlbumListUseCaseTest.kt │ │ │ └── GetAlbumUseCaseTest.kt │ │ └── presentation/ │ │ └── screen/ │ │ ├── albumdetail/ │ │ │ └── AlbumDetailViewModelTest.kt │ │ └── albumlist/ │ │ └── AlbumListViewModelTest.kt │ ├── base/ │ │ ├── build.gradle.kts │ │ ├── consumer-rules.pro │ │ ├── proguard-rules.pro │ │ └── src/ │ │ └── main/ │ │ ├── AndroidManifest.xml │ │ ├── kotlin/ │ │ │ └── com/ │ │ │ └── igorwojda/ │ │ │ └── showcase/ │ │ │ └── feature/ │ │ │ └── base/ │ │ │ ├── common/ │ │ │ │ ├── delegate/ │ │ │ │ │ └── Observer.kt │ │ │ │ └── res/ │ │ │ │ └── Dimen.kt │ │ │ ├── data/ │ │ │ │ └── retrofit/ │ │ │ │ ├── ApiResult.kt │ │ │ │ ├── ApiResultAdapterFactory.kt │ │ │ │ ├── ApiResultCall.kt │ │ │ │ └── ApiResultCallAdapter.kt │ │ │ ├── domain/ │ │ │ │ └── result/ │ │ │ │ ├── Result.kt │ │ │ │ └── ResultExt.kt │ │ │ ├── presentation/ │ │ │ │ ├── compose/ │ │ │ │ │ └── composable/ │ │ │ │ │ ├── ErrorAnim.kt │ │ │ │ │ ├── Loading.kt │ │ │ │ │ ├── Lottie.kt │ │ │ │ │ ├── PlaceholderImage.kt │ │ │ │ │ ├── TextTitleLarge.kt │ │ │ │ │ ├── TextTitleMedium.kt │ │ │ │ │ └── UnderConstructionAnim.kt │ │ │ │ └── viewmodel/ │ │ │ │ ├── BaseAction.kt │ │ │ │ ├── BaseState.kt │ │ │ │ ├── BaseViewModel.kt │ │ │ │ └── StateTimeTravelDebugger.kt │ │ │ └── util/ │ │ │ └── TimberLogTags.kt │ │ └── res/ │ │ ├── drawable/ │ │ │ ├── ic_search.xml │ │ │ ├── image_placeholder_1.xml │ │ │ ├── image_placeholder_2.xml │ │ │ └── image_placeholder_3.xml │ │ ├── raw/ │ │ │ ├── lottie_building_screen.json │ │ │ └── lottie_error_screen.json │ │ └── values/ │ │ ├── color_palete.xml │ │ ├── ids.xml │ │ ├── strings.xml │ │ └── styles.xml │ ├── favourite/ │ │ ├── build.gradle.kts │ │ ├── proguard-rules.pro │ │ └── src/ │ │ └── main/ │ │ ├── AndroidManifest.xml │ │ └── kotlin/ │ │ └── com/ │ │ └── igorwojda/ │ │ └── showcase/ │ │ └── feature/ │ │ └── favourite/ │ │ ├── FavouriteKoinModule.kt │ │ ├── data/ │ │ │ └── DataModule.kt │ │ ├── domain/ │ │ │ └── DomainModule.kt │ │ └── presentation/ │ │ ├── PresentationModule.kt │ │ └── screen/ │ │ └── favourite/ │ │ └── FavouriteScreen.kt │ └── settings/ │ ├── build.gradle.kts │ ├── proguard-rules.pro │ └── src/ │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── kotlin/ │ │ │ └── com/ │ │ │ └── igorwojda/ │ │ │ └── showcase/ │ │ │ └── feature/ │ │ │ └── settings/ │ │ │ ├── SettingsKoinModule.kt │ │ │ ├── data/ │ │ │ │ └── DataModule.kt │ │ │ ├── domain/ │ │ │ │ └── DomainModule.kt │ │ │ └── presentation/ │ │ │ ├── PresentationModule.kt │ │ │ └── screen/ │ │ │ ├── aboutlibraries/ │ │ │ │ ├── AboutLibrariesAction.kt │ │ │ │ ├── AboutLibrariesScreen.kt │ │ │ │ ├── AboutLibrariesUiState.kt │ │ │ │ └── AboutLibrariesViewModel.kt │ │ │ └── settings/ │ │ │ ├── SettingsAction.kt │ │ │ ├── SettingsScreen.kt │ │ │ ├── SettingsUiState.kt │ │ │ └── SettingsViewModel.kt │ │ └── res/ │ │ └── values/ │ │ └── strings.xml │ └── test/ │ └── kotlin/ │ └── com/ │ └── igorwojda/ │ └── showcase/ │ └── feature/ │ └── settings/ │ └── presentation/ │ └── screen/ │ ├── aboutlibraries/ │ │ └── AboutLibrariesViewModelTest.kt │ └── settings/ │ └── SettingsViewModelTest.kt ├── gradle/ │ ├── libs.versions.toml │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── konsist-test/ │ ├── build.gradle.kts │ └── src/ │ └── test/ │ └── kotlin/ │ └── com/ │ └── igorwojda/ │ └── showcase/ │ └── konsisttest/ │ ├── AndroidKonsistTest.kt │ ├── CleanArchitectureKonsistTest.kt │ ├── GeneralKonsistTest.kt │ ├── ModuleKonsistTest.kt │ ├── TestKonsistTest.kt │ ├── UseCaseKonsistTest.kt │ └── ViewModelKonsistTest.kt ├── library/ │ └── test-utils/ │ ├── build.gradle.kts │ ├── proguard-rules.pro │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ └── kotlin/ │ └── com/ │ └── igorwojda/ │ └── showcase/ │ └── library/ │ └── testutils/ │ ├── CoroutinesTestDispatcherExtension.kt │ └── InstantTaskExecutorExtension.kt ├── renovate.json └── settings.gradle.kts ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ root = true [*.{kt,kts}] end_of_line = lf insert_final_newline = true max_line_length = 140 ktlint_function_naming_ignore_when_annotated_with=Composable # Detekt orders imports correctly, while ktlint does not ktlint_standard_import-ordering = disabled ================================================ FILE: .github/stale.yml ================================================ # Number of days of inactivity before an issue becomes stale daysUntilStale: 60 # Number of days of inactivity before a stale issue is closed daysUntilClose: 7 # Issues with these labels will never be considered stale exemptLabels: - pinned - security - enhancement # Label to use when marking an issue as stale staleLabel: wontfix # Comment to post when marking an issue as stale. Set to `false` to disable markComment: > This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. # Comment to post when closing a stale issue. Set to `false` to disable closeComment: true ================================================ FILE: .github/workflows/auto-approve.yml ================================================ name: Auto Approve on: pull_request_target jobs: auto-approve: runs-on: ubuntu-latest permissions: pull-requests: write if: | github.event.pull_request.head.repo.full_name == github.repository && github.actor == github.event.pull_request.user.login && contains(fromJson('["renovate[bot]", "igorwojda"]'), github.actor) steps: - uses: hmarr/auto-approve-action@v4 ================================================ FILE: .github/workflows/check.yml ================================================ name: Check on: push: branches: [ main ] # Just in case main was not up to date while merging PR pull_request: types: [ opened, synchronize ] # Cancel previous runs on new push concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: build-debug: runs-on: ubuntu-latest steps: - name: checkout uses: actions/checkout@v5 - name: Set up JDK uses: actions/setup-java@v5 with: java-version: 17 distribution: 'zulu' - name: Setup Gradle uses: gradle/actions/setup-gradle@v5 - name: Build App run: ./gradlew :app:assembleDebug --no-build-cache - uses: actions/upload-artifact@v5 with: name: app-debug path: app/build/outputs/apk/debug/app-debug.apk android-lint: runs-on: ubuntu-latest steps: - name: checkout uses: actions/checkout@v5 - name: Set up JDK uses: actions/setup-java@v5 with: java-version: 17 distribution: 'zulu' - name: Setup Gradle uses: gradle/actions/setup-gradle@v5 - name: lintDebug run: ./gradlew lint - uses: actions/upload-artifact@v5 if: always() with: name: android-lint-report path: | app/build/reports/lint-results*.html feature/*/build/reports/lint-results*.html library/*/build/reports/lint-results*.html detekt: runs-on: ubuntu-latest steps: - name: checkout uses: actions/checkout@v5 - name: Set up JDK uses: actions/setup-java@v5 with: java-version: 17 distribution: 'zulu' - name: Setup Gradle uses: gradle/actions/setup-gradle@v5 - name: detekt run: ./gradlew detektCheck - uses: actions/upload-artifact@v5 if: always() with: name: detekt-report path: | build/reports/detekt/detekt.* app/build/reports/detekt/detekt.* feature/*/build/reports/detekt/detekt.* library/*/build/reports/detekt/detekt.* konsist-test/build/reports/detekt/detekt.* konsist: runs-on: ubuntu-latest steps: - name: checkout uses: actions/checkout@v5 - name: Set up JDK uses: actions/setup-java@v5 with: java-version: 17 distribution: 'zulu' - name: Setup Gradle uses: gradle/actions/setup-gradle@v5 - name: konsist run: ./gradlew konsist-test:test --rerun-tasks - uses: actions/upload-artifact@v5 if: always() with: name: konsist-report path: ./konsist-test/build/reports/* spotless: runs-on: ubuntu-latest steps: - name: checkout uses: actions/checkout@v5 - name: Set up JDK uses: actions/setup-java@v5 with: java-version: 17 distribution: 'zulu' - name: Setup Gradle uses: gradle/actions/setup-gradle@v5 - name: spotlessCheck run: ./gradlew spotlessCheck - uses: actions/upload-artifact@v5 if: always() with: name: spotless-report path: | build/reports/spotless/* app/build/reports/spotless/* feature/*/build/reports/spotless/* library/*/build/reports/spotless/* konsist-test/build/reports/spotless/* # ui-test: # runs-on: macos-latest # steps: # - name: checkout # uses: actions/checkout@v4 # # - name: Set up JDK # uses: actions/setup-java@v4 # with: # java-version: 17 # distribution: 'zulu' # # - name: Setup Gradle # uses: gradle/actions/setup-gradle@v4 # # - name: run ui tests # uses: reactivecircus/android-emulator-runner@v2 # with: # api-level: 29 # target: default # arch: x86 # profile: Nexus 6 # disable-animations: true # script: ./gradlew connectedCheck # # - uses: actions/upload-artifact@v4 # with: # name: ui-test-report # path: ./**/build/reports/androidTests/ unit-test: runs-on: ubuntu-latest steps: - name: checkout uses: actions/checkout@v5 - name: Set up JDK uses: actions/setup-java@v5 with: java-version: 17 distribution: 'zulu' - name: Setup Gradle uses: gradle/actions/setup-gradle@v5 - name: unitTest run: ./gradlew test -x konsist-test:test - uses: actions/upload-artifact@v5 if: always() with: name: unit-test-report path: | app/build/reports/tests/ feature/*/build/reports/tests/ library/*/build/reports/tests/ ================================================ FILE: .github/workflows/claude-code-review.yml ================================================ name: Claude Code Review on: pull_request: types: [opened, synchronize] # Optional: Only run on specific file changes # paths: # - "src/**/*.ts" # - "src/**/*.tsx" # - "src/**/*.js" # - "src/**/*.jsx" jobs: claude-review: # Optional: Filter by PR author # if: | # github.event.pull_request.user.login == 'external-contributor' || # github.event.pull_request.user.login == 'new-developer' || # github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR' runs-on: ubuntu-latest permissions: contents: read pull-requests: read issues: read id-token: write steps: - name: Checkout repository uses: actions/checkout@v5 with: fetch-depth: 1 - name: Run Claude Code Review id: claude-review uses: anthropics/claude-code-action@v1 with: claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} prompt: | Please review this pull request and provide feedback on: - Code quality and best practices - Potential bugs or issues - Performance considerations - Security concerns - Test coverage Use the repository's CLAUDE.md for guidance on style and conventions. Be constructive and helpful in your feedback. Use `gh pr comment` with your Bash tool to leave your review as a comment on the PR. # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md # or https://docs.anthropic.com/en/docs/claude-code/sdk#command-line for available options claude_args: '--allowed-tools "Bash(./gradlew *),Bash(./gradlew konsist-test:*),Bash(./gradlew library:test-utils:*),Bash(gh issue view:*),Bash(gh search:*),Bash(gh issue list:*),Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh pr list:*)"' ================================================ FILE: .github/workflows/claude.yml ================================================ name: Claude Code on: issue_comment: types: [created] pull_request_review_comment: types: [created] issues: types: [opened, assigned] pull_request_review: types: [submitted] jobs: claude: if: | (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) || (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) || (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) || (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude'))) runs-on: ubuntu-latest permissions: contents: read pull-requests: read issues: read id-token: write actions: read # Required for Claude to read CI results on PRs steps: - name: Checkout repository uses: actions/checkout@v5 with: fetch-depth: 1 - name: Run Claude Code id: claude uses: anthropics/claude-code-action@v1 with: claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} # This is an optional setting that allows Claude to read CI results on PRs additional_permissions: | actions: read # Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it. # prompt: 'Update the pull request description to include a summary of changes.' # Optional: Add claude_args to customize behavior and configuration # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md # or https://docs.anthropic.com/en/docs/claude-code/sdk#command-line for available options # claude_args: '--model claude-opus-4-1-20250805 --allowed-tools Bash(gh pr:*)' ================================================ FILE: .gitignore ================================================ # Built application files *.apk *.ap_ *.aab # Files for the ART/Dalvik VM *.dex # Java class files *.class # Generated files bin/ gen/ out/ # Gradle files .gradle/ # Build folders **/build/ # Local configuration file (sdk path, etc) local.properties # Proguard folder generated by Eclipse proguard/ # Log Files *.log # Android Studio Navigation editor temp files .navigation/ # Android Studio captures folder captures/ # IntelliJ *.iml .idea/ # Google Services (e.g. APIs or Firebase) google-services.json # Cache of project .gradletasknamecache #MacOS DS_Store **/.DS_Store # Kotlin *.salive # Claude Code .claude/ ================================================ FILE: CODE_OF_CONDUCT.md ================================================ # Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at {{ email }}. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html [homepage]: https://www.contributor-covenant.org ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing We appreciate contributions of any kind - new contributions are welcome whether it's through bug reports or new pull requests. ## Tell us about enhancements and bugs Please add an issue. We'll review it, add labels and reply when we get the chance. ## See an issue you'd like to work on Comment on the issue that you'd like to work on and we'll add the `claimed` label. If you see the `claimed` label already on the issue you might want to ask the contributor if they'd like some help. ## Documentation needs updating Go right ahead! Just submit a pull request when you're done. ## Pull Requests We love pull requests from everyone: 1. [Fork](https://docs.github.com/en/get-started/quickstart/fork-a-repo) this repository: 2. Clone forked repository `git clone git@github.com:YOUR-USERNAME/android-showcase.git` 3. Branch of the `main` branch. 4. Make changes, push changes to your fork and [submit a pull request](https://github.com/igorwojda/android-showcase/compare) against the `main` branch. At this point you're waiting on us. We like to at least comment on pull requests within few days. We may suggest some changes or improvements or alternatives. Some things that will increase the chance that your pull request is accepted: 1. Write a [good commit message](https://chris.beams.io/posts/git-commit/) 2. Make sure all tests and lint checks are passing (review them on the pull request page) 3. Update [README](README.md) with any changes are needed 4. Write tests (if needed) ================================================ FILE: DeveloperReadme.md ================================================ # Developer Readme ## Detekt - [Detekt configuration](https://detekt.dev/docs/introduction/configurations/) contains link to `default-detekt-config.yml`. ## Known Issues - AboutLibraries - AboutLibraries `12.2.4` Gradle plugin does nto include test dependencies https://github.com/mikepenz/AboutLibraries/issues/1238 - AboutLibraries `13.0.0-rc01` Gradle plugin required Kotlin 2.2.0 https://github.com/mikepenz/AboutLibraries/issues/1237 - Gradle - Gradle `9.0` - Generated type-safe version catalogs accessors for `projcts` are not avialable inside `build-logic` module - Gradle `9.0` - Generated type-safe version catalogs accessors for `libs` are not accessible from precompiled script plugin e.g. add("implementation", libs.koin). Workaround is to use `implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location))`. - Mockk - Unable to mock some methods with implicit `continuation` parameter in the `AlbumListViewModelTest` class ([Issue-957](https://github.com/mockk/mockk/issues/957)) - Detekt - The `UnnecessaryParentheses` rule was disabled https://github.com/detekt/detekt/issues/8668 - Kotlin Plugin - Auto-import (an import intention) for delegate does not work if the variable has the same name https://youtrack.jetbrains.com/issue/KTIJ-17403 - Android Studio - False positive "Unused symbol" for a custom Android application class referenced in `AndroidManifest.xml` file ([KT-27971](https://youtrack.jetbrains.net/issue/KT-27971)) - Coil - No way to automatically retry image load, so some images may not be loaded when connection speed is low ([Issue 132](https://github.com/coil-kt/coil/issues/132)) ================================================ FILE: README.md ================================================ # 💎 Android Showcase 2.0 [![Kotlin Version](https://img.shields.io/badge/Kotlin-2.x-blue.svg)](https://kotlinlang.org) [![AGP](https://img.shields.io/badge/AGP-8.x-blue?style=flat)](https://developer.android.com/studio/releases/gradle-plugin) [![Gradle](https://img.shields.io/badge/Gradle-9.x-blue?style=flat)](https://gradle.org) [![CodeFactor](https://www.codefactor.io/repository/github/igorwojda/android-showcase/badge)](https://www.codefactor.io/repository/github/igorwojda/android-showcase) A production-ready Android application demonstrating modern development practices and architectural patterns. This project showcases how to build scalable, maintainable, and testable Android applications using industry-standard tools and libraries. Built with **Clean Architecture** principles, this app serves as a comprehensive example of modular design, advanced Gradle configuration, and robust CI/CD practices. Perfect for teams looking to establish solid architectural foundations for large-scale Android projects. - [💎 Android Showcase 2.0](#-android-showcase-20) - [Application Scope](#application-scope) - [Tech-Stack](#tech-stack) - [Architecture](#architecture) - [Module Types and Dependencies](#module-types-and-dependencies) - [Feature Module Structure](#feature-module-structure) - [Presentation Layer](#presentation-layer) - [Domain Layer](#domain-layer) - [Data Layer](#data-layer) - [Common Module Components](#common-module-components) - [Data Flow](#data-flow) - [Project Features](#project-features) - [Development \& Debugging](#development--debugging) - [Custom Icons For Each Variant](#custom-icons-for-each-variant) - [Themed Icons](#themed-icons) - [Gradle Config](#gradle-config) - [Dependency Management](#dependency-management) - [Convention Plugins](#convention-plugins) - [Type Safe Project Accessors](#type-safe-project-accessors) - [Unified Version Configuration](#unified-version-configuration) - [Java/JVM Version Configuration](#javajvm-version-configuration) - [Generated type-safe version catalogs accessors in `build-logic` module](#generated-type-safe-version-catalogs-accessors-in-build-logic-module) - [Gradle Configuration Cache](#gradle-configuration-cache) - [Code Verification](#code-verification) - [CI Pipeline](#ci-pipeline) - [Pre-push Hooks](#pre-push-hooks) - [Project Scope \& Limitations](#project-scope--limitations) - [Getting Started](#getting-started) - [Roadmap](#roadmap) - [Resources](#resources) - [Contributing](#contributing) - [Author](#author) - [License](#license) - [Animations License](#animations-license) ## Application Scope A music discovery app built with Jetpack Compose that displays album information sourced from the [Last.fm API](https://www.last.fm/api). The application demonstrates real-world scenarios including network requests, local caching, navigation, and state management. **Features:** - **Album List** - Browse albums with search functionality - **Album Details** - View detailed album information and track listings - **Favorites** - Save preferred albums (WIP) - **Profile** - User preferences and settings (WIP)

## Tech-Stack Built with modern Android development tools and libraries, prioritizing, project structure stability and production-readiness. **Core Technologies:** - **[Kotlin 2.2+](https://kotlinlang.org/)** - Modern, expressive programming language - **[Coroutines](https://kotlinlang.org/docs/coroutines-overview.html)** - Asynchronous programming - **[Flow](https://kotlinlang.org/docs/flow.html)** - Reactive data streams - **[KSP (Kotlin Symbol Processing)](https://kotlinlang.org/docs/ksp-overview.html)** - Kotlin Symbol Processing - **[Serialization](https://kotlinlang.org/docs/serialization.html)** - JSON parsing **Android Jetpack:** - **[Compose](https://developer.android.com/jetpack/compose)** - Declarative UI framework - **[Navigation Compose](https://developer.android.com/jetpack/compose/navigation)** - Type-safe navigation - **[ViewModel](https://developer.android.com/topic/libraries/architecture/viewmodel)** - UI-related data management - **[Room](https://developer.android.com/jetpack/androidx/releases/room)** - Local database with SQLite - **[Core Splashscreen](https://developer.android.com/jetpack/androidx/releases/core#core_splashscreen_version_12_2)** - app Splashscreen **Networking & Images:** - **[Retrofit](https://square.github.io/retrofit/)** - HTTP client for API communication - **[Coil](https://github.com/coil-kt/coil)** - Image loading optimized for Compose **Dependency Injection:** - **[Koin](https://insert-koin.io/)** - Lightweight dependency injection framework **Architecture:** - **[Clean Architecture](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)** - Separation of concerns with defined layers - **Single Activity Architecture** - Modern navigation approach - **MVVM + MVI** - Reactive presentation layer pattern providing common UI state. - **Modular Design** - Feature-based modules for scalability **UI & Design:** - **[Material Design 3](https://m3.material.io/)** - Latest design system - **[Dynamic Theming](https://m3.material.io/styles/color/dynamic-color/overview)** - Wallpaper-based themes (Android 12+) - **[Dark Theme](https://material.io/develop/android/theming/dark)** - System-aware dark mode - **[Lottie](http://airbnb.io/lottie)** - Vector animations **Testing:** - **[JUnit 6](https://junit.org/)** - Modern testing framework - **[Mockk](https://mockk.io/)** - Kotlin-first mocking library - **[Kluent](https://github.com/MarkusAmshove/Kluent)** - Fluent assertion library - **[Espresso](https://developer.android.com/training/testing/espresso)** - UI testing (WIP) **Code Quality:** - **[Konsist](https://docs.konsist.lemonappdev.com/)** - Architecture and code structure convention tests - **[Ktlint](https://github.com/pinterest/ktlint)** - Kotlin code formatting and issue detection - **[Ktlint Standard Rules](https://pinterest.github.io/ktlint/0.49.1/rules/standard/)** - set of custom rules for Jetpack Compose - **[Nlopez Jetpack Compose Rules](https://mrmans0n.github.io/compose-rules/)** - set of custom rules for Jetpack Compose - **[Twitter's Jetpack Compose Rules](https://twitter.github.io/compose-rules/)** - set of custom rules for Jetpack Compose - **[Detekt](https://github.com/arturbosch/detekt)** - Static analysis and complexity checks - **[Android Lint](http://tools.android.com/tips/lint)** - Android-specific code analysis - **[Spotless](https://github.com/diffplug/spotless)** - Code formatting enforcement **Build & CI:** - **[Gradle Kotlin DSL](https://docs.gradle.org/current/userguide/kotlin_dsl.html)** - Type-safe build scripts - **[Version Catalogs](https://docs.gradle.org/current/userguide/platforms.html#sub:version-catalog)** - Centralized dependency management - **[Convention Plugins](https://docs.gradle.org/current/samples/sample_convention_plugins.html)** - Shared build logic - **[Renovate](https://github.com/renovatebot/renovate)** - Automated dependency updates **GitHub Actions:** - **[Check](.github/workflows/check.yml)** - CI pipeline with build, lint, test, and code quality checks - **[Auto Approve](.github/workflows/auto-approve.yml)** - Auto-approval for trusted bot and maintainer PRs - **[Claude Code](.github/workflows/claude.yml)** - AI-powered code assistance and review - **[Claude Code Review](.github/workflows/claude-code-review.yml)** - Automated PR reviews using Claude **Gradle Plugins:** - **[Android Application](https://developer.android.com/build/releases/gradle-plugin)** (`com.android.application`) - Android app module configuration - **[Android Library](https://developer.android.com/build/releases/gradle-plugin)** (`com.android.library`) - Android library module configuration - **[Kotlin Android](https://kotlinlang.org/docs/gradle.html)** (`org.jetbrains.kotlin.android`) - Kotlin compilation for Android - **[Kotlin Serialization](https://kotlinlang.org/docs/serialization.html)** (`org.jetbrains.kotlin.plugin.serialization`) - JSON serialization support - **[Kotlin Compose Compiler](https://developer.android.com/jetpack/androidx/releases/compose-kotlin)** (`org.jetbrains.kotlin.plugin.compose`) - Compose compiler plugin - **[KSP](https://kotlinlang.org/docs/ksp-overview.html)** - Kotlin Symbol Processing - **[Detekt](https://detekt.dev/)** - Static code analysis - **[Spotless](https://github.com/diffplug/spotless)** - Code formatting - **[Test Logger](https://github.com/radarsh/gradle-test-logger-plugin)** - Enhanced test log output - **[Easylauncher](https://github.com/usefulness/easylauncher-gradle-plugin)** - Modify the launcher icon of each of your app-variants - **[AboutLibraries](https://github.com/mikepenz/AboutLibraries)** - collects dependency details, including licenses and visualize these in the app ## Architecture The project implements **Clean Architecture** with a modular approach, treating each feature as an independent, reusable component similar to a microservice. This design enables maintainability and scalability for large development teams. **Benefits of Modular Architecture:** - **Reusability** - Shared code across multiple app variants - **Separation of Concerns** - Clear module boundaries with explicit dependencies - **Parallel Development** - Teams can work on features independently - **Faster Build Times** - Incremental compilation and build caching - **Testability** - Isolated testing of individual components ### Module Types and Dependencies ![Module Dependencies](./misc/image/module_dependencies.png) **Module Types:** - **`app`** - Main application module containing navigation setup, DI configuration, and app-level components - **`feature-*`** - Feature modules (album, profile, favourite) containing feature-specific business logic - **`feature-base`** - Shared foundation module providing common utilities and base classes - **`library-*`** - Utility modules for testing and shared functionality ### Feature Module Structure `Clean Architecture` is implemented at the module level - each module contains its own set of Clean Architecture layers: ![module_dependencies_layers](./misc/image/module_layers.png) > Notice that the `app` module and `library_x` modules structure differs a bit from the feature module structure. Each feature module contains 3 layers with a distinct set of responsibilities and common module components. ![feature_structure](./misc/image/module_layers_details.png) #### Presentation Layer This layer is closest to what the user sees on the screen. The `presentation` layer mixes `MVVM` and `MVI` patterns: - `MVVM` - Jetpack `ViewModel` is used to encapsulate a `common UI state`. It exposes the `state` via observable state holder (`Kotlin Flow`) - `MVI` - `action` modifies the `common UI state` and emits a new state to a view via `Kotlin Flow` > The `common state` is a single source of truth for each view. This solution derives from > [Unidirectional Data Flow](https://en.wikipedia.org/wiki/Unidirectional_Data_Flow_(computer_science)) and [Redux > principles](https://redux.js.org/introduction/three-principles). This approach facilitates the creation of consistent states. The state is collected via `collectAsUiStateWithLifecycle` method. Flows collection happens in a lifecycle-aware manner, so [no resources are wasted](https://medium.com/androiddevelopers/consuming-flows-safely-in-jetpack-compose-cde014d0d5a3). Stated is annotated with [Immutable](https://developer.android.com/reference/kotlin/androidx/compose/runtime/Immutable) annotation that is used by Jetpack compose to enable composition optimizations. Components: - **Screen (Composable)** - observes common view state (through `Kotlin Flow`). Compose transform state (emitted by Kotlin Flow) into application UI Consumes the state and transforms it into application UI (via `Jetpack Compose`). Pass user interactions to `ViewModel`. Views are hard to test, so they should be as simple as possible. - **ViewModel** - emits (through `Kotlin Flow`) view state changes to the view and deals with user interactions (these view models are not simply [POJO classes](https://en.wikipedia.org/wiki/Plain_old_Java_object)). - **ViewState** - common state for a single view - **StateTimeTravelDebugger** - logs actions and view state transitions to facilitate debugging. - **NavManager** - singleton that facilitates handling all navigation events inside `NavHostActivity` (instead of separately, inside each view) #### Domain Layer This is the core layer of the application. Notice that the `domain` layer is independent of any other layers. This allows making domain models and business logic independent from other layers. In other words, changes in other layers will not affect the `domain` layer eg. changing the database (`data` layer) or screen UI (`presentation` layer) ideally will not result in any code change within the `domain` layer. Components: - **UseCase** - contains business logic - **DomainModel** - defines the core structure of the data that will be used within the application. This is the source of truth for application data. - **Repository interface** - required to keep the `domain` layer independent from the `data layer` ([Dependency inversion](https://en.wikipedia.org/wiki/Dependency_inversion_principle)). #### Data Layer Encapsulates application data. Provides the data to the `domain` layer eg. retrieves data from the internet and cache the data in disk cache (when the device is offline). Components: - **Repository** is exposing data to the `domain` layer. Depending on the application structure and quality of the external API repository can also merge, filter, and transform the data. These operations intend to create a high-quality data source for the `domain` layer. It is the responsibility of the Repository (one or more) to construct Domain models by reading from the `Data Source` and accepting Domain models to be written to the `Data Source` - **Mapper** - maps `data model` to `domain model` (to keep `domain` layer independent from the `data` layer). This application has two `Data Sources` - `Retrofit` (used for network access) and `Room` (local storage used to access device persistent memory). These data sources can be treated as an implicit sub-layer. Each data source consists of multiple classes: - **Retrofit Service** - defines a set of API endpoints - **Retrofit Response Model** - definition of the network objects for a given endpoint (top-level model for the data consists of `ApiModels`) - **Retrofit Api Data Model** - defines the network objects (sub-objects of the `Response Model`) - **Room Database** - persistence database to store app data - **Room DAO** - interact with the stored data - **Room Entity** - definition of the stored objects Both `Retrofit API Data Models` and `Room Entities` contain annotations, so the given framework understands how to parse the data into objects. #### Common Module Components Each module in the Android project contains several standard items that provide essential functionality and configuration: Components: - **Gradle Build Script** - `build.gradle.kts` defining dependencies, build configurations, and plugins. - **Koin DI Module** - Dependency injection configuration - **Tests** - Unit tests (`test/`) and integration tests (`androidTest/`) - **Android Resources** - resources (`res/`) including strings, drawables, and assets. - **Android Manifest** - The `AndroidManifest.xml` file declaring module metadata. ### Data Flow The below diagram presents application data flow when a user interacts with the `album list screen`: ![app_data_flow](./misc/image/app_data_flow.png) ## Project Features ### Development & Debugging Tags ([LogTags](feature/base/src/main/kotlin/com/igorwojda/showcase/feature/base/util/LogTags.kt)) help filter and identify different types of logs during development and debugging. The app provides detailed logging for development and debugging, with each log easily filterable by its tag: - `Navigation` - Navigation events and route changes ![Navigation Logs](misc/image/logs_navigation.png) - `Action` - User actions and UI state modifications ![Action Logs](misc/image/logs_action.png) - `Network` - Network requests, responses, and HTTP-related logs ![Network Logs](misc/image/logs_network.png) ### Custom Icons For Each Variant Thanks to [Easylauncher Gradle plugin](https://github.com/usefulness/easylauncher-gradle-plugin) the `debug` build has custom icon label: application_icon_label ### Themed Icons App supports [Themed Icons](https://medium.com/@enikebraimoh/android-themed-icons-a-comprehensive-guide-3abb33ab51a7). Left (classic icon), Right (themed icon): application_icon_label ## Gradle Config ### Dependency Management Gradle [versions catalog](https://docs.gradle.org/current/userguide/platforms.html#sub:version-catalog) is used as a centralized dependency management third-party dependency coordinates (group, artifact, version) are shared across all modules (Gradle projects and subprojects). Gradle versions catalog consists of a few major sections: - `[versions]` - declare versions that can be referenced by all dependencies - `[libraries]` - declare the aliases to library coordinates - `[bundles]` - declare dependency bundles (groups) - `[plugins]` - declare Gradle plugin dependencies Each module uses convention a plugin, so common dependencies are shared without the need to add them explicitly in each module. ### Convention Plugins [Convention plugins](https://docs.gradle.org/current/samples/sample_convention_plugins.html) standardize build configuration across modules by encapsulating common build logic into reusable plugins: - **[`Application Convention`](./build-logic/src/main/kotlin/com/igorwojda/showcase/buildlogic/ApplicationConventionPlugin.kt)** - Main application module configuration with Android app setup - **[`Feature Convention`](./build-logic/src/main/kotlin/com/igorwojda/showcase/buildlogic/FeatureConventionPlugin.kt)** - Feature module configuration combining library and Kotlin conventions - **[`Library Convention`](./build-logic/src/main/kotlin/com/igorwojda/showcase/buildlogic/LibraryConventionPlugin.kt)** - Android library module setup with common Android configuration - **[`Lotlin Convention`](./build-logic/src/main/kotlin/com/igorwojda/showcase/buildlogic/KotlinConventionPlugin.kt)** - Kotlin compilation settings, toolchain, and compiler options - **[`Test Convention`](./build-logic/src/main/kotlin/com/igorwojda/showcase/buildlogic/TestConventionPlugin.kt)** - Testing framework setup (JUnit, test logging, and test configurations) - **[`Test Library Convention`](./build-logic/src/main/kotlin/com/igorwojda/showcase/buildlogic/TestConventionLibraryPlugin.kt)** - Testing setup specifically for library modules - **[`Detekt Convention`](./build-logic/src/main/kotlin/com/igorwojda/showcase/buildlogic/DetektConventionPlugin.kt)** - Static code analysis configuration with Detekt - **[`Spotless Convention`](./build-logic/src/main/kotlin/com/igorwojda/showcase/buildlogic/SpotlessConventionPlugin.kt)** - Code formatting and style enforcement with Spotless - **[`Easylauncher Convention`](./build-logic/src/main/kotlin/com/igorwojda/showcase/buildlogic/EasyLauncherConventionPlugin.kt)** - App icon customization for different build variants - **[`AboutLibraries Convention`](./build-logic/src/main/kotlin/com/igorwojda/showcase/buildlogic/AboutLibrariesConventionPlugin.kt)** - About libraries configuration ### Type Safe Project Accessors Enables type-safe project references instead of error-prone string-based module paths: ```kotlin // Before implementation(project(":feature:album")) // After implementation(projects.feature.album) ``` ### Unified Version Configuration All dependency and Gradle plugin versions are defined in the TOML version catalog file ([libs.versions.toml](gradle/libs.versions.toml)). #### Java/JVM Version Configuration The Java/JVM version is centralized across the project. It is defined once in [`libs.versions.toml`](gradle/libs.versions.toml) file under the java entry. The `generateJavaBuildConfig` task reads this value and generates a `JavaBuildConfig.kt` file with constants. These constants are then used in Gradle convention plugins to configure both Java and Kotlin consistently: ```kotlin compileOptions { sourceCompatibility = JavaBuildConfig.JAVA_VERSION targetCompatibility = JavaBuildConfig.JAVA_VERSION } kotlin { compilerOptions { jvmTarget = JavaBuildConfig.jvmTarget } jvmToolchain(JavaBuildConfig.jvmToolchainVersion) } ``` ### Generated type-safe version catalogs accessors in `build-logic` module The `build-logic` module provides type-safe access to version catalogs from within precompiled script plugins. This is enabled via the `versionCatalogs` block in `build-logic/settings.gradle.kts`, which references the main version catalog file and `implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location))` dependency in `build-logic/build.gradle.kts` file. This setup allows you to use catalog dependencies in plugins, for example: ```kotlin add("implementation", libs.timber) ``` Additionally, extensions defined in [DependencyHandlerScope](build-logic/src/main/kotlin/com/igorwojda/showcase/buildlogic/ext/DependencyHandlerExt.kt) make the syntax more natural and equivalent to standard Gradle usage: ```kotlin implementation(libs.timber) ``` ### Gradle Configuration Cache Enabled [Gradle Configuration Cache](https://docs.gradle.org/9.0.0/userguide/configuration_cache_enabling.html). ## Code Verification **Quality Checks:** ```bash ./gradlew konsist-test:test --rerun-tasks # Architecture & convention validation ./gradlew lintDebug # Android lint analysis ./gradlew detektCheck # Code complexity & style analysis ./gradlew spotlessCheck # Code formatting verification ./gradlew testDebugUnitTest -x konsist-test:test # Unit test execution (without Konsist tests) ./gradlew connectedCheck # UI test execution (WIP) ./gradlew :app:bundleDebug # Production build verification ``` **Auto-fix Commands:** ```bash ./gradlew detektApply # Apply Detekt formatting fixes ./gradlew spotlessApply # Apply code formatting fixes ./gradlew lintDebug # Update lint baseline ``` ### CI Pipeline [GitHub Actions](https://github.com/features/actions) workflows execute quality checks automatically: - **PR Validation** - All checks run in parallel on pull requests - **Main Branch Protection** - Post-merge validation ensures code quality - **Automated Dependency Updates** - Renovate bot creates PRs for dependency updates Configuration: [`.github/workflows`](.github/workflows) ### Pre-push Hooks Optional [Git hooks](https://git-scm.com/docs/githooks#_pre_push) can execute quality checks before pushing code, providing fast feedback during development. ## Project Scope & Limitations This showcase prioritizes **architecture, tooling, and development practices** over complex UI design. The interface uses Material Design 3 components but remains intentionally straightforward to focus on the underlying technical implementation. ## Getting Started **Prerequisites:** - Android Studio Giraffe | 2022.3.1+ - JDK 17+ - Android SDK 34+ **Setup:** ```bash # Clone the repository git clone https://github.com/igorwojda/android-showcase.git # Open in Android Studio # File -> Open -> Select cloned directory ``` **Recommended IDE Plugins:** - [Detekt](https://plugins.jetbrains.com/plugin/10761-detekt) - Configure with [detekt.yml](detekt.yml) - [Kotlin](https://plugins.jetbrains.com/plugin/6954-kotlin) - Usually pre-installed - [Android](https://developer.android.com/studio) - Usually pre-installed ## Roadmap Active development continues with focus on modern Android practices. View planned [enhancements](https://github.com/igorwojda/android-showcase/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3Aenhancement) and contribute ideas. ## Resources **Development Tools:** - [Material Theme Builder](https://m3.material.io/theme-builder#/dynamic) - Generate Material 3 dynamic themes - [Compose Material 3 Components](https://developer.android.com/reference/kotlin/androidx/compose/material3/package-summary) - Component reference - [Android Ecosystem Cheat Sheet](https://github.com/igorwojda/android-ecosystem-cheat-sheet) - 200+ essential Android tools - [Kotlin Coroutines Use Cases](https://github.com/LukasLechnerDev/Kotlin-Coroutine-Use-Cases-on-Android) - Practical coroutine examples **Recommended Projects:** - [Now in Android](https://github.com/android/nowinandroid) - Google's official modern Android showcase - [Android Architecture Blueprints](https://github.com/googlesamples/android-architecture) - Architecture pattern examples - [Compose Samples](https://github.com/android/compose-samples) - Official Jetpack Compose examples - [Kotlin Android Template](https://github.com/cortinico/kotlin-android-template) - Pre-configured project template - [Androidify](https://github.com/android/androidify) - Android's official character customization app - [WeatherXM Android](https://github.com/WeatherXM/wxm-android) - Weather data collection and rewards platform - [Songify](https://github.com/JamesBuhanan/Songify) - Spotify-inspired music streaming app - [Alkaa](https://github.com/igorescodro/alkaa) - Task management app with modern architecture - [KotlinConf App](https://github.com/JetBrains/kotlinconf-app) - JetBrains' official conference app - [Tivi](https://github.com/chrisbanes/tivi) - TV show tracking app by Chris Banes - [CatchUp](https://github.com/ZacSweers/CatchUp) - News aggregation app with modular architecture - [Heron](https://github.com/tunjid/heron) - Social media client showcasing modern Android development ## Contributing Contributions are welcome! Please check the [CONTRIBUTING.md](CONTRIBUTING.md) guidelines before submitting PRs. **Areas for Contribution:** - Feature implementations (Profile, Favorites screens) - UI/UX improvements and animations - Performance optimizations - Testing coverage expansion - Documentation improvements ## Author **Igor Wojda** - Senior Android Engineer [![Twitter Follow](https://img.shields.io/twitter/follow/igorwojda?style=social)](https://twitter.com/igorwojda) [![GitHub](https://img.shields.io/github/followers/igorwojda?style=social)](https://github.com/igorwojda) ## License ``` MIT License Copyright (c) 2025 Igor Wojda Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ``` ## Animations License Flowing animations are distributed under `Creative Commons License 2.0`: - [Error screen](https://lottiefiles.com/8049-error-screen) by Chetan Potnuru - [Building Screen](https://lottiefiles.com/1271-building-screen) by Carolina Cajazeira ================================================ FILE: app/build.gradle.kts ================================================ import com.igorwojda.showcase.buildlogic.ext.buildConfigFieldFromGradleProperty plugins { id("com.igorwojda.showcase.convention.application") } android { namespace = "com.igorwojda.showcase.app" defaultConfig { applicationId = "com.igorwojda.showcase" versionCode = 1 versionName = "0.0.1" // SemVer (Major.Minor.Patch) buildConfigFieldFromGradleProperty(project, "apiBaseUrl") buildConfigFieldFromGradleProperty(project, "apiToken") } buildTypes { getByName("release") { isMinifyEnabled = false proguardFiles("proguard-android.txt", "proguard-rules.pro") } } } dependencies { // "projects." Syntax utilizes Gradle TYPESAFE_PROJECT_ACCESSORS feature implementation(projects.feature.base) implementation(projects.feature.album) implementation(projects.feature.settings) implementation(projects.feature.favourite) } ================================================ 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/debug/AndroidManifest.xml ================================================ ================================================ FILE: app/src/debug/res/xml/network_security_config.xml ================================================ ws.audioscrobbler.com ================================================ FILE: app/src/main/AndroidManifest.xml ================================================ ================================================ FILE: app/src/main/kotlin/com/igorwojda/showcase/app/AppKoinModule.kt ================================================ package com.igorwojda.showcase.app import com.igorwojda.showcase.app.data.api.AuthenticationInterceptor import com.igorwojda.showcase.app.data.api.UserAgentInterceptor import com.igorwojda.showcase.feature.base.data.retrofit.ApiResultAdapterFactory import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory import kotlinx.serialization.ExperimentalSerializationApi import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor import org.koin.core.module.dsl.singleOf import org.koin.dsl.module import retrofit2.Retrofit import timber.log.Timber val appModule = module { single { AuthenticationInterceptor(BuildConfig.GRADLE_API_TOKEN) } singleOf(::UserAgentInterceptor) single { HttpLoggingInterceptor { message -> Timber.d("Http: $message") }.apply { level = HttpLoggingInterceptor.Level.BODY } } /* * OkHttp logging interceptor with custom Timber logger. * * By default, HttpLoggingInterceptor uses the calling class name as the log tag which clutters Logcat and makes filtering harder. * * This custom configuration ensures: * - All HTTP logs are tagged consistently as `"Network"`. * - Logs are printed through Timber (instead of Android's `Log`). * - Logging level is set to BODY to include headers and payloads. */ single { HttpLoggingInterceptor { message -> Timber.tag("Network").d(message) }.apply { /* Use BODY logging only in debug builds. Even if Timber.DebugTree() is planted only in debug, the interceptor still reads/constructs request/response bodies when level = BODY. This adds unnecessary overhead and may leak sensitive data if any logger is active in production. Setting NONE in release avoids both risks. */ level = if (BuildConfig.DEBUG) { HttpLoggingInterceptor.Level.BODY } else { HttpLoggingInterceptor.Level.NONE } } } single { OkHttpClient .Builder() .apply { if (BuildConfig.DEBUG) { addInterceptor(get()) } addInterceptor(get()) addInterceptor(get()) }.build() } single { val contentType = "application/json".toMediaType() val json = kotlinx.serialization.json.Json { // By default Kotlin serialization will serialize all of the keys present in JSON object and throw an // exception if given key is not present in the Kotlin class. This flag allows to ignore JSON fields ignoreUnknownKeys = true } @OptIn(ExperimentalSerializationApi::class) Retrofit .Builder() .baseUrl(BuildConfig.GRADLE_API_BASE_URL) .client(get()) .addConverterFactory(json.asConverterFactory(contentType)) .addCallAdapterFactory(ApiResultAdapterFactory()) .build() } } ================================================ FILE: app/src/main/kotlin/com/igorwojda/showcase/app/ShowcaseApplication.kt ================================================ package com.igorwojda.showcase.app import android.app.Application import com.igorwojda.showcase.feature.album.featureAlbumModules import com.igorwojda.showcase.feature.favourite.featureFavouriteModules import com.igorwojda.showcase.feature.settings.featureSettingsModules import org.koin.android.ext.koin.androidContext import org.koin.android.ext.koin.androidLogger import org.koin.core.context.GlobalContext import timber.log.Timber class ShowcaseApplication : Application() { override fun onCreate() { super.onCreate() initKoin() initTimber() } private fun initKoin() { GlobalContext.startKoin { androidLogger() androidContext(this@ShowcaseApplication) modules(appModule) modules(featureFavouriteModules) modules(featureAlbumModules) modules(featureSettingsModules) } } private fun initTimber() { if (BuildConfig.DEBUG) { Timber.plant(Timber.DebugTree()) } } } ================================================ FILE: app/src/main/kotlin/com/igorwojda/showcase/app/data/api/AuthenticationInterceptor.kt ================================================ package com.igorwojda.showcase.app.data.api import okhttp3.Interceptor import okhttp3.Response class AuthenticationInterceptor( private val apiKey: String, ) : Interceptor { override fun intercept(chain: Interceptor.Chain): Response = chain.request().let { val url = it.url .newBuilder() .addQueryParameter("api_key", apiKey) .addQueryParameter("format", "json") .build() val newRequest = it .newBuilder() .url(url) .build() chain.proceed(newRequest) } } ================================================ FILE: app/src/main/kotlin/com/igorwojda/showcase/app/data/api/UserAgentInterceptor.kt ================================================ package com.igorwojda.showcase.app.data.api import com.igorwojda.showcase.app.BuildConfig import okhttp3.Interceptor import okhttp3.Response /* * Adds a User-Agent header to the request. The header follows this format: * / Dalvik/ (Linux; U; Android ; Build/) * * See user agents in mobile apps: https://www.scientiamobile.com/correctly-form-user-agents-for-mobile-apps * See testing user agent: https://faisalman.github.io/ua-parser-js/ */ class UserAgentInterceptor : Interceptor { private val userAgent = "showcase/${BuildConfig.VERSION_NAME} ${System.getProperty("http.agent")}" override fun intercept(chain: Interceptor.Chain): Response = chain .request() .newBuilder() .header("User-Agent", userAgent) .build() .let { chain.proceed(it) } } ================================================ FILE: app/src/main/kotlin/com/igorwojda/showcase/app/presentation/BottomNavigationBar.kt ================================================ package com.igorwojda.showcase.app.presentation import androidx.annotation.DrawableRes import androidx.annotation.StringRes import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.NavigationBar import androidx.compose.material3.NavigationBarItem import androidx.compose.material3.NavigationBarItemDefaults import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.navigation.NavController import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.rememberNavController import com.igorwojda.showcase.app.R @Composable fun BottomNavigationBar( navController: NavController, modifier: Modifier = Modifier, ) { val navigationItems = getBottomNavigationItems() val navBackStackEntry by navController.currentBackStackEntryAsState() val currentRoute = navBackStackEntry?.destination?.route val selectedNavigationIndex = getSelectedNavigationIndex(currentRoute, navigationItems) NavigationBar( modifier = modifier, ) { navigationItems.forEachIndexed { index, item -> NavigationBarItem( selected = selectedNavigationIndex == index, onClick = { navController.navigate(item.route) { popUpTo(0) restoreState = true // Restores previous state if returning } }, icon = { Icon( painter = painterResource(item.iconRes), contentDescription = stringResource(item.titleRes), ) }, label = { Text( stringResource(item.titleRes), ) }, colors = NavigationBarItemDefaults.colors( selectedIconColor = MaterialTheme.colorScheme.surface, indicatorColor = MaterialTheme.colorScheme.primary, ), ) } } } private fun getBottomNavigationItems() = listOf( NavigationBarItem( R.string.bottom_navigation_albums, R.drawable.ic_music_library, NavigationRoute.AlbumList, ), NavigationBarItem( R.string.bottom_navigation_favorites, R.drawable.ic_favorite, NavigationRoute.Favourites, ), NavigationBarItem( R.string.bottom_navigation_settings, R.drawable.ic_settings, NavigationRoute.Settings, ), ) /* Returns the index of the selected bottom menu item based on the current route. If no match is found, it defaults to the first item (index 0). */ private fun getSelectedNavigationIndex( currentRoute: String?, navigationItems: List, ): Int = navigationItems .indexOfFirst { item -> when (currentRoute) { null -> false NavigationRoute.AlbumDetail::class.qualifiedName -> item.route is NavigationRoute.AlbumList NavigationRoute.AboutLibraries::class.qualifiedName -> item.route is NavigationRoute.Settings else -> item.route::class.qualifiedName == currentRoute } }.takeIf { it >= 0 } ?: 0 data class NavigationBarItem( @StringRes val titleRes: Int, @DrawableRes val iconRes: Int, val route: NavigationRoute, ) @Preview @Composable private fun BottomNavigationBarPreview() { BottomNavigationBar( navController = rememberNavController(), ) } ================================================ FILE: app/src/main/kotlin/com/igorwojda/showcase/app/presentation/MainShowcaseActivity.kt ================================================ package com.igorwojda.showcase.app.presentation import android.os.Build import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.material3.ColorScheme import androidx.compose.material3.MaterialTheme import androidx.compose.material3.darkColorScheme import androidx.compose.material3.dynamicDarkColorScheme import androidx.compose.material3.dynamicLightColorScheme import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable import androidx.compose.ui.platform.LocalContext import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen class MainShowcaseActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { installSplashScreen() super.onCreate(savedInstanceState) setContent { MaterialTheme(colorScheme = getColorScheme()) { MainShowcaseScreen() } } } @Composable private fun getColorScheme(): ColorScheme { val darkTheme: Boolean = isSystemInDarkTheme() val dynamicColor: Boolean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S val context = LocalContext.current return when { dynamicColor -> if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) darkTheme -> darkColorScheme() else -> lightColorScheme() } } } ================================================ FILE: app/src/main/kotlin/com/igorwojda/showcase/app/presentation/MainShowcaseScreen.kt ================================================ package com.igorwojda.showcase.app.presentation import android.os.Bundle import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.navigation.NavController import androidx.navigation.NavDestination import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import androidx.navigation.createGraph import androidx.navigation.toRoute import com.igorwojda.showcase.app.BuildConfig import com.igorwojda.showcase.app.presentation.util.NavigationDestinationLogger import com.igorwojda.showcase.feature.album.presentation.screen.albumdetail.AlbumDetailScreen import com.igorwojda.showcase.feature.album.presentation.screen.albumlist.AlbumListScreen import com.igorwojda.showcase.feature.favourite.presentation.screen.favourite.FavouriteScreen import com.igorwojda.showcase.feature.settings.presentation.screen.aboutlibraries.AboutLibrariesScreen import com.igorwojda.showcase.feature.settings.presentation.screen.settings.SettingsScreen @Composable fun MainShowcaseScreen(modifier: Modifier = Modifier) { val navController = rememberNavController() if (BuildConfig.DEBUG) { addOnDestinationChangedListener(navController) } Scaffold( modifier = modifier.fillMaxSize(), bottomBar = { BottomNavigationBar(navController) }, ) { innerPadding -> val graph = navController.createGraph(startDestination = NavigationRoute.AlbumList) { composable { AlbumListScreen( // artistName: String, albumName: String, mbId: String? onNavigateToAlbumDetail = { artistName, albumName, albumMbId -> navController.navigate( NavigationRoute.AlbumDetail(artistName, albumName, albumMbId), ) }, ) } composable { backStackEntry -> // Retrieve typed args val args = backStackEntry.toRoute() AlbumDetailScreen( albumName = args.albumName, artistName = args.artistName, albumMbId = args.albumMbId, onBackClick = { navController.popBackStack() }, ) } composable { FavouriteScreen() } composable { SettingsScreen( onNavigateToAboutLibraries = { navController.navigate(NavigationRoute.AboutLibraries) }, ) } composable { AboutLibrariesScreen( onBackClick = { navController.popBackStack() }, ) } } NavHost( navController = navController, graph = graph, modifier = Modifier.padding(innerPadding), ) } } private fun addOnDestinationChangedListener(navController: NavController) { navController.addOnDestinationChangedListener( object : NavController.OnDestinationChangedListener { override fun onDestinationChanged( controller: NavController, destination: NavDestination, arguments: Bundle?, ) { NavigationDestinationLogger.logDestinationChange(destination, arguments) } }, ) } ================================================ FILE: app/src/main/kotlin/com/igorwojda/showcase/app/presentation/NavigationRoute.kt ================================================ package com.igorwojda.showcase.app.presentation import kotlinx.serialization.Serializable sealed interface NavigationRoute { @Serializable data object AlbumList : NavigationRoute @Serializable data class AlbumDetail( val albumName: String, val artistName: String, val albumMbId: String?, ) : NavigationRoute @Serializable data object Favourites : NavigationRoute @Serializable data object Settings : NavigationRoute @Serializable data object AboutLibraries : NavigationRoute } ================================================ FILE: app/src/main/kotlin/com/igorwojda/showcase/app/presentation/util/NavigationDestinationLogger.kt ================================================ package com.igorwojda.showcase.app.presentation.util import android.os.Bundle import androidx.navigation.NavDestination import com.igorwojda.showcase.app.presentation.NavigationRoute import com.igorwojda.showcase.feature.base.util.TimberLogTags import timber.log.Timber object NavigationDestinationLogger { fun logDestinationChange( destination: NavDestination, arguments: Bundle?, ) { val className = NavigationRoute::class.simpleName val destinationRoute = destination.route?.substringAfter("$className.") ?: "Unknown" val destinationId = destination.id val destinationLabel = destination.label ?: "No Label" val logMessage = buildString { appendLine("Navigation destination changed:") appendLine("\tRoute: $destinationRoute") appendLine("\tID: $destinationId") appendLine("\tLabel: $destinationLabel") arguments?.let { bundle -> if (!bundle.isEmpty) { appendLine(" Arguments:") bundle.keySet().forEach { key -> val value = getValueFromBundle(bundle, key) ?: "null" appendLine("\t\t$key: $value") } } } } Timber.tag(TimberLogTags.NAVIGATION).d(logMessage) } /** * Retrieves a value from Bundle using Android Navigation supported types. * Navigation supports: String, Int, Long, Float, Boolean, Parcelable, Serializable, and their arrays. * * @return String representation of the value, or null if no matching type found */ private fun getValueFromBundle( bundle: Bundle, key: String, ): String? = bundle.getString(key)?.let { "\"$it\"" } ?: runCatching { bundle.getInt(key) }.getOrNull()?.toString() ?: runCatching { bundle.getLong(key) }.getOrNull()?.toString() ?: runCatching { bundle.getFloat(key) }.getOrNull()?.toString() ?: runCatching { bundle.getBoolean(key) }.getOrNull()?.toString() ?: bundle.getStringArray(key)?.contentToString() ?: bundle.getIntArray(key)?.contentToString() ?: bundle.getLongArray(key)?.contentToString() ?: bundle.getFloatArray(key)?.contentToString() ?: bundle.getBooleanArray(key)?.contentToString() } ================================================ FILE: app/src/main/res/drawable/ic_favorite.xml ================================================ ================================================ FILE: app/src/main/res/drawable/ic_launcher_foreground.xml ================================================ ================================================ FILE: app/src/main/res/drawable/ic_launcher_foreground_themed.xml ================================================ ================================================ FILE: app/src/main/res/drawable/ic_music_library.xml ================================================ ================================================ FILE: app/src/main/res/drawable/ic_settings.xml ================================================ ================================================ FILE: app/src/main/res/mipmap-anydpi/ic_launcher.xml ================================================ ================================================ FILE: app/src/main/res/mipmap-anydpi/ic_launcher_round.xml ================================================ ================================================ FILE: app/src/main/res/mipmap-anydpi-v33/ic_launcher.xml ================================================ ================================================ FILE: app/src/main/res/mipmap-anydpi-v33/ic_launcher_round.xml ================================================ ================================================ FILE: app/src/main/res/values/colors.xml ================================================ #FFFFFF #3DDC84 ================================================ FILE: app/src/main/res/values/ic_launcher_background.xml ================================================ #000000 ================================================ FILE: app/src/main/res/values/strings.xml ================================================ Showcase Albums Favorites Settings ================================================ FILE: app/src/main/res/values/styles.xml ================================================ ================================================ FILE: app/src/main/res/xml/data_extraction_rules.xml ================================================ ================================================ FILE: build-logic/build.gradle.kts ================================================ import org.jetbrains.kotlin.gradle.dsl.JvmTarget plugins { `kotlin-dsl` } group = "com.igorwojda.showcase.buildlogic" /* Configure the build-logic plugins to target JDK from version catalog This matches the JDK used to build the project, and is not related to what is running on device. */ val javaVersion = libs .versions .java .get() kotlin { compilerOptions { jvmTarget = JvmTarget.fromTarget(javaVersion) } jvmToolchain(javaVersion.toInt()) } dependencies { implementation(libs.android.gradlePlugin) implementation(libs.kotlin.gradlePlugin) implementation(libs.ksp.gradlePlugin) implementation(libs.spotless.gradlePlugin) implementation(libs.detekt.gradlePlugin) implementation(libs.test.logger.gradlePlugin) implementation(libs.compose.gradlePlugin) implementation(libs.junit5.gradlePlugin) implementation(libs.easy.launcher.gradlePlugin) implementation(libs.about.libraries.gradlePlugin) /* Expose generated type-safe version catalogs accessors accessible from precompiled script plugins e.g. add("implementation", libs.koin) https://github.com/gradle/gradle/issues/15383 */ implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location)) } tasks { validatePlugins { enableStricterValidation = true failOnWarning = true } } gradlePlugin { plugins { register("applicationConvention") { id = "com.igorwojda.showcase.convention.application" implementationClass = "com.igorwojda.showcase.buildlogic.ApplicationConventionPlugin" } register("featureConvention") { id = "com.igorwojda.showcase.convention.feature" implementationClass = "com.igorwojda.showcase.buildlogic.FeatureConventionPlugin" } register("libraryConvention") { id = "com.igorwojda.showcase.convention.library" implementationClass = "com.igorwojda.showcase.buildlogic.LibraryConventionPlugin" } register("kotlinConvention") { id = "com.igorwojda.showcase.convention.kotlin" implementationClass = "com.igorwojda.showcase.buildlogic.KotlinConventionPlugin" } register("testConvention") { id = "com.igorwojda.showcase.convention.test" implementationClass = "com.igorwojda.showcase.buildlogic.TestConventionPlugin" } register("testLibraryConvention") { id = "com.igorwojda.showcase.convention.test.library" implementationClass = "com.igorwojda.showcase.buildlogic.TestConventionLibraryPlugin" } register("spotlessConvention") { id = "com.igorwojda.showcase.convention.spotless" implementationClass = "com.igorwojda.showcase.buildlogic.SpotlessConventionPlugin" } register("detektConvention") { id = "com.igorwojda.showcase.convention.detekt" implementationClass = "com.igorwojda.showcase.buildlogic.DetektConventionPlugin" } register("easyLauncherConvention") { id = "com.igorwojda.showcase.convention.easylauncher" implementationClass = "com.igorwojda.showcase.buildlogic.EasyLauncherConventionPlugin" } register("aboutLibrariesConvention") { id = "com.igorwojda.showcase.convention.aboutlibraries" implementationClass = "com.igorwojda.showcase.buildlogic.AboutLibrariesConventionPlugin" } } } ================================================ FILE: build-logic/settings.gradle.kts ================================================ rootProject.name = "build-logic" @Suppress("UnstableApiUsage") dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() mavenCentral() gradlePluginPortal() } versionCatalogs { create("libs") { from(files("../gradle/libs.versions.toml")) } } } ================================================ FILE: build-logic/src/main/kotlin/com/igorwojda/showcase/buildlogic/AboutLibrariesConventionPlugin.kt ================================================ package com.igorwojda.showcase.buildlogic import com.mikepenz.aboutlibraries.plugin.AboutLibrariesExtension import com.mikepenz.aboutlibraries.plugin.DuplicateMode import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.kotlin.dsl.configure class AboutLibrariesConventionPlugin : Plugin { override fun apply(target: Project) { with(target) { with(pluginManager) { apply("com.mikepenz.aboutlibraries.plugin.android") } extensions.configure { library { // Avoids duplicate entries in the generated about libraries screen duplicationMode.set(DuplicateMode.MERGE) } collect { all.set(true) } } } } } ================================================ FILE: build-logic/src/main/kotlin/com/igorwojda/showcase/buildlogic/ApplicationConventionPlugin.kt ================================================ package com.igorwojda.showcase.buildlogic import com.android.build.api.dsl.ApplicationExtension import com.igorwojda.showcase.buildlogic.config.JavaBuildConfig import com.igorwojda.showcase.buildlogic.ext.debugImplementation import com.igorwojda.showcase.buildlogic.ext.excludeLicenseAndMetaFiles import com.igorwojda.showcase.buildlogic.ext.implementation import com.igorwojda.showcase.buildlogic.ext.libs import com.igorwojda.showcase.buildlogic.ext.versions import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.kotlin.dsl.apply import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.dependencies @Suppress("detekt.LongMethod") class ApplicationConventionPlugin : Plugin { override fun apply(target: Project) { with(target) { with(pluginManager) { apply("com.android.application") apply("com.google.devtools.ksp") apply("org.jetbrains.kotlin.plugin.compose") apply() apply() apply() apply() } extensions.configure { compileSdk = versions .compile .sdk .get() .toInt() defaultConfig { applicationId = "com.igorwojda.showcase" minSdk = versions .min .sdk .get() .toInt() targetSdk = versions .target .sdk .get() .toInt() versionCode = 1 versionName = "1.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" multiDexEnabled = true vectorDrawables { useSupportLibrary = true } } buildFeatures { viewBinding = true buildConfig = true compose = true } compileOptions { sourceCompatibility = JavaBuildConfig.JAVA_VERSION targetCompatibility = JavaBuildConfig.JAVA_VERSION } packaging { excludeLicenseAndMetaFiles() } testOptions { unitTests.isReturnDefaultValues = true } } dependencies { implementation(libs.kotlin.reflect) implementation(libs.core.ktx) implementation(libs.timber) implementation(libs.coroutines) implementation(libs.material.material) implementation(libs.compose.material) implementation(libs.material.icons) // Compose dependencies implementation(platform(libs.compose.bom)) implementation(libs.tooling.preview) debugImplementation(libs.compose.ui.tooling) debugImplementation(libs.compose.ui.test.manifest) implementation(libs.navigation.compose) // Koin implementation(platform(libs.koin.bom)) implementation(libs.bundles.koin) implementation(libs.bundles.retrofit) implementation(libs.viewmodel.ktx) implementation(libs.core.splashscreen) } } } } ================================================ FILE: build-logic/src/main/kotlin/com/igorwojda/showcase/buildlogic/DetektConventionPlugin.kt ================================================ package com.igorwojda.showcase.buildlogic import io.gitlab.arturbosch.detekt.Detekt import org.gradle.api.Plugin import org.gradle.api.Project class DetektConventionPlugin : Plugin { override fun apply(target: Project) { with(target) { pluginManager.apply("io.gitlab.arturbosch.detekt") repositories.mavenCentral() val detektCheck = tasks.register("detektCheck", Detekt::class.java) { description = "Checks that sourcecode satisfies detekt rules." autoCorrect = false } val detektApply = tasks.register("detektApply", Detekt::class.java) { description = "Applies code formatting rules to sourcecode in-place." autoCorrect = true } listOf(detektCheck, detektApply).forEach { taskProvider -> taskProvider.configure { group = "verification" parallel = true ignoreFailures = false setSource(file(rootDir)) // Custom detekt config config.setFrom("$rootDir/detekt.yml") // Use default configuration on top of custom config buildUponDefaultConfig = true // Runs detekt for all files in the Gradle project and all subprojects include("**/*.kt", "**/*.kts") exclude("**/resources/**", "**/build/**", "**/generated/**") reports { html.required.set(true) xml.required.set(true) } } } } } } ================================================ FILE: build-logic/src/main/kotlin/com/igorwojda/showcase/buildlogic/EasyLauncherConventionPlugin.kt ================================================ package com.igorwojda.showcase.buildlogic import com.project.starter.easylauncher.filter.ChromeLikeFilter import com.project.starter.easylauncher.plugin.EasyLauncherExtension import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.kotlin.dsl.configure @Suppress("detekt.LongMethod") class EasyLauncherConventionPlugin : Plugin { override fun apply(project: Project) { with(project) { with(pluginManager) { apply("com.starter.easylauncher") } extensions.configure { defaultFlavorNaming(true) buildTypes.create("debug") { setFilters( chromeLike( ribbonColor = OVERLAY_COLOR_BACKGROUND_DEBUG, labelColor = OVERLAY_COLOR_TEXT, gravity = ChromeLikeFilter.Gravity.BOTTOM, overlayHeight = OVERLAY_HEIGHT, textSizeRatio = 0.2F, ), ) } } } } companion object { private const val OVERLAY_COLOR_BACKGROUND_DEBUG = "#99AD0000" private const val OVERLAY_COLOR_TEXT = "#FFFFFF" private const val OVERLAY_HEIGHT = 0.25F } } ================================================ FILE: build-logic/src/main/kotlin/com/igorwojda/showcase/buildlogic/FeatureConventionPlugin.kt ================================================ package com.igorwojda.showcase.buildlogic import com.android.build.api.dsl.LibraryExtension import com.igorwojda.showcase.buildlogic.config.JavaBuildConfig import com.igorwojda.showcase.buildlogic.ext.debugImplementation import com.igorwojda.showcase.buildlogic.ext.excludeLicenseAndMetaFiles import com.igorwojda.showcase.buildlogic.ext.implementation import com.igorwojda.showcase.buildlogic.ext.ksp import com.igorwojda.showcase.buildlogic.ext.libs import com.igorwojda.showcase.buildlogic.ext.testImplementation import com.igorwojda.showcase.buildlogic.ext.testRuntimeOnly import com.igorwojda.showcase.buildlogic.ext.versions import com.mikepenz.aboutlibraries.plugin.AboutLibrariesPlugin import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.kotlin.dsl.apply import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.dependencies @Suppress("detekt.LongMethod") class FeatureConventionPlugin : Plugin { override fun apply(target: Project) { with(target) { with(pluginManager) { apply("com.android.library") apply() apply() apply() apply("com.google.devtools.ksp") apply("org.jetbrains.kotlin.plugin.compose") } extensions.configure { compileSdk = versions .compile .sdk .get() .toInt() defaultConfig { minSdk = versions .min .sdk .get() .toInt() testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles("consumer-rules.pro") } buildFeatures { viewBinding = true buildConfig = true compose = true } compileOptions { sourceCompatibility = JavaBuildConfig.JAVA_VERSION targetCompatibility = JavaBuildConfig.JAVA_VERSION } testOptions { unitTests.isReturnDefaultValues = true } packaging { excludeLicenseAndMetaFiles() } } dependencies { // Add feature:base dependency only for non-base feature modules if (project.path != ":feature:base") { implementation(project(":feature:base")) } implementation(libs.kotlin.reflect) implementation(libs.core.ktx) implementation(libs.timber) implementation(libs.coroutines) implementation(libs.material.material) implementation(libs.compose.material) implementation(libs.material.icons) // Compose dependencies implementation(platform(libs.compose.bom)) implementation(libs.bundles.compose) debugImplementation(libs.compose.ui.tooling) debugImplementation(libs.compose.ui.test.manifest) // Koin implementation(platform(libs.koin.bom)) implementation(libs.bundles.koin) implementation(libs.bundles.retrofit) implementation(libs.viewmodel.ktx) // Room implementation(libs.bundles.room) ksp(libs.room.compiler) // Test dependencies testImplementation(project(":library:test-utils")) testImplementation(libs.bundles.test) testRuntimeOnly(libs.junit.jupiter.engine) } } } } ================================================ FILE: build-logic/src/main/kotlin/com/igorwojda/showcase/buildlogic/KotlinConventionPlugin.kt ================================================ package com.igorwojda.showcase.buildlogic import com.igorwojda.showcase.buildlogic.config.JavaBuildConfig import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.kotlin.dsl.withType import org.jetbrains.kotlin.gradle.dsl.kotlinExtension import org.jetbrains.kotlin.gradle.tasks.KotlinCompile class KotlinConventionPlugin : Plugin { override fun apply(target: Project) { with(target) { with(pluginManager) { apply("org.jetbrains.kotlin.android") apply("org.jetbrains.kotlin.plugin.serialization") } kotlinExtension.jvmToolchain(JavaBuildConfig.JVM_TOOLCHAIN_VERSION) tasks.withType().configureEach { compilerOptions { freeCompilerArgs.addAll( /* This ensures annotations on data class constructor parameters are applied to both the parameter and the backing field, preventing future breaking changes. See https://youtrack.jetbrains.com/issue/KT-73255: for more details. */ "-Xannotation-default-target=param-property", ) } } } } } ================================================ FILE: build-logic/src/main/kotlin/com/igorwojda/showcase/buildlogic/LibraryConventionPlugin.kt ================================================ package com.igorwojda.showcase.buildlogic import com.android.build.api.dsl.LibraryExtension import com.igorwojda.showcase.buildlogic.config.JavaBuildConfig import com.igorwojda.showcase.buildlogic.ext.excludeLicenseAndMetaFiles import com.igorwojda.showcase.buildlogic.ext.versions import com.mikepenz.aboutlibraries.plugin.AboutLibrariesPlugin import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.kotlin.dsl.apply import org.gradle.kotlin.dsl.configure class LibraryConventionPlugin : Plugin { override fun apply(target: Project) { with(target) { with(pluginManager) { apply("com.android.library") apply() apply() apply() apply("com.google.devtools.ksp") apply("org.jetbrains.kotlin.plugin.compose") } extensions.configure { compileSdk = versions .compile .sdk .get() .toInt() defaultConfig { minSdk = versions .min .sdk .get() .toInt() testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles("consumer-rules.pro") } buildFeatures { viewBinding = true buildConfig = true compose = true } compileOptions { sourceCompatibility = JavaBuildConfig.JAVA_VERSION targetCompatibility = JavaBuildConfig.JAVA_VERSION } testOptions { unitTests.isReturnDefaultValues = true } packaging { excludeLicenseAndMetaFiles() } } } } } ================================================ FILE: build-logic/src/main/kotlin/com/igorwojda/showcase/buildlogic/SpotlessConventionPlugin.kt ================================================ package com.igorwojda.showcase.buildlogic import com.diffplug.gradle.spotless.SpotlessExtension import com.igorwojda.showcase.buildlogic.ext.libs import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.kotlin.dsl.configure class SpotlessConventionPlugin : Plugin { override fun apply(target: Project) { with(target) { pluginManager.apply("com.diffplug.spotless") extensions.configure { kotlin { target("**/*.kt", "**/*.kts") // Some rules are disabled in .editorconfig to avoid conflicts with detekt val customRuleSets = listOf( libs.ktlint.ruleset.standard, libs.nlopez.compose.rules, libs.twitter.compose.rules, ).map { it.get().toString() } ktlint() .customRuleSets(customRuleSets) endWithNewline() } // Don't add spotless as dependency for the Gradle's check task to facilitate separated codebase checks isEnforceCheck = false } } } } ================================================ FILE: build-logic/src/main/kotlin/com/igorwojda/showcase/buildlogic/TestConventionLibraryPlugin.kt ================================================ package com.igorwojda.showcase.buildlogic import com.android.build.api.dsl.LibraryExtension import com.igorwojda.showcase.buildlogic.config.JavaBuildConfig import com.igorwojda.showcase.buildlogic.ext.versions import com.mikepenz.aboutlibraries.plugin.AboutLibrariesPlugin import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.kotlin.dsl.apply import org.gradle.kotlin.dsl.configure class TestConventionLibraryPlugin : Plugin { override fun apply(target: Project) { with(target) { with(pluginManager) { apply("com.android.library") apply() apply() apply() apply("com.google.devtools.ksp") } extensions.configure { compileSdk = versions .compile .sdk .get() .toInt() defaultConfig { minSdk = versions .min .sdk .get() .toInt() testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles("consumer-rules.pro") } buildFeatures { viewBinding = false buildConfig = false compose = false } compileOptions { sourceCompatibility = JavaBuildConfig.JAVA_VERSION targetCompatibility = JavaBuildConfig.JAVA_VERSION } testOptions { unitTests.isReturnDefaultValues = true } packaging { resources.excludes += setOf( "META-INF/AL2.0", "META-INF/licenses/**", "**/attach_hotspot_windows.dll", "META-INF/LGPL2.1", ) } } } } } ================================================ FILE: build-logic/src/main/kotlin/com/igorwojda/showcase/buildlogic/TestConventionPlugin.kt ================================================ package com.igorwojda.showcase.buildlogic import com.adarshr.gradle.testlogger.TestLoggerExtension import com.adarshr.gradle.testlogger.theme.ThemeType import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.tasks.testing.Test import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.withType class TestConventionPlugin : Plugin { override fun apply(target: Project) { with(target) { pluginManager.apply("com.adarshr.test-logger") tasks.withType { useJUnitPlatform() // Enable parallel test execution systemProperties = mapOf( "junit.jupiter.execution.parallel.enabled" to "true", "junit.jupiter.execution.parallel.mode.default " to "concurrent", ) } extensions.configure { theme = ThemeType.MOCHA } } } } ================================================ FILE: build-logic/src/main/kotlin/com/igorwojda/showcase/buildlogic/config/JavaBuildConfig.kt ================================================ package com.igorwojda.showcase.buildlogic.config import org.gradle.api.JavaVersion import java.io.File object JavaBuildConfig { /** * Reads the Java version from the `gradle/libs.versions.toml` file. * (VersionCatalogsExtension is not available at this stage). */ private val tomlJavaVersion by lazy { File(System.getProperty("user.dir")) .resolve("gradle/libs.versions.toml") .readLines() .firstOrNull { it.trim().startsWith("java") } ?.substringAfter("=") ?.trim('"', ' ') ?: error("❌ Could not find 'java' version in libs.versions.toml file") } /* Configure the buildLogic config to target JDK from version catalog This matches the JDK used to build the project. */ val JAVA_VERSION: JavaVersion = JavaVersion.toVersion(tomlJavaVersion) val JVM_TOOLCHAIN_VERSION: Int = tomlJavaVersion.toInt() } ================================================ FILE: build-logic/src/main/kotlin/com/igorwojda/showcase/buildlogic/ext/BuildConfigExt.kt ================================================ package com.igorwojda.showcase.buildlogic.ext import com.android.build.api.dsl.ApplicationDefaultConfig import com.android.build.api.dsl.LibraryDefaultConfig import org.gradle.api.Project import java.util.Locale /** * Takes value from Gradle project property and sets it as Android build config property. * Example: apiToken variable present in the settings.gradle file will be accessible * as BuildConfig.GRADLE_API_TOKEN in the app. */ fun ApplicationDefaultConfig.buildConfigFieldFromGradleProperty( project: Project, gradlePropertyName: String, ) { val (androidResourceName, propertyValue) = extractBuildConfigField(project, gradlePropertyName) buildConfigField("String", androidResourceName, propertyValue) } /** * Takes value from Gradle project property and sets it as Android build config property. * Example: apiToken variable present in the settings.gradle file will be accessible * as BuildConfig.GRADLE_API_TOKEN in the library. */ fun LibraryDefaultConfig.buildConfigFieldFromGradleProperty( project: Project, gradlePropertyName: String, ) { val (androidResourceName, propertyValue) = extractBuildConfigField(project, gradlePropertyName) buildConfigField("String", androidResourceName, propertyValue) } private fun extractBuildConfigField( project: Project, gradlePropertyName: String, ): Pair { val propertyValue = project.properties[gradlePropertyName] as? String checkNotNull(propertyValue) { "Gradle property $gradlePropertyName is null" } val androidResourceName = "GRADLE_${gradlePropertyName.toSnakeCase()}".uppercase(Locale.getDefault()) return androidResourceName to propertyValue } private fun String.toSnakeCase() = this.split(Regex("(?=[A-Z])")).joinToString("_") { it.lowercase(Locale.getDefault()) } ================================================ FILE: build-logic/src/main/kotlin/com/igorwojda/showcase/buildlogic/ext/DependencyHandlerExt.kt ================================================ package com.igorwojda.showcase.buildlogic.ext import org.gradle.accessors.dm.LibrariesForLibs import org.gradle.api.Project import org.gradle.api.provider.Provider import org.gradle.kotlin.dsl.DependencyHandlerScope private const val IMPLEMENTATION = "implementation" private const val DEBUG_IMPLEMENTATION = "debugImplementation" private const val TEST_IMPLEMENTATION = "testImplementation" private const val TEST_RUNTIME_ONLY = "testRuntimeOnly" private const val KSP = "ksp" fun DependencyHandlerScope.implementation(provider: Provider) { add(IMPLEMENTATION, provider) } fun DependencyHandlerScope.implementation(project: Project) { add(IMPLEMENTATION, project) } fun DependencyHandlerScope.implementation(provider: LibrariesForLibs.KotlinLibraryAccessors) { add(IMPLEMENTATION, provider) } fun DependencyHandlerScope.debugImplementation(provider: Provider) { add(DEBUG_IMPLEMENTATION, provider) } fun DependencyHandlerScope.ksp(provider: Provider) { add(KSP, provider) } fun DependencyHandlerScope.testImplementation(project: Project) { add(TEST_IMPLEMENTATION, project) } fun DependencyHandlerScope.testImplementation(provider: Provider) { add(TEST_IMPLEMENTATION, provider) } fun DependencyHandlerScope.testRuntimeOnly(provider: Provider) { add(TEST_RUNTIME_ONLY, provider) } ================================================ FILE: build-logic/src/main/kotlin/com/igorwojda/showcase/buildlogic/ext/PackagingExt.kt ================================================ package com.igorwojda.showcase.buildlogic.ext import com.android.build.api.dsl.Packaging fun Packaging.excludeLicenseAndMetaFiles() { resources.excludes += setOf( "META-INF/AL2.0", "META-INF/licenses/**", "**/attach_hotspot_windows.dll", "META-INF/LGPL2.1", ) } ================================================ FILE: build-logic/src/main/kotlin/com/igorwojda/showcase/buildlogic/ext/ProjectExt.kt ================================================ package com.igorwojda.showcase.buildlogic.ext import org.gradle.accessors.dm.LibrariesForLibs import org.gradle.api.Project import org.gradle.kotlin.dsl.the /** * Returns "libs" from version catalog. */ val Project.libs: LibrariesForLibs get() = the() /** * Returns "versions" from version catalog. */ val Project.versions: LibrariesForLibs.VersionAccessors get() = the().versions ================================================ FILE: build.gradle.kts ================================================ plugins { // Convention plugins id("com.igorwojda.showcase.convention.detekt") id("com.igorwojda.showcase.convention.spotless") // Core Android and Kotlin plugins using version catalog alias(libs.plugins.android.application) apply false alias(libs.plugins.android.library) apply false alias(libs.plugins.kotlin.android) apply false alias(libs.plugins.kotlin.serialization) apply false alias(libs.plugins.kotlin.symbol.processing) apply false alias(libs.plugins.compose) apply false alias(libs.plugins.test.logger) apply false alias(libs.plugins.detekt) apply false alias(libs.plugins.spotless) apply false alias(libs.plugins.junit5.android) apply false } ================================================ FILE: detekt.yml ================================================ build: maxIssues: 0 excludeCorrectable: false weights: # complexity: 2 # LongParameterList: 1 # style: 1 # comments: 1 config: validation: true warningsAsErrors: true checkExhaustiveness: true # when writing own rules with new properties, exclude the property path e.g.: 'my_rule_set,.*>.*>[my_property]' excludes: '' processors: active: true exclude: - 'DetektProgressListener' # - 'KtFileCountProcessor' # - 'PackageCountProcessor' # - 'ClassCountProcessor' # - 'FunctionCountProcessor' # - 'PropertyCountProcessor' # - 'ProjectComplexityProcessor' # - 'ProjectCognitiveComplexityProcessor' # - 'ProjectLLOCProcessor' # - 'ProjectCLOCProcessor' # - 'ProjectLOCProcessor' # - 'ProjectSLOCProcessor' # - 'LicenseHeaderLoaderExtension' console-reports: active: true exclude: - 'ProjectStatisticsReport' - 'ComplexityReport' - 'NotificationReport' - 'FindingsReport' - 'FileBasedFindingsReport' # - 'LiteFindingsReport' output-reports: active: true exclude: # - 'TxtOutputReport' # - 'XmlOutputReport' # - 'HtmlOutputReport' # - 'MdOutputReport' # - 'SarifOutputReport' comments: active: true AbsentOrWrongFileLicense: active: false licenseTemplateFile: 'license.template' licenseTemplateIsRegex: false CommentOverPrivateFunction: active: false CommentOverPrivateProperty: active: false DeprecatedBlockTag: active: true EndOfSentenceFormat: active: true endOfSentenceFormat: '([.?!][ \t\n\r\f<])|([.?!:]$)' KDocReferencesNonPublicProperty: active: true excludes: ['**/[Tt]est/**', '**/*Test.kt'] OutdatedDocumentation: active: true matchTypeParameters: true matchDeclarationsOrder: true allowParamOnConstructorProperties: false UndocumentedPublicClass: active: false excludes: ['**/[Tt]est/**', '**/*Test.kt'] searchInNestedClass: true searchInInnerClass: true searchInInnerObject: true searchInInnerInterface: true searchInProtectedClass: false ignoreDefaultCompanionObject: false UndocumentedPublicFunction: active: false excludes: ['**/[Tt]est/**', '**/*Test.kt'] searchProtectedFunction: false UndocumentedPublicProperty: active: false excludes: ['**/[Tt]est/**', '**/*Test.kt'] searchProtectedProperty: false complexity: active: true CognitiveComplexMethod: active: true threshold: 15 ComplexCondition: active: true threshold: 4 ComplexInterface: active: true threshold: 10 includeStaticDeclarations: false includePrivateDeclarations: false ignoreOverloaded: false CyclomaticComplexMethod: active: true threshold: 15 ignoreSingleWhenExpression: false ignoreSimpleWhenEntries: false ignoreNestingFunctions: false nestingFunctions: - 'also' - 'apply' - 'forEach' - 'isNotNull' - 'ifNull' - 'let' - 'run' - 'use' - 'with' LabeledExpression: active: true ignoredLabels: [] LargeClass: active: true threshold: 600 LongMethod: active: true threshold: 60 LongParameterList: active: true functionThreshold: 6 constructorThreshold: 7 ignoreDefaultParameters: false ignoreDataClasses: true ignoreAnnotatedParameter: [] excludes: ['**/[Tt]est/**', '**/*Test.kt'] MethodOverloading: active: true threshold: 6 NamedArguments: active: true threshold: 3 ignoreArgumentsMatchingNames: false NestedBlockDepth: active: true threshold: 4 NestedScopeFunctions: active: true threshold: 1 functions: - 'kotlin.apply' - 'kotlin.run' - 'kotlin.with' - 'kotlin.let' - 'kotlin.also' ReplaceSafeCallChainWithRun: active: true StringLiteralDuplication: active: true excludes: ['**/[Tt]est/**', '**/*Test.kt'] threshold: 3 ignoreAnnotation: true excludeStringsWithLessThan5Characters: true ignoreStringsRegex: '$^' TooManyFunctions: active: true excludes: ['**/[Tt]est/**', '**/*Test.kt'] thresholdInFiles: 11 thresholdInClasses: 11 thresholdInInterfaces: 11 thresholdInObjects: 11 thresholdInEnums: 11 ignoreDeprecated: false ignorePrivate: false ignoreOverridden: false ignoreAnnotatedFunctions: [] coroutines: active: true GlobalCoroutineUsage: active: true InjectDispatcher: active: true dispatcherNames: - 'IO' - 'Default' - 'Unconfined' RedundantSuspendModifier: active: true SleepInsteadOfDelay: active: true SuspendFunSwallowedCancellation: active: true SuspendFunWithCoroutineScopeReceiver: active: true SuspendFunWithFlowReturnType: active: true empty-blocks: active: true EmptyCatchBlock: active: true allowedExceptionNameRegex: '_|(ignore|expected).*' EmptyClassBlock: active: true EmptyDefaultConstructor: active: true EmptyDoWhileBlock: active: true EmptyElseBlock: active: true EmptyFinallyBlock: active: true EmptyForBlock: active: true EmptyFunctionBlock: active: true ignoreOverridden: false EmptyIfBlock: active: true EmptyInitBlock: active: true EmptyKtFile: active: true EmptySecondaryConstructor: active: true EmptyTryBlock: active: true EmptyWhenBlock: active: true EmptyWhileBlock: active: true exceptions: active: true ExceptionRaisedInUnexpectedLocation: active: true methodNames: - 'equals' - 'finalize' - 'hashCode' - 'toString' InstanceOfCheckForException: active: true excludes: ['**/[Tt]est/**', '**/*Test.kt'] NotImplementedDeclaration: active: true ObjectExtendsThrowable: active: true PrintStackTrace: active: true RethrowCaughtException: active: true ReturnFromFinally: active: true ignoreLabeled: false SwallowedException: active: true ignoredExceptionTypes: - 'InterruptedException' - 'MalformedURLException' - 'NumberFormatException' - 'ParseException' allowedExceptionNameRegex: '_|(ignore|expected).*' ThrowingExceptionFromFinally: active: true ThrowingExceptionInMain: active: true ThrowingExceptionsWithoutMessageOrCause: active: true excludes: ['**/[Tt]est/**', '**/*Test.kt'] exceptions: - 'ArrayIndexOutOfBoundsException' - 'Exception' - 'IllegalArgumentException' - 'IllegalMonitorStateException' - 'IllegalStateException' - 'IndexOutOfBoundsException' - 'NullPointerException' - 'RuntimeException' - 'Throwable' ThrowingNewInstanceOfSameException: active: true TooGenericExceptionCaught: active: true excludes: ['**/[Tt]est/**', '**/*Test.kt'] exceptionNames: - 'ArrayIndexOutOfBoundsException' - 'Error' - 'Exception' - 'IllegalMonitorStateException' - 'IndexOutOfBoundsException' - 'NullPointerException' - 'RuntimeException' - 'Throwable' allowedExceptionNameRegex: '_|(ignore|expected).*' TooGenericExceptionThrown: active: true exceptionNames: - 'Error' - 'Exception' - 'RuntimeException' - 'Throwable' naming: active: true BooleanPropertyNaming: active: true allowedPattern: '^(is|has|are)' ClassNaming: active: true classPattern: '[A-Z][a-zA-Z0-9]*' ConstructorParameterNaming: active: true parameterPattern: '[a-z][A-Za-z0-9]*' privateParameterPattern: '[a-z][A-Za-z0-9]*' excludeClassPattern: '$^' EnumNaming: active: true enumEntryPattern: '[A-Z][_a-zA-Z0-9]*' ForbiddenClassName: active: true forbiddenName: [] FunctionMaxLength: active: false maximumFunctionNameLength: 50 excludes: ['**/[Tt]est/**', '**/*Test.kt'] FunctionMinLength: active: true minimumFunctionNameLength: 3 FunctionNaming: active: true excludes: ['**/[Tt]est/**', '**/*Test.kt'] functionPattern: '[a-z][a-zA-Z0-9]*' excludeClassPattern: '$^' ignoreAnnotated: ['Composable'] FunctionParameterNaming: active: true parameterPattern: '[a-z][A-Za-z0-9]*' excludeClassPattern: '$^' InvalidPackageDeclaration: active: true rootPackage: '' requireRootInDeclaration: false LambdaParameterNaming: active: true parameterPattern: '[a-z][A-Za-z0-9]*|_' MatchingDeclarationName: active: true mustBeFirst: true multiplatformTargets: - 'ios' - 'android' - 'js' - 'jvm' - 'native' - 'iosArm64' - 'iosX64' - 'macosX64' - 'mingwX64' - 'linuxX64' MemberNameEqualsClassName: active: true ignoreOverridden: true NoNameShadowing: active: true NonBooleanPropertyPrefixedWithIs: active: true ObjectPropertyNaming: active: true constantPattern: '[A-Za-z][_A-Za-z0-9]*' propertyPattern: '[A-Za-z][_A-Za-z0-9]*' privatePropertyPattern: '(_)?[A-Za-z][_A-Za-z0-9]*' PackageNaming: active: true packagePattern: '[a-z]+(\.[a-z][A-Za-z0-9]*)*' TopLevelPropertyNaming: active: true constantPattern: '[A-Z][_A-Z0-9]*' propertyPattern: '[A-Za-z][_A-Za-z0-9]*' privatePropertyPattern: '_?[A-Za-z][_A-Za-z0-9]*' VariableMaxLength: active: true maximumVariableNameLength: 64 VariableMinLength: active: true minimumVariableNameLength: 1 VariableNaming: active: true variablePattern: '[a-z][A-Za-z0-9]*' privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*' excludeClassPattern: '$^' performance: active: true ArrayPrimitive: active: true CouldBeSequence: active: true threshold: 3 ForEachOnRange: active: true excludes: ['**/[Tt]est/**', '**/*Test.kt'] SpreadOperator: active: true excludes: ['**/[Tt]est/**', '**/*Test.kt'] UnnecessaryPartOfBinaryExpression: active: true UnnecessaryTemporaryInstantiation: active: true potential-bugs: active: true AvoidReferentialEquality: active: true forbiddenTypePatterns: - 'kotlin.String' CastNullableToNonNullableType: active: true CastToNullableType: active: true Deprecation: active: true DontDowncastCollectionTypes: active: true DoubleMutabilityForCollection: active: true mutableTypes: - 'kotlin.collections.MutableList' - 'kotlin.collections.MutableMap' - 'kotlin.collections.MutableSet' - 'java.util.ArrayList' - 'java.util.LinkedHashSet' - 'java.util.HashSet' - 'java.util.LinkedHashMap' - 'java.util.HashMap' ElseCaseInsteadOfExhaustiveWhen: active: true ignoredSubjectTypes: [] EqualsAlwaysReturnsTrueOrFalse: active: true EqualsWithHashCodeExist: active: true ExitOutsideMain: active: true ExplicitGarbageCollectionCall: active: true HasPlatformType: active: true IgnoredReturnValue: active: true restrictToConfig: true returnValueAnnotations: - 'CheckResult' - '*.CheckResult' - 'CheckReturnValue' - '*.CheckReturnValue' ignoreReturnValueAnnotations: - 'CanIgnoreReturnValue' - '*.CanIgnoreReturnValue' returnValueTypes: - 'kotlin.sequences.Sequence' - 'kotlinx.coroutines.flow.*Flow' - 'java.util.stream.*Stream' ignoreFunctionCall: [] ImplicitDefaultLocale: active: true ImplicitUnitReturnType: active: true allowExplicitReturnType: true InvalidRange: active: true IteratorHasNextCallsNextMethod: active: true IteratorNotThrowingNoSuchElementException: active: true LateinitUsage: active: true excludes: ['**/[Tt]est/**', '**/*Test.kt'] ignoreOnClassesPattern: '' MapGetWithNotNullAssertionOperator: active: true MissingPackageDeclaration: active: true excludes: ['**/*.kts'] NullCheckOnMutableProperty: active: true NullableToStringCall: active: true PropertyUsedBeforeDeclaration: active: true UnconditionalJumpStatementInLoop: active: true UnnecessaryNotNullCheck: active: true UnnecessaryNotNullOperator: active: true UnnecessarySafeCall: active: true UnreachableCatchBlock: active: true UnreachableCode: active: true UnsafeCallOnNullableType: active: true excludes: ['**/[Tt]est/**', '**/*Test.kt'] UnsafeCast: active: true UnusedUnaryOperator: active: true UselessPostfixExpression: active: true WrongEqualsTypeParameter: active: true style: active: true AlsoCouldBeApply: active: true BracesOnIfStatements: active: true singleLine: 'never' multiLine: 'always' BracesOnWhenStatements: active: true singleLine: 'necessary' multiLine: 'consistent' CanBeNonNullable: active: true CascadingCallWrapping: active: false includeElvis: true ClassOrdering: active: true CollapsibleIfStatements: active: true DataClassContainsFunctions: active: false conversionFunctionPrefix: - 'to' allowOperators: false DataClassShouldBeImmutable: active: true DestructuringDeclarationWithTooManyEntries: active: true maxDestructuringEntries: 3 DoubleNegativeLambda: active: true negativeFunctions: - reason: 'Use `takeIf` instead.' value: 'takeUnless' - reason: 'Use `all` instead.' value: 'none' negativeFunctionNameParts: - 'not' - 'non' EqualsNullCall: active: true EqualsOnSignatureLine: active: true ExplicitCollectionElementAccessMethod: active: true ExplicitItLambdaParameter: active: true ExpressionBodySyntax: active: true includeLineWrapping: false ForbiddenAnnotation: active: true annotations: - reason: 'it is a java annotation. Use `Suppress` instead.' value: 'java.lang.SuppressWarnings' - reason: 'it is a java annotation. Use `kotlin.Deprecated` instead.' value: 'java.lang.Deprecated' - reason: 'it is a java annotation. Use `kotlin.annotation.MustBeDocumented` instead.' value: 'java.lang.annotation.Documented' - reason: 'it is a java annotation. Use `kotlin.annotation.Target` instead.' value: 'java.lang.annotation.Target' - reason: 'it is a java annotation. Use `kotlin.annotation.Retention` instead.' value: 'java.lang.annotation.Retention' - reason: 'it is a java annotation. Use `kotlin.annotation.Repeatable` instead.' value: 'java.lang.annotation.Repeatable' - reason: 'Kotlin does not support @Inherited annotation, see https://youtrack.jetbrains.com/issue/KT-22265' value: 'java.lang.annotation.Inherited' ForbiddenComment: active: true comments: - reason: 'Forbidden FIXME todo marker in comment, please fix the problem.' value: 'FIXME:' - reason: 'Forbidden STOPSHIP todo marker in comment, please address the problem before shipping the code.' value: 'STOPSHIP:' - reason: 'Forbidden TODO todo marker in comment, please do the changes.' value: 'TODO:' allowedPatterns: '' ForbiddenImport: active: true imports: [] forbiddenPatterns: '' ForbiddenMethodCall: active: true methods: - reason: 'print does not allow you to configure the output stream. Use a logger instead.' value: 'kotlin.io.print' - reason: 'println does not allow you to configure the output stream. Use a logger instead.' value: 'kotlin.io.println' ForbiddenSuppress: active: true rules: [] ForbiddenVoid: active: true ignoreOverridden: false ignoreUsageInGenerics: false FunctionOnlyReturningConstant: active: true ignoreOverridableFunction: true ignoreActualFunction: true excludedFunctions: [] LoopWithTooManyJumpStatements: active: true maxJumpCount: 1 MagicNumber: active: true excludes: ['**/[Tt]est/**', '**/*Test.kt'] ignoreNumbers: - '-1' - '0' - '1' - '2' - '60' # Common for seconds/minutes - '300' # Common for milliseconds delay - '1000' # Common for milliseconds - '3600' # Seconds in hour ignoreHashCodeFunction: true ignorePropertyDeclaration: false ignoreLocalVariableDeclaration: false ignoreConstantDeclaration: true ignoreCompanionObjectPropertyDeclaration: true ignoreAnnotation: false ignoreNamedArgument: true ignoreEnums: false ignoreRanges: false ignoreExtensionFunctions: true MandatoryBracesLoops: active: true MaxChainedCallsOnSameLine: active: true maxChainedCalls: 5 MaxLineLength: active: true maxLineLength: 140 excludePackageStatements: true excludeImportStatements: true excludeCommentStatements: false excludeRawStrings: true MayBeConst: active: true ModifierOrder: active: true MultilineLambdaItParameter: active: true MultilineRawStringIndentation: active: true indentSize: 4 trimmingMethods: - 'trimIndent' - 'trimMargin' NestedClassesVisibility: active: true NewLineAtEndOfFile: active: true NoTabs: active: true NullableBooleanCheck: active: true ObjectLiteralToLambda: active: true OptionalAbstractKeyword: active: true OptionalUnit: active: true PreferToOverPairSyntax: active: true ProtectedMemberInFinalClass: active: true RedundantExplicitType: active: true RedundantHigherOrderMapUsage: active: true RedundantVisibilityModifierRule: active: true ReturnCount: active: true max: 3 excludedFunctions: - 'equals' excludeLabeled: false excludeReturnFromLambda: true excludeGuardClauses: false SafeCast: active: true SerialVersionUIDInSerializableClass: active: true SpacingBetweenPackageAndImports: active: true StringShouldBeRawString: active: true maxEscapedCharacterCount: 2 ignoredCharacters: [] ThrowsCount: active: true max: 2 excludeGuardClauses: false TrailingWhitespace: active: true TrimMultilineRawString: active: true trimmingMethods: - 'trimIndent' - 'trimMargin' UnderscoresInNumericLiterals: active: true acceptableLength: 4 allowNonStandardGrouping: false UnnecessaryAbstractClass: active: true UnnecessaryAnnotationUseSiteTarget: active: true UnnecessaryApply: active: true UnnecessaryBackticks: active: true UnnecessaryBracesAroundTrailingLambda: active: true UnnecessaryFilter: active: true UnnecessaryInheritance: active: true UnnecessaryInnerClass: active: true UnnecessaryLet: active: true UnnecessaryParentheses: active: false allowForUnclearPrecedence: false UntilInsteadOfRangeTo: active: true UnusedImports: active: true UnusedParameter: active: true allowedNames: 'ignored|expected' UnusedPrivateClass: active: true UnusedPrivateMember: active: true allowedNames: '' ignoreAnnotated: ['Preview'] UnusedPrivateProperty: active: true allowedNames: '_|ignored|expected|serialVersionUID' UseAnyOrNoneInsteadOfFind: active: true UseArrayLiteralsInAnnotations: active: true UseCheckNotNull: active: true UseCheckOrError: active: true UseDataClass: active: true allowVars: false UseEmptyCounterpart: active: true UseIfEmptyOrIfBlank: active: true UseIfInsteadOfWhen: active: true ignoreWhenContainingVariableDeclaration: false UseIsNullOrEmpty: active: true UseLet: active: true UseOrEmpty: active: true UseRequire: active: true UseRequireNotNull: active: true UseSumOfInsteadOfFlatMapSize: active: true UselessCallOnNotNull: active: true UtilityClassWithPublicConstructor: active: true VarCouldBeVal: active: true ignoreLateinitVar: false WildcardImport: active: true excludeImports: - 'java.util.*' ================================================ FILE: feature/album/build.gradle.kts ================================================ plugins { id("com.igorwojda.showcase.convention.feature") } android { namespace = "com.igorwojda.showcase.feature.album" } ================================================ FILE: feature/album/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: feature/album/src/main/AndroidManifest.xml ================================================ ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/AlbumKoinModule.kt ================================================ package com.igorwojda.showcase.feature.album import com.igorwojda.showcase.feature.album.data.dataModule import com.igorwojda.showcase.feature.album.domain.domainModule import com.igorwojda.showcase.feature.album.presentation.presentationModule val featureAlbumModules = listOf( presentationModule, domainModule, dataModule, ) ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/data/DataModule.kt ================================================ package com.igorwojda.showcase.feature.album.data import androidx.room.Room import com.igorwojda.showcase.feature.album.data.datasource.api.service.AlbumRetrofitService import com.igorwojda.showcase.feature.album.data.datasource.database.AlbumDatabase import com.igorwojda.showcase.feature.album.data.mapper.AlbumMapper import com.igorwojda.showcase.feature.album.data.mapper.ImageMapper import com.igorwojda.showcase.feature.album.data.mapper.ImageSizeMapper import com.igorwojda.showcase.feature.album.data.mapper.TagMapper import com.igorwojda.showcase.feature.album.data.mapper.TrackMapper import com.igorwojda.showcase.feature.album.data.repository.AlbumRepositoryImpl import com.igorwojda.showcase.feature.album.domain.repository.AlbumRepository import org.koin.core.module.dsl.bind import org.koin.core.module.dsl.singleOf import org.koin.dsl.module import retrofit2.Retrofit internal val dataModule = module { singleOf(::AlbumRepositoryImpl) { bind() } single { get().create(AlbumRetrofitService::class.java) } single { Room .databaseBuilder( get(), AlbumDatabase::class.java, "Albums.db", ).build() } single { get().albums() } singleOf(::ImageSizeMapper) singleOf(::ImageMapper) singleOf(::TrackMapper) singleOf(::TagMapper) singleOf(::AlbumMapper) } ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/data/datasource/api/model/AlbumApiModel.kt ================================================ package com.igorwojda.showcase.feature.album.data.datasource.api.model import com.igorwojda.showcase.feature.album.data.datasource.database.model.AlbumRoomModel import com.igorwojda.showcase.feature.album.domain.model.Album import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable internal data class AlbumApiModel( @SerialName("mbid") val mbId: String? = null, @SerialName("name") val name: String, @SerialName("artist") val artist: String, @SerialName("image") val images: List? = null, @SerialName("tracks") val tracks: TrackListApiModel? = null, @SerialName("tags") val tags: TagListApiModel? = null, ) internal fun AlbumApiModel.toRoomModel() = AlbumRoomModel( mbId = this.mbId ?: "", name = this.name, artist = this.artist, images = this.images?.mapNotNull { it.toRoomModel() } ?: listOf(), tracks = this.tracks?.track?.map { it.toRoomModel() }, tags = this.tags?.tag?.map { it.toRoomModel() }, ) internal fun AlbumApiModel.toDomainModel(): Album { val images = this.images ?.filterNot { it.size == ImageSizeApiModel.UNKNOWN || it.url.isBlank() } ?.map { it.toDomainModel() } return Album( mbId = this.mbId, name = this.name, artist = this.artist, images = images ?: listOf(), tracks = this.tracks?.track?.map { it.toDomainModel() }, tags = this.tags?.tag?.map { it.toDomainModel() }, ) } ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/data/datasource/api/model/AlbumListApiModel.kt ================================================ package com.igorwojda.showcase.feature.album.data.datasource.api.model import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable internal data class AlbumListApiModel( @SerialName("album") val album: List, ) ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/data/datasource/api/model/ImageApiModel.kt ================================================ package com.igorwojda.showcase.feature.album.data.datasource.api.model import com.igorwojda.showcase.feature.album.data.datasource.database.model.ImageRoomModel import com.igorwojda.showcase.feature.album.domain.model.Image import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable internal data class ImageApiModel( @SerialName("#text") val url: String, @SerialName("size") val size: ImageSizeApiModel, ) internal fun ImageApiModel.toDomainModel() = Image( url = this.url, size = this.size.toDomainModel(), ) internal fun ImageApiModel.toRoomModel() = this.size.toRoomModel()?.let { ImageRoomModel(this.url, it) } ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/data/datasource/api/model/ImageSizeApiModel.kt ================================================ package com.igorwojda.showcase.feature.album.data.datasource.api.model import com.igorwojda.showcase.feature.album.data.datasource.database.model.ImageSizeRoomModel import com.igorwojda.showcase.feature.album.domain.enum.ImageSize import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable internal enum class ImageSizeApiModel { @SerialName("medium") MEDIUM, @SerialName("small") SMALL, @SerialName("large") LARGE, @SerialName("extralarge") EXTRA_LARGE, @SerialName("mega") MEGA, @SerialName("") UNKNOWN, } internal fun ImageSizeApiModel.toDomainModel() = ImageSize.valueOf(this.name) internal fun ImageSizeApiModel.toRoomModel(): ImageSizeRoomModel? = when (this) { ImageSizeApiModel.MEDIUM -> ImageSizeRoomModel.MEDIUM ImageSizeApiModel.SMALL -> ImageSizeRoomModel.SMALL ImageSizeApiModel.LARGE -> ImageSizeRoomModel.LARGE ImageSizeApiModel.EXTRA_LARGE -> ImageSizeRoomModel.EXTRA_LARGE ImageSizeApiModel.MEGA -> ImageSizeRoomModel.MEGA ImageSizeApiModel.UNKNOWN -> null } ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/data/datasource/api/model/SearchAlbumResultsApiModel.kt ================================================ package com.igorwojda.showcase.feature.album.data.datasource.api.model import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable internal data class SearchAlbumResultsApiModel( @SerialName("albummatches") val albumMatches: AlbumListApiModel, ) ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/data/datasource/api/model/TagApiModel.kt ================================================ package com.igorwojda.showcase.feature.album.data.datasource.api.model import com.igorwojda.showcase.feature.album.data.datasource.database.model.TagRoomModel import com.igorwojda.showcase.feature.album.domain.model.Tag import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable internal data class TagApiModel( @SerialName("name") val name: String, ) internal fun TagApiModel.toDomainModel() = Tag( name = this.name, ) internal fun TagApiModel.toRoomModel() = TagRoomModel( name = this.name, ) ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/data/datasource/api/model/TagListApiModel.kt ================================================ package com.igorwojda.showcase.feature.album.data.datasource.api.model import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable internal data class TagListApiModel( @SerialName("tag") val tag: List, ) ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/data/datasource/api/model/TrackApiModel.kt ================================================ package com.igorwojda.showcase.feature.album.data.datasource.api.model import com.igorwojda.showcase.feature.album.data.datasource.database.model.TrackRoomModel import com.igorwojda.showcase.feature.album.domain.model.Track import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable internal data class TrackApiModel( @SerialName("name") val name: String, @SerialName("duration") val duration: Int? = null, ) internal fun TrackApiModel.toDomainModel() = Track( name = this.name, duration = this.duration, ) internal fun TrackApiModel.toRoomModel() = TrackRoomModel( name = this.name, duration = this.duration, ) ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/data/datasource/api/model/TrackListApiModel.kt ================================================ package com.igorwojda.showcase.feature.album.data.datasource.api.model import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable internal data class TrackListApiModel( @SerialName("track") val track: List, ) ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/data/datasource/api/response/GetAlbumInfoResponse.kt ================================================ package com.igorwojda.showcase.feature.album.data.datasource.api.response import com.igorwojda.showcase.feature.album.data.datasource.api.model.AlbumApiModel import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable internal data class GetAlbumInfoResponse( @SerialName("album") val album: AlbumApiModel, ) ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/data/datasource/api/response/SearchAlbumResponse.kt ================================================ package com.igorwojda.showcase.feature.album.data.datasource.api.response import com.igorwojda.showcase.feature.album.data.datasource.api.model.SearchAlbumResultsApiModel import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable internal data class SearchAlbumResponse( @SerialName("results") val results: SearchAlbumResultsApiModel, ) ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/data/datasource/api/service/AlbumRetrofitService.kt ================================================ package com.igorwojda.showcase.feature.album.data.datasource.api.service import com.igorwojda.showcase.feature.album.data.datasource.api.response.GetAlbumInfoResponse import com.igorwojda.showcase.feature.album.data.datasource.api.response.SearchAlbumResponse import com.igorwojda.showcase.feature.base.data.retrofit.ApiResult import retrofit2.http.POST import retrofit2.http.Query internal interface AlbumRetrofitService { @POST("./?method=album.search") suspend fun searchAlbumAsync( @Query("album") phrase: String?, @Query("limit") limit: Int = 60, ): ApiResult @POST("./?method=album.getInfo") suspend fun getAlbumInfoAsync( @Query("artist") artistName: String, @Query("album") albumName: String, @Query("mbid") mbId: String?, ): ApiResult } ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/data/datasource/database/AlbumDao.kt ================================================ package com.igorwojda.showcase.feature.album.data.datasource.database import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query import com.igorwojda.showcase.feature.album.data.datasource.database.model.AlbumRoomModel @Dao internal interface AlbumDao { @Query("SELECT * FROM albums") suspend fun getAll(): List @Query("SELECT * FROM albums where artist = :artistName and name = :albumName and mbId = :mbId") suspend fun getAlbum( artistName: String, albumName: String, mbId: String?, ): AlbumRoomModel @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertAlbums(albums: List) } ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/data/datasource/database/AlbumDatabase.kt ================================================ package com.igorwojda.showcase.feature.album.data.datasource.database import androidx.room.Database import androidx.room.RoomDatabase import com.igorwojda.showcase.feature.album.data.datasource.database.model.AlbumRoomModel @Database(entities = [AlbumRoomModel::class], version = 1, exportSchema = false) internal abstract class AlbumDatabase : RoomDatabase() { abstract fun albums(): AlbumDao } ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/data/datasource/database/model/AlbumRoomModel.kt ================================================ package com.igorwojda.showcase.feature.album.data.datasource.database.model import androidx.room.Entity import androidx.room.PrimaryKey import androidx.room.TypeConverter import androidx.room.TypeConverters import com.igorwojda.showcase.feature.album.domain.model.Album import kotlinx.serialization.json.Json @Entity(tableName = "albums") @TypeConverters( AlbumImageRoomTypeConverter::class, AlbumTrackRoomTypeConverter::class, AlbumTagRoomTypeConverter::class, ) internal data class AlbumRoomModel( @PrimaryKey(autoGenerate = true) val id: Int = 0, val mbId: String, val name: String, val artist: String, val images: List = listOf(), val tracks: List?, val tags: List?, ) internal fun AlbumRoomModel.toDomainModel() = Album( this.name, this.artist, this.mbId, this.images.mapNotNull { it.toDomainModel() }, this.tracks?.map { it.toDomainModel() }, this.tags?.map { it.toDomainModel() }, ) internal class AlbumImageRoomTypeConverter { @TypeConverter fun stringToList(data: String?) = data?.let { Json.decodeFromString>(it) } ?: listOf() @TypeConverter fun listToString(someObjects: List): String = Json.encodeToString(someObjects) } internal class AlbumTrackRoomTypeConverter { @TypeConverter fun stringToList(data: String?) = data?.let { Json.decodeFromString>(it) } ?: listOf() @TypeConverter fun listToString(someObjects: List): String = Json.encodeToString(someObjects) } internal class AlbumTagRoomTypeConverter { @TypeConverter fun stringToList(data: String?) = data?.let { Json.decodeFromString>(it) } ?: listOf() @TypeConverter fun listToString(someObjects: List): String = Json.encodeToString(someObjects) } ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/data/datasource/database/model/ImageRoomModel.kt ================================================ package com.igorwojda.showcase.feature.album.data.datasource.database.model import com.igorwojda.showcase.feature.album.domain.model.Image import kotlinx.serialization.Serializable @Serializable internal data class ImageRoomModel( val url: String, val size: ImageSizeRoomModel, ) internal fun ImageRoomModel.toDomainModel() = this.size.toDomainModel()?.let { Image(this.url, it) } ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/data/datasource/database/model/ImageSizeRoomModel.kt ================================================ package com.igorwojda.showcase.feature.album.data.datasource.database.model import com.igorwojda.showcase.feature.album.domain.enum.ImageSize internal enum class ImageSizeRoomModel { MEDIUM, SMALL, LARGE, EXTRA_LARGE, MEGA, } internal fun ImageSizeRoomModel.toDomainModel(): ImageSize? = when (this) { ImageSizeRoomModel.MEDIUM -> ImageSize.MEDIUM ImageSizeRoomModel.SMALL -> ImageSize.SMALL ImageSizeRoomModel.LARGE -> ImageSize.LARGE ImageSizeRoomModel.EXTRA_LARGE -> ImageSize.EXTRA_LARGE ImageSizeRoomModel.MEGA -> ImageSize.MEGA } ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/data/datasource/database/model/TagRoomModel.kt ================================================ package com.igorwojda.showcase.feature.album.data.datasource.database.model import com.igorwojda.showcase.feature.album.domain.model.Tag import kotlinx.serialization.Serializable @Serializable internal data class TagRoomModel( val name: String, ) internal fun TagRoomModel.toDomainModel() = Tag( name = this.name, ) ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/data/datasource/database/model/TrackRoomModel.kt ================================================ package com.igorwojda.showcase.feature.album.data.datasource.database.model import com.igorwojda.showcase.feature.album.domain.model.Track import kotlinx.serialization.Serializable @Serializable internal data class TrackRoomModel( val name: String, val duration: Int? = null, ) internal fun TrackRoomModel.toDomainModel() = Track( name = this.name, duration = this.duration, ) ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/data/mapper/AlbumMapper.kt ================================================ package com.igorwojda.showcase.feature.album.data.mapper import com.igorwojda.showcase.feature.album.data.datasource.api.model.AlbumApiModel import com.igorwojda.showcase.feature.album.data.datasource.api.model.ImageSizeApiModel import com.igorwojda.showcase.feature.album.data.datasource.database.model.AlbumRoomModel import com.igorwojda.showcase.feature.album.domain.model.Album internal class AlbumMapper( private val imageMapper: ImageMapper, private val trackMapper: TrackMapper, private val tagMapper: TagMapper, ) { fun apiToRoom(apiModel: AlbumApiModel) = AlbumRoomModel( mbId = apiModel.mbId ?: "", name = apiModel.name, artist = apiModel.artist, images = apiModel.images?.mapNotNull { imageMapper.apiToRoom(it) } ?: listOf(), tracks = apiModel.tracks?.track?.map { trackMapper.apiToRoom(it) }, tags = apiModel.tags?.tag?.map { tagMapper.apiToRoom(it) }, ) fun apiToDomain(apiModel: AlbumApiModel): Album { val images = apiModel.images ?.filterNot { it.size == ImageSizeApiModel.UNKNOWN || it.url.isBlank() } ?.map { imageMapper.apiToDomain(it) } return Album( mbId = apiModel.mbId, name = apiModel.name, artist = apiModel.artist, images = images ?: listOf(), tracks = apiModel.tracks?.track?.map { trackMapper.apiToDomain(it) }, tags = apiModel.tags?.tag?.map { tagMapper.apiToDomain(it) }, ) } fun roomToDomain(roomModel: AlbumRoomModel) = Album( roomModel.name, roomModel.artist, roomModel.mbId, roomModel.images.mapNotNull { imageMapper.roomToDomain(it) }, roomModel.tracks?.map { trackMapper.roomToDomain(it) }, roomModel.tags?.map { tagMapper.roomToDomain(it) }, ) } ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/data/mapper/ImageMapper.kt ================================================ package com.igorwojda.showcase.feature.album.data.mapper import com.igorwojda.showcase.feature.album.data.datasource.api.model.ImageApiModel import com.igorwojda.showcase.feature.album.data.datasource.database.model.ImageRoomModel import com.igorwojda.showcase.feature.album.domain.model.Image internal class ImageMapper( private val imageSizeMapper: ImageSizeMapper, ) { fun apiToDomain(apiModel: ImageApiModel) = Image( url = apiModel.url, size = imageSizeMapper.apiToDomain(apiModel.size), ) fun apiToRoom(apiModel: ImageApiModel) = imageSizeMapper.apiToRoom(apiModel.size)?.let { ImageRoomModel(apiModel.url, it) } fun roomToDomain(roomModel: ImageRoomModel) = imageSizeMapper.roomToDomain(roomModel.size)?.let { Image(roomModel.url, it) } } ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/data/mapper/ImageSizeMapper.kt ================================================ package com.igorwojda.showcase.feature.album.data.mapper import com.igorwojda.showcase.feature.album.data.datasource.api.model.ImageSizeApiModel import com.igorwojda.showcase.feature.album.data.datasource.database.model.ImageSizeRoomModel import com.igorwojda.showcase.feature.album.domain.enum.ImageSize internal class ImageSizeMapper { fun apiToDomain(apiModel: ImageSizeApiModel): ImageSize = ImageSize.valueOf(apiModel.name) fun apiToRoom(apiModel: ImageSizeApiModel): ImageSizeRoomModel? = when (apiModel) { ImageSizeApiModel.MEDIUM -> ImageSizeRoomModel.MEDIUM ImageSizeApiModel.SMALL -> ImageSizeRoomModel.SMALL ImageSizeApiModel.LARGE -> ImageSizeRoomModel.LARGE ImageSizeApiModel.EXTRA_LARGE -> ImageSizeRoomModel.EXTRA_LARGE ImageSizeApiModel.MEGA -> ImageSizeRoomModel.MEGA ImageSizeApiModel.UNKNOWN -> null } fun roomToDomain(roomModel: ImageSizeRoomModel): ImageSize? = when (roomModel) { ImageSizeRoomModel.MEDIUM -> ImageSize.MEDIUM ImageSizeRoomModel.SMALL -> ImageSize.SMALL ImageSizeRoomModel.LARGE -> ImageSize.LARGE ImageSizeRoomModel.EXTRA_LARGE -> ImageSize.EXTRA_LARGE ImageSizeRoomModel.MEGA -> ImageSize.MEGA } } ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/data/mapper/TagMapper.kt ================================================ package com.igorwojda.showcase.feature.album.data.mapper import com.igorwojda.showcase.feature.album.data.datasource.api.model.TagApiModel import com.igorwojda.showcase.feature.album.data.datasource.database.model.TagRoomModel import com.igorwojda.showcase.feature.album.domain.model.Tag internal class TagMapper { fun apiToDomain(apiModel: TagApiModel): Tag = Tag( name = apiModel.name, ) fun apiToRoom(apiModel: TagApiModel): TagRoomModel = TagRoomModel( name = apiModel.name, ) fun roomToDomain(roomModel: TagRoomModel): Tag = Tag( name = roomModel.name, ) } ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/data/mapper/TrackMapper.kt ================================================ package com.igorwojda.showcase.feature.album.data.mapper import com.igorwojda.showcase.feature.album.data.datasource.api.model.TrackApiModel import com.igorwojda.showcase.feature.album.data.datasource.database.model.TrackRoomModel import com.igorwojda.showcase.feature.album.domain.model.Track internal class TrackMapper { fun apiToDomain(apiModel: TrackApiModel): Track = Track( name = apiModel.name, duration = apiModel.duration, ) fun apiToRoom(apiModel: TrackApiModel): TrackRoomModel = TrackRoomModel( name = apiModel.name, duration = apiModel.duration, ) fun roomToDomain(roomModel: TrackRoomModel): Track = Track( name = roomModel.name, duration = roomModel.duration, ) } ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/data/repository/AlbumRepositoryImpl.kt ================================================ package com.igorwojda.showcase.feature.album.data.repository import com.igorwojda.showcase.feature.album.data.datasource.api.service.AlbumRetrofitService import com.igorwojda.showcase.feature.album.data.datasource.database.AlbumDao import com.igorwojda.showcase.feature.album.data.mapper.AlbumMapper import com.igorwojda.showcase.feature.album.domain.model.Album import com.igorwojda.showcase.feature.album.domain.repository.AlbumRepository import com.igorwojda.showcase.feature.base.data.retrofit.ApiResult import com.igorwojda.showcase.feature.base.domain.result.Result import timber.log.Timber internal class AlbumRepositoryImpl( private val albumRetrofitService: AlbumRetrofitService, private val albumDao: AlbumDao, private val albumMapper: AlbumMapper, ) : AlbumRepository { override suspend fun searchAlbum(phrase: String?): Result> = when (val apiResult = albumRetrofitService.searchAlbumAsync(phrase)) { is ApiResult.Success -> { val albums = apiResult .data .results .albumMatches .album .also { albumsApiModels -> val albumsRoomModels = albumsApiModels.map { albumMapper.apiToRoom(it) } albumDao.insertAlbums(albumsRoomModels) }.map { albumMapper.apiToDomain(it) } Result.Success(albums) } is ApiResult.Error -> { Result.Failure() } is ApiResult.Exception -> { Timber.e(apiResult.throwable) val albums = albumDao .getAll() .map { albumMapper.roomToDomain(it) } Result.Success(albums) } } override suspend fun getAlbumInfo( artistName: String, albumName: String, mbId: String?, ): Result = when (val apiResult = albumRetrofitService.getAlbumInfoAsync(artistName, albumName, mbId)) { is ApiResult.Success -> { val album = apiResult .data .album .let { albumMapper.apiToDomain(it) } Result.Success(album) } is ApiResult.Error -> { Result.Failure() } is ApiResult.Exception -> { Timber.e(apiResult.throwable) val album = albumDao .getAlbum(artistName, albumName, mbId) .let { albumMapper.roomToDomain(it) } Result.Success(album) } } } ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/domain/DomainModule.kt ================================================ package com.igorwojda.showcase.feature.album.domain import com.igorwojda.showcase.feature.album.domain.usecase.GetAlbumListUseCase import com.igorwojda.showcase.feature.album.domain.usecase.GetAlbumUseCase import org.koin.core.module.dsl.singleOf import org.koin.dsl.module internal val domainModule = module { singleOf(::GetAlbumListUseCase) singleOf(::GetAlbumUseCase) } ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/domain/enum/ImageSize.kt ================================================ package com.igorwojda.showcase.feature.album.domain.enum internal enum class ImageSize { SMALL, MEDIUM, LARGE, EXTRA_LARGE, MEGA, } ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/domain/model/Album.kt ================================================ package com.igorwojda.showcase.feature.album.domain.model import com.igorwojda.showcase.feature.album.domain.enum.ImageSize // Images are loaded for both album list and album detail instance // Tracks and Tags are only loaded for album detail instance (not album list instance) internal data class Album( val name: String, val artist: String, val mbId: String? = null, val images: List = emptyList(), val tracks: List? = null, val tags: List? = null, ) { val id: String = "$artist - $name" fun getDefaultImageUrl() = images.firstOrNull { it.size == ImageSize.EXTRA_LARGE }?.url } ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/domain/model/Image.kt ================================================ package com.igorwojda.showcase.feature.album.domain.model import com.igorwojda.showcase.feature.album.domain.enum.ImageSize internal data class Image( val url: String, val size: ImageSize, ) ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/domain/model/Tag.kt ================================================ package com.igorwojda.showcase.feature.album.domain.model internal data class Tag( val name: String, ) ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/domain/model/Track.kt ================================================ package com.igorwojda.showcase.feature.album.domain.model internal data class Track( val name: String, val duration: Int? = null, ) ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/domain/repository/AlbumRepository.kt ================================================ package com.igorwojda.showcase.feature.album.domain.repository import com.igorwojda.showcase.feature.album.domain.model.Album import com.igorwojda.showcase.feature.base.domain.result.Result internal interface AlbumRepository { suspend fun getAlbumInfo( artistName: String, albumName: String, mbId: String?, ): Result suspend fun searchAlbum(phrase: String?): Result> } ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/domain/usecase/GetAlbumListUseCase.kt ================================================ package com.igorwojda.showcase.feature.album.domain.usecase import com.igorwojda.showcase.feature.album.domain.model.Album import com.igorwojda.showcase.feature.album.domain.repository.AlbumRepository import com.igorwojda.showcase.feature.base.domain.result.Result import com.igorwojda.showcase.feature.base.domain.result.mapSuccess internal class GetAlbumListUseCase( private val albumRepository: AlbumRepository, ) { suspend operator fun invoke(query: String?): Result> { val result = albumRepository .searchAlbum(query) .mapSuccess { val albumsWithImages = value.filter { it.getDefaultImageUrl() != null } copy(value = albumsWithImages) } return result } } ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/domain/usecase/GetAlbumUseCase.kt ================================================ package com.igorwojda.showcase.feature.album.domain.usecase import com.igorwojda.showcase.feature.album.domain.model.Album import com.igorwojda.showcase.feature.album.domain.repository.AlbumRepository import com.igorwojda.showcase.feature.base.domain.result.Result internal class GetAlbumUseCase( private val albumRepository: AlbumRepository, ) { suspend operator fun invoke( artistName: String, albumName: String, mbId: String?, ): Result = albumRepository.getAlbumInfo(artistName, albumName, mbId) } ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/presentation/PresentationModule.kt ================================================ package com.igorwojda.showcase.feature.album.presentation import coil.ImageLoader import com.igorwojda.showcase.feature.album.presentation.screen.albumdetail.AlbumDetailViewModel import com.igorwojda.showcase.feature.album.presentation.screen.albumlist.AlbumListViewModel import org.koin.core.module.dsl.singleOf import org.koin.core.module.dsl.viewModelOf import org.koin.dsl.module internal val presentationModule = module { // AlbumList viewModelOf(::AlbumListViewModel) singleOf(::ImageLoader) // AlbumDetails viewModelOf(::AlbumDetailViewModel) } ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/presentation/composable/SearchBarComposable.kt ================================================ package com.igorwojda.showcase.feature.album.presentation.composable import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Clear import androidx.compose.material.icons.filled.Search import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextFieldDefaults import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.tooling.preview.Preview import com.igorwojda.showcase.feature.album.R import com.igorwojda.showcase.feature.base.common.res.Dimen import kotlinx.coroutines.delay @Composable fun SearchBar( query: String, onQueryChange: (String) -> Unit, onSearch: (String) -> Unit, modifier: Modifier = Modifier, ) { val minimumProductQuerySize = 1 val delayBeforeSubmittingQuery = 300L var textFieldValue by remember(query) { mutableStateOf(TextFieldValue(query)) } // Debounce search - only trigger search after user stops typing LaunchedEffect(textFieldValue.text, onSearch, onQueryChange) { if (textFieldValue.text.length >= minimumProductQuerySize) { delay(delayBeforeSubmittingQuery) onSearch(textFieldValue.text) onQueryChange(textFieldValue.text) } else if (textFieldValue.text.isEmpty()) { // Immediately search when query is cleared onSearch("") onQueryChange("") } } OutlinedTextField( value = textFieldValue, modifier = modifier .fillMaxWidth() .padding(Dimen.spaceM), onValueChange = { newValue -> textFieldValue = newValue }, placeholder = { Text(stringResource(R.string.album_list_search_placeholder)) }, leadingIcon = { Icon( imageVector = Icons.Default.Search, contentDescription = "Search", ) }, trailingIcon = if (textFieldValue.text.isNotEmpty()) { { IconButton( onClick = { textFieldValue = TextFieldValue("") onSearch("") onQueryChange("") }, ) { Icon( imageVector = Icons.Default.Clear, contentDescription = "Clear search", ) } } } else { null }, singleLine = true, colors = OutlinedTextFieldDefaults.colors( unfocusedBorderColor = MaterialTheme.colorScheme.outline.copy(alpha = 0.5f), focusedBorderColor = MaterialTheme.colorScheme.primary, ), ) } @Preview @Composable private fun SearchBarPreview() { SearchBar( query = "Sample query", onQueryChange = { }, onSearch = { }, ) } @Preview @Composable private fun SearchBarEmptyPreview() { SearchBar( query = "", onQueryChange = { }, onSearch = { }, ) } ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/presentation/screen/albumdetail/AlbumDetailAction.kt ================================================ package com.igorwojda.showcase.feature.album.presentation.screen.albumdetail import com.igorwojda.showcase.feature.album.domain.model.Album import com.igorwojda.showcase.feature.base.presentation.viewmodel.BaseAction internal sealed interface AlbumDetailAction : BaseAction { object AlbumLoadStart : AlbumDetailAction { override fun reduce(state: AlbumDetailUiState) = AlbumDetailUiState.Loading } class AlbumLoadSuccess( private val album: Album, ) : AlbumDetailAction { override fun reduce(state: AlbumDetailUiState) = AlbumDetailUiState.Content( artistName = album.artist, albumName = album.name, coverImageUrl = album.getDefaultImageUrl() ?: "", tracks = album.tracks, tags = album.tags, ) } object AlbumLoadFailure : AlbumDetailAction { override fun reduce(state: AlbumDetailUiState) = AlbumDetailUiState.Error } } ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/presentation/screen/albumdetail/AlbumDetailScreen.kt ================================================ package com.igorwojda.showcase.feature.album.presentation.screen.albumdetail import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.outlined.Star import androidx.compose.material3.ElevatedCard import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Scaffold import androidx.compose.material3.SuggestionChip import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment.Companion.CenterHorizontally import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.igorwojda.showcase.feature.album.R import com.igorwojda.showcase.feature.album.domain.model.Tag import com.igorwojda.showcase.feature.album.domain.model.Track import com.igorwojda.showcase.feature.album.presentation.util.TimeUtil import com.igorwojda.showcase.feature.base.common.res.Dimen import com.igorwojda.showcase.feature.base.presentation.compose.composable.ErrorAnim import com.igorwojda.showcase.feature.base.presentation.compose.composable.LoadingIndicator import com.igorwojda.showcase.feature.base.presentation.compose.composable.PlaceholderImage import com.igorwojda.showcase.feature.base.presentation.compose.composable.TextTitleLarge import com.igorwojda.showcase.feature.base.presentation.compose.composable.TextTitleMedium import org.koin.androidx.compose.koinViewModel @OptIn(ExperimentalMaterial3Api::class) @Composable fun AlbumDetailScreen( albumName: String, artistName: String, albumMbId: String?, modifier: Modifier = Modifier, onBackClick: () -> Unit = {}, ) { val viewModel: AlbumDetailViewModel = koinViewModel() // Initialize the viewModel with args when the composable enters composition LaunchedEffect(Unit) { viewModel.onInit(albumName, artistName, albumMbId) } val uiState by viewModel.uiStateFlow.collectAsStateWithLifecycle() Scaffold( modifier = modifier, topBar = { TopAppBar( title = { Text(text = albumName) }, navigationIcon = { IconButton(onClick = onBackClick) { Icon( imageVector = Icons.AutoMirrored.Filled.ArrowBack, contentDescription = stringResource(R.string.album_detail_back), ) } }, ) }, ) { innerPadding -> when (val currentUiState = uiState) { AlbumDetailUiState.Error -> { ErrorAnim() } AlbumDetailUiState.Loading -> { LoadingIndicator() } is AlbumDetailUiState.Content -> { AlbumDetailContent( content = currentUiState, modifier = Modifier.padding(innerPadding), ) } } } } @Composable private fun AlbumDetailContent( content: AlbumDetailUiState.Content, modifier: Modifier = Modifier, ) { Column( modifier = modifier .padding(horizontal = Dimen.screenContentPadding) .verticalScroll(rememberScrollState()), ) { ElevatedCard( modifier = Modifier .padding(Dimen.spaceM) .wrapContentSize() .size(320.dp) .align(CenterHorizontally), ) { PlaceholderImage( url = content.coverImageUrl, contentDescription = stringResource(id = R.string.album_detail_cover_content_description), modifier = Modifier.fillMaxWidth(), ) } Spacer(modifier = Modifier.height(Dimen.spaceL)) TextTitleLarge(text = content.albumName) TextTitleMedium(text = content.artistName) Spacer(modifier = Modifier.height(Dimen.spaceL)) if (content.tags?.isNotEmpty() == true) { Tags(content.tags) Spacer(modifier = Modifier.height(Dimen.spaceL)) } if (content.tracks?.isNotEmpty() == true) { TextTitleMedium(text = stringResource(id = R.string.album_detail_tracks)) Spacer(modifier = Modifier.height(Dimen.spaceS)) Tracks(content.tracks) } } } @Composable private fun Tags(tags: List?) { FlowRow( horizontalArrangement = Arrangement.spacedBy(Dimen.spaceS), verticalArrangement = Arrangement.spacedBy(Dimen.spaceS), ) { tags?.forEach { tag -> SuggestionChip( onClick = { }, label = { Text(tag.name) }, ) } } } @Composable internal fun Tracks(tracks: List?) { tracks?.forEach { track -> TrackItem(track) } } @Composable internal fun TrackItem(track: Track) { Row { Icon(Icons.Outlined.Star, contentDescription = null) Spacer(modifier = Modifier.width(Dimen.spaceS)) val text = buildString { append(track.name) track.duration?.let { duration -> append(" ${TimeUtil.formatTime(duration)}") } } Text(text = text) } } @Preview @Composable private fun TrackItemPreview() { TrackItem( track = Track( name = "Sample Track", duration = 180, // 3 minutes in seconds ), ) } ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/presentation/screen/albumdetail/AlbumDetailUiState.kt ================================================ package com.igorwojda.showcase.feature.album.presentation.screen.albumdetail import androidx.compose.runtime.Immutable import com.igorwojda.showcase.feature.album.domain.model.Tag import com.igorwojda.showcase.feature.album.domain.model.Track import com.igorwojda.showcase.feature.base.presentation.viewmodel.BaseState @Immutable internal sealed interface AlbumDetailUiState : BaseState { @Immutable data class Content( val albumName: String = "", val artistName: String = "", val coverImageUrl: String = "", val tracks: List? = emptyList(), val tags: List? = emptyList(), ) : AlbumDetailUiState @Immutable data object Loading : AlbumDetailUiState @Immutable data object Error : AlbumDetailUiState } ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/presentation/screen/albumdetail/AlbumDetailViewModel.kt ================================================ package com.igorwojda.showcase.feature.album.presentation.screen.albumdetail import androidx.lifecycle.viewModelScope import com.igorwojda.showcase.feature.album.domain.usecase.GetAlbumUseCase import com.igorwojda.showcase.feature.base.domain.result.Result.Failure import com.igorwojda.showcase.feature.base.domain.result.Result.Success import com.igorwojda.showcase.feature.base.presentation.viewmodel.BaseViewModel import kotlinx.coroutines.launch internal class AlbumDetailViewModel( private val getAlbumUseCase: GetAlbumUseCase, ) : BaseViewModel(AlbumDetailUiState.Loading) { fun onInit( albumName: String, artistName: String, albumMbId: String?, ) { getAlbum(albumName, artistName, albumMbId) } private fun getAlbum( albumName: String, artistName: String, albumMbId: String?, ) { sendAction(AlbumDetailAction.AlbumLoadStart) viewModelScope.launch { getAlbumUseCase(artistName, albumName, albumMbId).also { when (it) { is Success -> { sendAction(AlbumDetailAction.AlbumLoadSuccess(it.value)) } is Failure -> { sendAction(AlbumDetailAction.AlbumLoadFailure) } } } } } } ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/presentation/screen/albumlist/AlbumListAction.kt ================================================ package com.igorwojda.showcase.feature.album.presentation.screen.albumlist import com.igorwojda.showcase.feature.album.domain.model.Album import com.igorwojda.showcase.feature.base.presentation.viewmodel.BaseAction internal sealed interface AlbumListAction : BaseAction { object AlbumListLoadStart : AlbumListAction { override fun reduce(state: AlbumListUiState) = AlbumListUiState.Loading } class AlbumListLoadSuccess( private val albums: List, ) : AlbumListAction { override fun reduce(state: AlbumListUiState) = AlbumListUiState.Content(albums) } object AlbumListLoadFailure : AlbumListAction { override fun reduce(state: AlbumListUiState) = AlbumListUiState.Error } } ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/presentation/screen/albumlist/AlbumListScreen.kt ================================================ package com.igorwojda.showcase.feature.album.presentation.screen.albumlist import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.items import androidx.compose.material3.ElevatedCard import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.igorwojda.showcase.feature.album.R import com.igorwojda.showcase.feature.album.domain.model.Album import com.igorwojda.showcase.feature.album.presentation.composable.SearchBar import com.igorwojda.showcase.feature.base.common.res.Dimen import com.igorwojda.showcase.feature.base.presentation.compose.composable.ErrorAnim import com.igorwojda.showcase.feature.base.presentation.compose.composable.LoadingIndicator import com.igorwojda.showcase.feature.base.presentation.compose.composable.PlaceholderImage import org.koin.androidx.compose.koinViewModel @Composable fun AlbumListScreen( modifier: Modifier = Modifier, onNavigateToAlbumDetail: ((artistName: String, albumName: String, albumMbId: String?) -> Unit)? = null, ) { val viewModel: AlbumListViewModel = koinViewModel() val uiState by viewModel.uiStateFlow.collectAsStateWithLifecycle() var searchQuery by remember { mutableStateOf("") } LaunchedEffect(Unit) { viewModel.onInit() } Column(modifier = modifier.fillMaxSize()) { // Search bar SearchBar( query = searchQuery, onQueryChange = { newQuery -> searchQuery = newQuery }, onSearch = { query -> if (query.isNotEmpty()) { viewModel.onInit(query) } else { viewModel.onInit() } }, ) // Content Box( modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center, ) { when (val currentUiState = uiState) { // Extract to local variable for smart casting AlbumListUiState.Error -> ErrorAnim() AlbumListUiState.Loading -> LoadingIndicator() is AlbumListUiState.Content -> AlbumListContent(currentUiState, onNavigateToAlbumDetail) } } } } @Composable private fun AlbumListContent( uiState: AlbumListUiState.Content, onNavigateToAlbumDetail: ((String, String, String?) -> Unit)?, ) { AlbumGrid( albums = uiState.albums, onAlbumClick = { album -> onNavigateToAlbumDetail?.invoke(album.artist, album.name, album.mbId) }, ) } @Composable private fun AlbumGrid( albums: List, onAlbumClick: (Album) -> Unit, ) { LazyVerticalGrid( columns = GridCells.Adaptive(Dimen.imageSize), ) { items(items = albums, key = { it.id }) { album -> ElevatedCard( modifier = Modifier .padding(Dimen.spaceS) .wrapContentSize(), onClick = { onAlbumClick(album) }, ) { PlaceholderImage( url = album.getDefaultImageUrl(), contentDescription = stringResource(id = R.string.album_detail_cover_content_description), modifier = Modifier.size(Dimen.imageSize), ) } } } } @Preview @Composable private fun AlbumGridPreview() { val sampleAlbums = listOf( Album( name = "Sample Album 1", artist = "Sample Artist", mbId = null, images = emptyList(), ), Album( name = "Sample Album 2", artist = "Sample Artist 2", mbId = null, images = emptyList(), ), ) AlbumGrid( albums = sampleAlbums, onAlbumClick = { }, ) } ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/presentation/screen/albumlist/AlbumListUiState.kt ================================================ package com.igorwojda.showcase.feature.album.presentation.screen.albumlist import androidx.compose.runtime.Immutable import com.igorwojda.showcase.feature.album.domain.model.Album import com.igorwojda.showcase.feature.base.presentation.viewmodel.BaseState @Immutable internal sealed interface AlbumListUiState : BaseState { @Immutable data class Content( val albums: List, ) : AlbumListUiState @Immutable data object Loading : AlbumListUiState @Immutable data object Error : AlbumListUiState } ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/presentation/screen/albumlist/AlbumListViewModel.kt ================================================ package com.igorwojda.showcase.feature.album.presentation.screen.albumlist import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import com.igorwojda.showcase.feature.album.domain.usecase.GetAlbumListUseCase import com.igorwojda.showcase.feature.base.domain.result.Result import com.igorwojda.showcase.feature.base.presentation.viewmodel.BaseViewModel import kotlinx.coroutines.Job import kotlinx.coroutines.launch internal class AlbumListViewModel( private val savedStateHandle: SavedStateHandle, private val getAlbumListUseCase: GetAlbumListUseCase, ) : BaseViewModel(AlbumListUiState.Loading) { private var job: Job? = null fun onInit(query: String? = (savedStateHandle[SAVED_QUERY_KEY] as? String) ?: DEFAULT_QUERY_NAME) { getAlbumList(query) } private fun getAlbumList(query: String?) { if (job != null) { job?.cancel() job = null } savedStateHandle[SAVED_QUERY_KEY] = query sendAction(AlbumListAction.AlbumListLoadStart) job = viewModelScope.launch { getAlbumListUseCase(query).also { result -> val albumListAction = when (result) { is Result.Success -> { AlbumListAction.AlbumListLoadSuccess(result.value) } is Result.Failure -> { AlbumListAction.AlbumListLoadFailure } } sendAction(albumListAction) } } } companion object { const val DEFAULT_QUERY_NAME = "Jackson" private const val SAVED_QUERY_KEY = "query" } } ================================================ FILE: feature/album/src/main/kotlin/com/igorwojda/showcase/feature/album/presentation/util/TimeUtil.kt ================================================ package com.igorwojda.showcase.feature.album.presentation.util object TimeUtil { /** * provides a String representation of the given time. * @return `seconds` in mm:ss format */ internal fun formatTime(seconds: Int): String { val secondsInMinute = 60 val secondsInHour = 3600 @Suppress("detekt.ImplicitDefaultLocale") return String.format("%02d:%02d", seconds % secondsInHour / secondsInMinute, seconds % secondsInMinute) } } ================================================ FILE: feature/album/src/main/res/values/strings.xml ================================================ Search Album Albums Search albums… Album Cover Tracks Back ================================================ FILE: feature/album/src/test/kotlin/com/igorwojda/showcase/feature/album/data/DataFixtures.kt ================================================ package com.igorwojda.showcase.feature.album.data import com.igorwojda.showcase.feature.album.data.datasource.api.model.AlbumApiModel import com.igorwojda.showcase.feature.album.data.datasource.api.model.AlbumListApiModel import com.igorwojda.showcase.feature.album.data.datasource.api.model.ImageApiModel import com.igorwojda.showcase.feature.album.data.datasource.api.model.ImageSizeApiModel import com.igorwojda.showcase.feature.album.data.datasource.api.model.SearchAlbumResultsApiModel import com.igorwojda.showcase.feature.album.data.datasource.api.model.TagApiModel import com.igorwojda.showcase.feature.album.data.datasource.api.model.TagListApiModel import com.igorwojda.showcase.feature.album.data.datasource.api.model.TrackApiModel import com.igorwojda.showcase.feature.album.data.datasource.api.model.TrackListApiModel import com.igorwojda.showcase.feature.album.data.datasource.api.response.SearchAlbumResponse import com.igorwojda.showcase.feature.album.data.datasource.database.model.AlbumRoomModel import com.igorwojda.showcase.feature.album.data.datasource.database.model.ImageRoomModel import com.igorwojda.showcase.feature.album.data.datasource.database.model.ImageSizeRoomModel import com.igorwojda.showcase.feature.album.data.datasource.database.model.TagRoomModel import com.igorwojda.showcase.feature.album.data.datasource.database.model.TrackRoomModel object DataFixtures { internal fun getAlbumsApiModel() = listOf( getAlbumApiModel("mbid1", "album1", "artist1"), ) internal fun getAlbumsRoomModels() = listOf( getAlbumRoomModel(1, "mbid1", "album1", "artist1"), getAlbumRoomModel(2, "mbid2", "album2", "artist2"), ) internal fun getAlbumApiModel( mbId: String = "mbId", name: String = "album", artist: String = "artist", images: List? = listOf(getImageModelApiModel()), tracks: TrackListApiModel = TrackListApiModel(getTrackModelApiModel()), tags: TagListApiModel = TagListApiModel(getTagModelApiModel()), ): AlbumApiModel = AlbumApiModel( mbId, name, artist, images, tracks, tags, ) internal fun getImageModelApiModel( url: String = "url_${ImageSizeApiModel.EXTRA_LARGE}", size: ImageSizeApiModel = ImageSizeApiModel.EXTRA_LARGE, ) = ImageApiModel(url, size) private fun getTrackModelApiModel( name: String = "track", duration: Int? = 12, ) = listOf(TrackApiModel(name, duration)) private fun getTagModelApiModel(name: String = "tag") = listOf(TagApiModel(name)) private fun getAlbumRoomModel( id: Int = 0, mbId: String = "mbId", name: String = "album", artist: String = "artist", images: List = listOf(getImageRoomModel()), tracks: List = listOf(getTrackRoomModel()), tags: List = listOf(getTagRoomModel()), ): AlbumRoomModel = AlbumRoomModel( id, mbId, name, artist, images, tracks, tags, ) private fun getImageRoomModel( url: String = "url_${ImageSizeApiModel.EXTRA_LARGE}", size: ImageSizeRoomModel = ImageSizeRoomModel.EXTRA_LARGE, ) = ImageRoomModel(url, size) private fun getTrackRoomModel( name: String = "track", duration: Int = 12, ) = TrackRoomModel(name, duration) private fun getTagRoomModel(name: String = "tag") = TagRoomModel(name) object ApiResponse { internal fun getSearchAlbum() = SearchAlbumResponse( SearchAlbumResultsApiModel( AlbumListApiModel(getAlbumsApiModel()), ), ) } } ================================================ FILE: feature/album/src/test/kotlin/com/igorwojda/showcase/feature/album/data/datasource/api/model/AlbumApiModelTest.kt ================================================ package com.igorwojda.showcase.feature.album.data.datasource.api.model import com.igorwojda.showcase.feature.album.data.DataFixtures import com.igorwojda.showcase.feature.album.domain.enum.ImageSize import com.igorwojda.showcase.feature.album.domain.model.Album import com.igorwojda.showcase.feature.album.domain.model.Tag import com.igorwojda.showcase.feature.album.domain.model.Track import org.amshove.kluent.shouldBeEqualTo import org.junit.jupiter.api.Test class AlbumApiModelTest { @Test fun `data model with full data maps to AlbumDomainModel`() { // given val sut = DataFixtures.getAlbumApiModel() // when val domainModel = sut.toDomainModel() // then domainModel shouldBeEqualTo Album( sut.name, sut.artist, sut.mbId, sut.images?.map { it.toDomainModel() } ?: listOf(), sut.tracks?.track?.map { it.toDomainModel() }, sut.tags?.tag?.map { it.toDomainModel() }, ) } @Test fun `data model with missing data maps to AlbumDomainModel`() { // given val sut = DataFixtures.getAlbumApiModel( images = emptyList(), ) // when val domainModel = sut.toDomainModel() // then domainModel shouldBeEqualTo Album( mbId = "mbId", name = "album", artist = "artist", images = emptyList(), tracks = listOf(Track("track", 12)), tags = listOf(Tag("tag")), ) } @Test fun `mapping filters out unknown size`() { // given val albumDataImages = listOf(ImageSizeApiModel.EXTRA_LARGE, ImageSizeApiModel.UNKNOWN) .map { DataFixtures.getImageModelApiModel(size = it) } val sut = DataFixtures.getAlbumApiModel(images = albumDataImages) // when val domainModel = sut.toDomainModel() // then domainModel.images.single { it.size == ImageSize.EXTRA_LARGE } } @Test fun `mapping filters out blank url`() { // given val images = listOf("", "url") .map { DataFixtures.getImageModelApiModel(url = it) } val sut = DataFixtures.getAlbumApiModel(images = images) // when val domainModel = sut.toDomainModel() // then domainModel.images.single { it.url == "url" } } } ================================================ FILE: feature/album/src/test/kotlin/com/igorwojda/showcase/feature/album/data/datasource/api/model/ImageApiModelTest.kt ================================================ package com.igorwojda.showcase.feature.album.data.datasource.api.model import com.igorwojda.showcase.feature.album.data.DataFixtures import com.igorwojda.showcase.feature.album.domain.model.Image import org.amshove.kluent.shouldBeEqualTo import org.amshove.kluent.shouldThrow import org.junit.jupiter.api.Test class ImageApiModelTest { @Test fun `map to AlbumWikiDomainModel`() { // given val url = "url" val size = ImageSizeApiModel.EXTRA_LARGE val sut = DataFixtures.getImageModelApiModel(url, size) // when val domainModel = sut.toDomainModel() // then domainModel shouldBeEqualTo Image(url, size.toDomainModel()) } @Test fun `crash when mapping unknown AlbumWikiDomainModel`() { // given val url = "url" val size = ImageSizeApiModel.UNKNOWN val sut = DataFixtures.getImageModelApiModel(url, size) // when val func = { sut.toDomainModel() } // then func shouldThrow IllegalArgumentException::class } } ================================================ FILE: feature/album/src/test/kotlin/com/igorwojda/showcase/feature/album/data/datasource/api/model/ImageSizeApiModelTest.kt ================================================ package com.igorwojda.showcase.feature.album.data.datasource.api.model import org.junit.jupiter.api.Test class ImageSizeApiModelTest { @Test fun `maps to AlbumDomainImageSize`() { // given val dataEnums = ImageSizeApiModel .entries .filterNot { it == ImageSizeApiModel.UNKNOWN } // when dataEnums.forEach { it.toDomainModel() } // then // no explicit check is required, because test will crash if any of // the costs in the enums can't be mapped to a domain enum } } ================================================ FILE: feature/album/src/test/kotlin/com/igorwojda/showcase/feature/album/data/mapper/AlbumMapperTest.kt ================================================ package com.igorwojda.showcase.feature.album.data.mapper import com.igorwojda.showcase.feature.album.data.datasource.api.model.AlbumApiModel import com.igorwojda.showcase.feature.album.data.datasource.api.model.ImageApiModel import com.igorwojda.showcase.feature.album.data.datasource.api.model.ImageSizeApiModel import com.igorwojda.showcase.feature.album.data.datasource.api.model.TagApiModel import com.igorwojda.showcase.feature.album.data.datasource.api.model.TagListApiModel import com.igorwojda.showcase.feature.album.data.datasource.api.model.TrackApiModel import com.igorwojda.showcase.feature.album.data.datasource.api.model.TrackListApiModel import com.igorwojda.showcase.feature.album.data.datasource.database.model.AlbumRoomModel import com.igorwojda.showcase.feature.album.data.datasource.database.model.ImageRoomModel import com.igorwojda.showcase.feature.album.data.datasource.database.model.ImageSizeRoomModel import com.igorwojda.showcase.feature.album.data.datasource.database.model.TagRoomModel import com.igorwojda.showcase.feature.album.data.datasource.database.model.TrackRoomModel import com.igorwojda.showcase.feature.album.domain.enum.ImageSize import com.igorwojda.showcase.feature.album.domain.model.Album import com.igorwojda.showcase.feature.album.domain.model.Image import com.igorwojda.showcase.feature.album.domain.model.Tag import com.igorwojda.showcase.feature.album.domain.model.Track import io.mockk.every import io.mockk.mockk import org.amshove.kluent.shouldBeEqualTo import org.junit.jupiter.api.Test class AlbumMapperTest { private val imageMapper: ImageMapper = mockk() private val trackMapper: TrackMapper = mockk() private val tagMapper: TagMapper = mockk() private val sut = AlbumMapper( imageMapper, trackMapper, tagMapper, ) @Test fun `apiToRoom maps album correctly`() { // given val apiModel = AlbumApiModel( mbId = "test-id", name = "Test Album", artist = "Test Artist", images = listOf( ImageApiModel("https://example.com/image.jpg", ImageSizeApiModel.LARGE), ), tracks = TrackListApiModel( listOf(TrackApiModel("Track 1", 180)), ), tags = TagListApiModel( listOf(TagApiModel("rock")), ), ) every { imageMapper.apiToRoom(any()) } returns ImageRoomModel("https://example.com/image.jpg", ImageSizeRoomModel.LARGE) every { trackMapper.apiToRoom(any()) } returns TrackRoomModel("Track 1", 180) every { tagMapper.apiToRoom(any()) } returns TagRoomModel("rock") // when val result = sut.apiToRoom(apiModel) // then result shouldBeEqualTo AlbumRoomModel( mbId = "test-id", name = "Test Album", artist = "Test Artist", images = listOf(ImageRoomModel("https://example.com/image.jpg", ImageSizeRoomModel.LARGE)), tracks = listOf(TrackRoomModel("Track 1", 180)), tags = listOf(TagRoomModel("rock")), ) } @Test fun `apiToDomain maps album correctly`() { // given val apiModel = AlbumApiModel( mbId = "test-id", name = "Test Album", artist = "Test Artist", images = listOf( ImageApiModel("https://example.com/image.jpg", ImageSizeApiModel.LARGE), ), tracks = TrackListApiModel( listOf(TrackApiModel("Track 1", 180)), ), tags = TagListApiModel( listOf(TagApiModel("rock")), ), ) every { imageMapper.apiToDomain(any()) } returns Image("https://example.com/image.jpg", ImageSize.LARGE) every { trackMapper.apiToDomain(any()) } returns Track("Track 1", 180) every { tagMapper.apiToDomain(any()) } returns Tag("rock") // when val result = sut.apiToDomain(apiModel) // then result shouldBeEqualTo Album( mbId = "test-id", name = "Test Album", artist = "Test Artist", images = listOf(Image("https://example.com/image.jpg", ImageSize.LARGE)), tracks = listOf(Track("Track 1", 180)), tags = listOf(Tag("rock")), ) } @Test fun `roomToDomain maps album correctly`() { // given val roomModel = AlbumRoomModel( id = 1, mbId = "test-id", name = "Test Album", artist = "Test Artist", images = listOf(ImageRoomModel("https://example.com/image.jpg", ImageSizeRoomModel.LARGE)), tracks = listOf(TrackRoomModel("Track 1", 180)), tags = listOf(TagRoomModel("rock")), ) every { imageMapper.roomToDomain(any()) } returns Image("https://example.com/image.jpg", ImageSize.LARGE) every { trackMapper.roomToDomain(any()) } returns Track("Track 1", 180) every { tagMapper.roomToDomain(any()) } returns Tag("rock") // when val result = sut.roomToDomain(roomModel) // then result shouldBeEqualTo Album( name = "Test Album", artist = "Test Artist", mbId = "test-id", images = listOf(Image("https://example.com/image.jpg", ImageSize.LARGE)), tracks = listOf(Track("Track 1", 180)), tags = listOf(Tag("rock")), ) } } ================================================ FILE: feature/album/src/test/kotlin/com/igorwojda/showcase/feature/album/data/mapper/ImageMapperTest.kt ================================================ package com.igorwojda.showcase.feature.album.data.mapper import com.igorwojda.showcase.feature.album.data.datasource.api.model.ImageApiModel import com.igorwojda.showcase.feature.album.data.datasource.api.model.ImageSizeApiModel import com.igorwojda.showcase.feature.album.data.datasource.database.model.ImageRoomModel import com.igorwojda.showcase.feature.album.data.datasource.database.model.ImageSizeRoomModel import com.igorwojda.showcase.feature.album.domain.enum.ImageSize import com.igorwojda.showcase.feature.album.domain.model.Image import io.mockk.every import io.mockk.mockk import org.amshove.kluent.shouldBeEqualTo import org.junit.jupiter.api.Test class ImageMapperTest { private val imageSizeMapper: ImageSizeMapper = mockk() private val sut = ImageMapper(imageSizeMapper) @Test fun `apiToDomain maps image correctly`() { // given val apiModel = ImageApiModel("https://example.com/image.jpg", ImageSizeApiModel.LARGE) every { imageSizeMapper.apiToDomain(ImageSizeApiModel.LARGE) } returns ImageSize.LARGE // when val result = sut.apiToDomain(apiModel) // then result shouldBeEqualTo Image("https://example.com/image.jpg", ImageSize.LARGE) } @Test fun `apiToRoom maps image correctly`() { // given val apiModel = ImageApiModel("https://example.com/image.jpg", ImageSizeApiModel.LARGE) every { imageSizeMapper.apiToRoom(ImageSizeApiModel.LARGE) } returns ImageSizeRoomModel.LARGE // when val result = sut.apiToRoom(apiModel) // then result shouldBeEqualTo ImageRoomModel("https://example.com/image.jpg", ImageSizeRoomModel.LARGE) } @Test fun `roomToDomain maps image correctly`() { // given val roomModel = ImageRoomModel("https://example.com/image.jpg", ImageSizeRoomModel.LARGE) every { imageSizeMapper.roomToDomain(ImageSizeRoomModel.LARGE) } returns ImageSize.LARGE // when val result = sut.roomToDomain(roomModel) // then result shouldBeEqualTo Image("https://example.com/image.jpg", ImageSize.LARGE) } } ================================================ FILE: feature/album/src/test/kotlin/com/igorwojda/showcase/feature/album/data/mapper/ImageSizeMapperTest.kt ================================================ package com.igorwojda.showcase.feature.album.data.mapper import com.igorwojda.showcase.feature.album.data.datasource.api.model.ImageSizeApiModel import com.igorwojda.showcase.feature.album.data.datasource.database.model.ImageSizeRoomModel import com.igorwojda.showcase.feature.album.domain.enum.ImageSize import org.amshove.kluent.shouldBeEqualTo import org.junit.jupiter.api.Test class ImageSizeMapperTest { private val sut = ImageSizeMapper() @Test fun `apiToDomain maps image size correctly`() { // when & then sut.apiToDomain(ImageSizeApiModel.LARGE) shouldBeEqualTo ImageSize.LARGE sut.apiToDomain(ImageSizeApiModel.MEDIUM) shouldBeEqualTo ImageSize.MEDIUM sut.apiToDomain(ImageSizeApiModel.SMALL) shouldBeEqualTo ImageSize.SMALL } @Test fun `apiToRoom maps image size correctly`() { // when & then sut.apiToRoom(ImageSizeApiModel.LARGE) shouldBeEqualTo ImageSizeRoomModel.LARGE sut.apiToRoom(ImageSizeApiModel.MEDIUM) shouldBeEqualTo ImageSizeRoomModel.MEDIUM sut.apiToRoom(ImageSizeApiModel.SMALL) shouldBeEqualTo ImageSizeRoomModel.SMALL } @Test fun `roomToDomain maps image size correctly`() { // when & then sut.roomToDomain(ImageSizeRoomModel.LARGE) shouldBeEqualTo ImageSize.LARGE sut.roomToDomain(ImageSizeRoomModel.MEDIUM) shouldBeEqualTo ImageSize.MEDIUM sut.roomToDomain(ImageSizeRoomModel.SMALL) shouldBeEqualTo ImageSize.SMALL } } ================================================ FILE: feature/album/src/test/kotlin/com/igorwojda/showcase/feature/album/data/mapper/TagMapperTest.kt ================================================ package com.igorwojda.showcase.feature.album.data.mapper import com.igorwojda.showcase.feature.album.data.datasource.api.model.TagApiModel import com.igorwojda.showcase.feature.album.data.datasource.database.model.TagRoomModel import com.igorwojda.showcase.feature.album.domain.model.Tag import org.amshove.kluent.shouldBeEqualTo import org.junit.jupiter.api.Test class TagMapperTest { private val sut = TagMapper() @Test fun `apiToDomain maps tag correctly`() { // given val apiModel = TagApiModel("rock") // when val result = sut.apiToDomain(apiModel) // then result shouldBeEqualTo Tag("rock") } @Test fun `apiToRoom maps tag correctly`() { // given val apiModel = TagApiModel("rock") // when val result = sut.apiToRoom(apiModel) // then result shouldBeEqualTo TagRoomModel("rock") } @Test fun `roomToDomain maps tag correctly`() { // given val roomModel = TagRoomModel("rock") // when val result = sut.roomToDomain(roomModel) // then result shouldBeEqualTo Tag("rock") } } ================================================ FILE: feature/album/src/test/kotlin/com/igorwojda/showcase/feature/album/data/mapper/TrackMapperTest.kt ================================================ package com.igorwojda.showcase.feature.album.data.mapper import com.igorwojda.showcase.feature.album.data.datasource.api.model.TrackApiModel import com.igorwojda.showcase.feature.album.data.datasource.database.model.TrackRoomModel import com.igorwojda.showcase.feature.album.domain.model.Track import org.amshove.kluent.shouldBeEqualTo import org.junit.jupiter.api.Test class TrackMapperTest { private val trackMapper = TrackMapper() @Test fun `apiToDomain maps track correctly`() { // given val apiModel = TrackApiModel("Test Track", 180) // when val result = trackMapper.apiToDomain(apiModel) // then result shouldBeEqualTo Track("Test Track", 180) } @Test fun `apiToRoom maps track correctly`() { // given val apiModel = TrackApiModel("Test Track", 180) // when val result = trackMapper.apiToRoom(apiModel) // then result shouldBeEqualTo TrackRoomModel("Test Track", 180) } @Test fun `roomToDomain maps track correctly`() { // given val roomModel = TrackRoomModel("Test Track", 180) // when val result = trackMapper.roomToDomain(roomModel) // then result shouldBeEqualTo Track("Test Track", 180) } } ================================================ FILE: feature/album/src/test/kotlin/com/igorwojda/showcase/feature/album/data/repository/AlbumRepositoryImplTest.kt ================================================ package com.igorwojda.showcase.feature.album.data.repository import com.igorwojda.showcase.feature.album.data.DataFixtures import com.igorwojda.showcase.feature.album.data.datasource.api.response.GetAlbumInfoResponse import com.igorwojda.showcase.feature.album.data.datasource.api.response.SearchAlbumResponse import com.igorwojda.showcase.feature.album.data.datasource.api.service.AlbumRetrofitService import com.igorwojda.showcase.feature.album.data.datasource.database.AlbumDao import com.igorwojda.showcase.feature.album.data.mapper.AlbumMapper import com.igorwojda.showcase.feature.album.domain.model.Album import com.igorwojda.showcase.feature.base.data.retrofit.ApiResult import com.igorwojda.showcase.feature.base.domain.result.Result import io.mockk.coEvery import io.mockk.coVerify import io.mockk.every import io.mockk.mockk import kotlinx.coroutines.runBlocking import org.amshove.kluent.shouldBeEqualTo import org.junit.jupiter.api.Test import java.net.UnknownHostException class AlbumRepositoryImplTest { private val mockService: AlbumRetrofitService = mockk() private val mockAlbumDao: AlbumDao = mockk(relaxed = true) private val mockAlbumMapper: AlbumMapper = mockk() private val sut = AlbumRepositoryImpl(mockService, mockAlbumDao, mockAlbumMapper) @Test fun `searchAlbum handles api success and returns albums`() { // given val phrase = "phrase" val mockAlbum = mockk() coEvery { mockService.searchAlbumAsync(phrase) } returns ApiResult.Success( DataFixtures.ApiResponse.getSearchAlbum(), ) every { mockAlbumMapper.apiToRoom(any()) } returns mockk() every { mockAlbumMapper.apiToDomain(any()) } returns mockAlbum // when val actual = runBlocking { sut.searchAlbum(phrase) } // then actual shouldBeEqualTo Result.Success(listOf(mockAlbum)) } @Test fun `searchAlbum handles api success and saves album in database`() { // given val phrase = "phrase" coEvery { mockService.searchAlbumAsync(phrase) } returns ApiResult.Success( DataFixtures.ApiResponse.getSearchAlbum(), ) every { mockAlbumMapper.apiToRoom(any()) } returns mockk() every { mockAlbumMapper.apiToDomain(any()) } returns mockk() // when runBlocking { sut.searchAlbum(phrase) } // then coVerify { mockAlbumDao.insertAlbums(any()) } } @Test fun `searchAlbum handles api exception and fallbacks to database`() { // given val phrase = "phrase" val albumRoomModels = DataFixtures.getAlbumsRoomModels() val mockAlbum1 = mockk() val mockAlbum2 = mockk() coEvery { mockService.searchAlbumAsync(phrase) } returns ApiResult.Exception(UnknownHostException()) coEvery { mockAlbumDao.getAll() } returns albumRoomModels every { mockAlbumMapper.roomToDomain(albumRoomModels[0]) } returns mockAlbum1 every { mockAlbumMapper.roomToDomain(albumRoomModels[1]) } returns mockAlbum2 // when val actual = runBlocking { sut.searchAlbum(phrase) } // then actual shouldBeEqualTo Result.Success(listOf(mockAlbum1, mockAlbum2)) } @Test fun `searchAlbum handles api error `() { // given val phrase = "phrase" coEvery { mockService.searchAlbumAsync(phrase) } returns mockk>() // when val actual = runBlocking { sut.searchAlbum(phrase) } // then actual shouldBeEqualTo Result.Failure() } @Test fun `getAlbumInfo handles api success and returns Album`() { // given val artistName = "Michael Jackson" val albumName = "Thriller" val mbId = "123" val album = DataFixtures.getAlbumApiModel(mbId, albumName, artistName) val mockAlbum = mockk() coEvery { mockService.getAlbumInfoAsync(artistName, albumName, mbId) } returns ApiResult.Success( GetAlbumInfoResponse(album), ) every { mockAlbumMapper.apiToDomain(album) } returns mockAlbum // when val actual = runBlocking { sut.getAlbumInfo(artistName, albumName, mbId) } // then actual shouldBeEqualTo Result.Success(mockAlbum) } @Test fun `getAlbumInfo handles api exception and fallbacks to database`() { // given val artistName = "Michael Jackson" val albumName = "Thriller" val mbId = "123" coEvery { mockService.getAlbumInfoAsync(artistName, albumName, mbId) } returns ApiResult.Exception(UnknownHostException()) coEvery { mockAlbumDao.getAlbum(artistName, albumName, mbId) } returns mockk() every { mockAlbumMapper.roomToDomain(any()) } returns mockk() // when runBlocking { sut.getAlbumInfo(artistName, albumName, mbId) } // then coVerify { mockAlbumDao.getAlbum(artistName, albumName, mbId) } } @Test fun `getAlbumInfo handles api error`() { // given val artistName = "Michael Jackson" val albumName = "Thriller" val mbId = "123" coEvery { mockService.getAlbumInfoAsync(artistName, albumName, mbId) } returns mockk>() // when val actual = runBlocking { sut.getAlbumInfo(artistName, albumName, mbId) } // then actual shouldBeEqualTo Result.Failure() } } ================================================ FILE: feature/album/src/test/kotlin/com/igorwojda/showcase/feature/album/domain/DomainFixtures.kt ================================================ package com.igorwojda.showcase.feature.album.domain import com.igorwojda.showcase.feature.album.domain.enum.ImageSize import com.igorwojda.showcase.feature.album.domain.model.Album import com.igorwojda.showcase.feature.album.domain.model.Image import com.igorwojda.showcase.feature.album.domain.model.Tag import com.igorwojda.showcase.feature.album.domain.model.Track object DomainFixtures { internal fun getAlbum( name: String = "albumName", artist: String = "artistName", mbId: String? = "mbId", images: List = listOf(getImage()), tracks: List = listOf(getTrack()), tags: List = listOf(getTag()), ): Album = Album(name, artist, mbId, images, tracks, tags) internal fun getImage( url: String = "url_${ImageSize.EXTRA_LARGE}", size: ImageSize = ImageSize.EXTRA_LARGE, ) = Image(url, size) private fun getTrack( name: String = "track", duration: Int = 12, ) = Track(name, duration) private fun getTag(name: String = "tag") = Tag(name) } ================================================ FILE: feature/album/src/test/kotlin/com/igorwojda/showcase/feature/album/domain/model/AlbumTest.kt ================================================ package com.igorwojda.showcase.feature.album.domain.model import com.igorwojda.showcase.feature.album.domain.DomainFixtures import org.amshove.kluent.shouldBeEqualTo import org.junit.jupiter.api.Test class AlbumTest { private lateinit var sut: Album @Test fun `get default image url`() { // given val image = DomainFixtures.getImage() // when sut = DomainFixtures.getAlbum(images = listOf(image)) // then sut.getDefaultImageUrl() shouldBeEqualTo image.url } @Test fun `get null default image url`() { // given sut = DomainFixtures.getAlbum(images = listOf()) // then sut.getDefaultImageUrl() shouldBeEqualTo null } } ================================================ FILE: feature/album/src/test/kotlin/com/igorwojda/showcase/feature/album/domain/usecase/GetAlbumListUseCaseTest.kt ================================================ package com.igorwojda.showcase.feature.album.domain.usecase import com.igorwojda.showcase.feature.album.data.repository.AlbumRepositoryImpl import com.igorwojda.showcase.feature.album.domain.DomainFixtures import com.igorwojda.showcase.feature.base.domain.result.Result import io.mockk.coEvery import io.mockk.coVerify import io.mockk.mockk import kotlinx.coroutines.runBlocking import org.amshove.kluent.shouldBeEqualTo import org.junit.jupiter.api.Test class GetAlbumListUseCaseTest { private val mockAlbumRepository: AlbumRepositoryImpl = mockk() private val sut = GetAlbumListUseCase(mockAlbumRepository) @Test fun `return list of albums`() { // given val albums = listOf(DomainFixtures.getAlbum(), DomainFixtures.getAlbum()) coEvery { mockAlbumRepository.searchAlbum(any()) } returns Result.Success(albums) // when val actual = runBlocking { sut(null) } // then actual shouldBeEqualTo Result.Success(albums) } @Test fun `WHEN onInit is called with no value then the default query search term is null`() = runBlocking { // given val albums = listOf(DomainFixtures.getAlbum(), DomainFixtures.getAlbum()) coEvery { mockAlbumRepository.searchAlbum(any()) } returns Result.Success(albums) sut(null) coVerify { mockAlbumRepository.searchAlbum(null) } } @Test fun `filter albums with default image`() { // given val albumWithImage = DomainFixtures.getAlbum() val albumWithoutImage = DomainFixtures.getAlbum(images = listOf()) val albums = listOf(albumWithImage, albumWithoutImage) coEvery { mockAlbumRepository.searchAlbum(any()) } returns Result.Success(albums) // when val actual = runBlocking { sut(null) } // then actual shouldBeEqualTo Result.Success(listOf(albumWithImage)) } @Test fun `return error when repository throws an exception`() { // given val resultFailure = mockk() coEvery { mockAlbumRepository.searchAlbum(any()) } returns resultFailure // when val actual = runBlocking { sut(null) } // then actual shouldBeEqualTo resultFailure } } ================================================ FILE: feature/album/src/test/kotlin/com/igorwojda/showcase/feature/album/domain/usecase/GetAlbumUseCaseTest.kt ================================================ package com.igorwojda.showcase.feature.album.domain.usecase import com.igorwojda.showcase.feature.album.data.repository.AlbumRepositoryImpl import com.igorwojda.showcase.feature.album.domain.model.Album import com.igorwojda.showcase.feature.base.domain.result.Result import io.mockk.coEvery import io.mockk.coVerify import io.mockk.mockk import kotlinx.coroutines.runBlocking import org.amshove.kluent.shouldBeEqualTo import org.junit.jupiter.api.Test class GetAlbumUseCaseTest { private val mockAlbumRepository: AlbumRepositoryImpl = mockk() private val sut = GetAlbumUseCase(mockAlbumRepository) @Test fun `return album`() { // given val albumName = "Thriller" val artistName = "Michael Jackson" val mbId = "123" val album = mockk() coEvery { mockAlbumRepository.getAlbumInfo(artistName, albumName, mbId) } answers { Result.Success(album) } // when val actual = runBlocking { sut(artistName, albumName, mbId) } // then actual shouldBeEqualTo Result.Success(album) } @Test fun `return error`() { // given val albumName = "Thriller" val artistName = "Michael Jackson" val mbId = "123" val resultFailure = mockk() coEvery { mockAlbumRepository.getAlbumInfo(artistName, albumName, mbId) } returns resultFailure // when val actual = runBlocking { sut(artistName, albumName, mbId) } // then coVerify { mockAlbumRepository.getAlbumInfo(artistName, albumName, mbId) } actual shouldBeEqualTo resultFailure } } ================================================ FILE: feature/album/src/test/kotlin/com/igorwojda/showcase/feature/album/presentation/screen/albumdetail/AlbumDetailViewModelTest.kt ================================================ package com.igorwojda.showcase.feature.album.presentation.screen.albumdetail import com.igorwojda.showcase.feature.album.domain.model.Album import com.igorwojda.showcase.feature.album.domain.usecase.GetAlbumUseCase import com.igorwojda.showcase.feature.base.domain.result.Result import com.igorwojda.showcase.library.testutils.CoroutinesTestDispatcherExtension import com.igorwojda.showcase.library.testutils.InstantTaskExecutorExtension import io.mockk.coEvery import io.mockk.mockk import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest import org.amshove.kluent.shouldBeEqualTo import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(InstantTaskExecutorExtension::class, CoroutinesTestDispatcherExtension::class) class AlbumDetailViewModelTest { private val mockGetAlbumUseCase: GetAlbumUseCase = mockk() private val sut = AlbumDetailViewModel( mockGetAlbumUseCase, ) @Test fun `onInit album is not found`() = runTest { // given val albumName = "Thriller" val artistName = "Michael Jackson" val mbId = "123" coEvery { mockGetAlbumUseCase.invoke(artistName, albumName, mbId) } returns Result.Failure() // when sut.onInit(albumName, artistName, mbId) // then advanceUntilIdle() sut.uiStateFlow.value shouldBeEqualTo AlbumDetailUiState.Error } @Test fun `onInit album is found`() = runTest { // given val albumName = "Thriller" val artistName = "Michael Jackson" val mbId = "123" val album = Album(albumName, artistName, mbId) coEvery { mockGetAlbumUseCase.invoke(artistName, albumName, mbId) } returns Result.Success(album) // when sut.onInit(albumName, artistName, mbId) // then advanceUntilIdle() sut.uiStateFlow.value shouldBeEqualTo AlbumDetailUiState.Content( albumName = albumName, artistName = artistName, coverImageUrl = "", tracks = null, tags = null, ) } } ================================================ FILE: feature/album/src/test/kotlin/com/igorwojda/showcase/feature/album/presentation/screen/albumlist/AlbumListViewModelTest.kt ================================================ package com.igorwojda.showcase.feature.album.presentation.screen.albumlist import androidx.lifecycle.SavedStateHandle import com.igorwojda.showcase.feature.album.domain.model.Album import com.igorwojda.showcase.feature.album.domain.usecase.GetAlbumListUseCase import com.igorwojda.showcase.feature.base.domain.result.Result import com.igorwojda.showcase.library.testutils.CoroutinesTestDispatcherExtension import com.igorwojda.showcase.library.testutils.InstantTaskExecutorExtension import io.mockk.coEvery import io.mockk.mockk import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest import org.amshove.kluent.shouldBeEqualTo import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(InstantTaskExecutorExtension::class, CoroutinesTestDispatcherExtension::class) class AlbumListViewModelTest { private val mockGetAlbumListUseCase: GetAlbumListUseCase = mockk() private val savedStateHandle: SavedStateHandle = mockk(relaxed = true) private val sut = AlbumListViewModel( savedStateHandle, mockGetAlbumListUseCase, ) @Test fun `onInit emits state error`() = runTest { // given coEvery { mockGetAlbumListUseCase.invoke("Jackson") } returns Result.Failure() // when sut.onInit("Jackson") // then advanceUntilIdle() sut.uiStateFlow.value shouldBeEqualTo AlbumListUiState.Error } @Test fun `onInit emits state success`() = runTest { // given val album = Album("albumName", "artistName") val albums = listOf(album) coEvery { mockGetAlbumListUseCase.invoke("Jackson") } returns Result.Success(albums) // when sut.onInit("Jackson") // then advanceUntilIdle() sut.uiStateFlow.value shouldBeEqualTo AlbumListUiState.Content( albums = albums, ) } } ================================================ FILE: feature/base/build.gradle.kts ================================================ plugins { id("com.igorwojda.showcase.convention.feature") } android { namespace = "com.igorwojda.showcase.feature.base" } ================================================ FILE: feature/base/consumer-rules.pro ================================================ ================================================ FILE: feature/base/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: feature/base/src/main/AndroidManifest.xml ================================================ ================================================ FILE: feature/base/src/main/kotlin/com/igorwojda/showcase/feature/base/common/delegate/Observer.kt ================================================ package com.igorwojda.showcase.feature.base.common.delegate import kotlin.properties.ObservableProperty import kotlin.properties.ReadWriteProperty import kotlin.reflect.KProperty inline fun observer( initialValue: T, crossinline onChange: (newValue: T) -> Unit, ): ReadWriteProperty = object : ObservableProperty(initialValue) { override fun afterChange( property: KProperty<*>, oldValue: T, newValue: T, ) = onChange(newValue) } ================================================ FILE: feature/base/src/main/kotlin/com/igorwojda/showcase/feature/base/common/res/Dimen.kt ================================================ package com.igorwojda.showcase.feature.base.common.res import androidx.compose.ui.unit.dp object Dimen { val spaceS = 4.dp val spaceM = 8.dp val spaceL = 16.dp val spaceXL = 32.dp val spaceXXL = 64.dp val screenContentPadding = spaceL val imageSize = 100.dp } ================================================ FILE: feature/base/src/main/kotlin/com/igorwojda/showcase/feature/base/data/retrofit/ApiResult.kt ================================================ package com.igorwojda.showcase.feature.base.data.retrofit sealed interface ApiResult { /** * Represents a network result that successfully received a response containing body data. */ data class Success( val data: T, ) : ApiResult /** * Represents a network result that successfully received a response containing an error message. */ data class Error( val code: Int, val message: String?, ) : ApiResult /** * Represents a network result that faced an unexpected exception before getting a response * from the network such as IOException and UnKnownHostException. */ data class Exception( val throwable: Throwable, ) : ApiResult } ================================================ FILE: feature/base/src/main/kotlin/com/igorwojda/showcase/feature/base/data/retrofit/ApiResultAdapterFactory.kt ================================================ package com.igorwojda.showcase.feature.base.data.retrofit import retrofit2.Call import retrofit2.CallAdapter import retrofit2.Retrofit import java.lang.reflect.ParameterizedType import java.lang.reflect.Type class ApiResultAdapterFactory : CallAdapter.Factory() { override fun get( returnType: Type, annotations: Array, retrofit: Retrofit, ): CallAdapter<*, *>? { if (Call::class.java != getRawType(returnType)) return null check(returnType is ParameterizedType) val responseType = getParameterUpperBound(0, returnType) if (getRawType(responseType) != ApiResult::class.java) return null check(responseType is ParameterizedType) val successType = getParameterUpperBound(0, responseType) return ApiResultCallAdapter(successType) } } ================================================ FILE: feature/base/src/main/kotlin/com/igorwojda/showcase/feature/base/data/retrofit/ApiResultCall.kt ================================================ package com.igorwojda.showcase.feature.base.data.retrofit import okhttp3.Request import okio.Timeout import retrofit2.Call import retrofit2.Callback import retrofit2.Response internal class ApiResultCall constructor( private val callDelegate: Call, ) : Call> { @Suppress("detekt.MagicNumber") override fun enqueue(callback: Callback>) = callDelegate.enqueue( object : Callback { override fun onResponse( call: Call, response: Response, ) { response.body()?.let { when (response.code()) { in 200..208 -> { callback.onResponse(this@ApiResultCall, Response.success(ApiResult.Success(it))) } in 400..409 -> { callback.onResponse( this@ApiResultCall, Response.success(ApiResult.Error(response.code(), response.message())), ) } } } ?: callback.onResponse(this@ApiResultCall, Response.success(ApiResult.Error(123, "message"))) } override fun onFailure( call: Call, throwable: Throwable, ) { callback.onResponse(this@ApiResultCall, Response.success(ApiResult.Exception(throwable))) call.cancel() } }, ) override fun clone(): Call> = ApiResultCall(callDelegate.clone()) override fun execute(): Response> = throw UnsupportedOperationException("ResponseCall does not support execute.") override fun isExecuted(): Boolean = callDelegate.isExecuted override fun cancel() = callDelegate.cancel() override fun isCanceled(): Boolean = callDelegate.isCanceled override fun request(): Request = callDelegate.request() override fun timeout(): Timeout = callDelegate.timeout() } ================================================ FILE: feature/base/src/main/kotlin/com/igorwojda/showcase/feature/base/data/retrofit/ApiResultCallAdapter.kt ================================================ package com.igorwojda.showcase.feature.base.data.retrofit import retrofit2.Call import retrofit2.CallAdapter import java.lang.reflect.Type internal class ApiResultCallAdapter( private val successType: Type, ) : CallAdapter>> { override fun responseType(): Type = successType override fun adapt(call: Call): Call> = ApiResultCall(call) } ================================================ FILE: feature/base/src/main/kotlin/com/igorwojda/showcase/feature/base/domain/result/Result.kt ================================================ package com.igorwojda.showcase.feature.base.domain.result sealed interface Result { data class Success( val value: T, ) : Result data class Failure( val throwable: Throwable? = null, ) : Result } ================================================ FILE: feature/base/src/main/kotlin/com/igorwojda/showcase/feature/base/domain/result/ResultExt.kt ================================================ package com.igorwojda.showcase.feature.base.domain.result inline fun Result.mapSuccess(crossinline onResult: Result.Success.() -> Result): Result { if (this is Result.Success) { return onResult(this) } return this } ================================================ FILE: feature/base/src/main/kotlin/com/igorwojda/showcase/feature/base/presentation/compose/composable/ErrorAnim.kt ================================================ package com.igorwojda.showcase.feature.base.presentation.compose.composable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import com.igorwojda.showcase.feature.base.R @Composable fun ErrorAnim(modifier: Modifier = Modifier) { Box( modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center, ) { LabeledAnimation(R.string.common_data_not_found, R.raw.lottie_error_screen) } } @Preview @Composable private fun ErrorAnimPreview() { ErrorAnim() } ================================================ FILE: feature/base/src/main/kotlin/com/igorwojda/showcase/feature/base/presentation/compose/composable/Loading.kt ================================================ package com.igorwojda.showcase.feature.base.presentation.compose.composable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.size import androidx.compose.material3.CircularProgressIndicator import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import com.igorwojda.showcase.feature.base.common.res.Dimen @Composable fun LoadingIndicator(modifier: Modifier = Modifier) { Box( modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center, ) { CircularProgressIndicator( modifier = Modifier .size(Dimen.spaceXXL), ) } } @Preview @Composable private fun LoadingIndicatorPreview() { LoadingIndicator() } ================================================ FILE: feature/base/src/main/kotlin/com/igorwojda/showcase/feature/base/presentation/compose/composable/Lottie.kt ================================================ package com.igorwojda.showcase.feature.base.presentation.compose.composable import androidx.annotation.RawRes import androidx.annotation.StringRes import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.requiredSize import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.material3.Card import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import com.airbnb.lottie.compose.LottieAnimation import com.airbnb.lottie.compose.LottieCompositionSpec import com.airbnb.lottie.compose.rememberLottieComposition import com.igorwojda.showcase.feature.base.common.res.Dimen @Composable fun LabeledAnimation( @StringRes label: Int, @RawRes assetResId: Int, modifier: Modifier = Modifier, ) { Card( modifier = modifier.wrapContentSize(), ) { Column( horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier .wrapContentSize() .padding(Dimen.spaceXL), ) { TextTitleMedium(text = stringResource(label)) LottieAssetLoader(assetResId) } } } @Composable fun LottieAssetLoader( @RawRes assetResId: Int, modifier: Modifier = Modifier, ) { val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(assetResId)) LottieAnimation( composition, modifier = modifier.requiredSize(Dimen.imageSize), ) } @Preview @Composable private fun LabeledAnimationPreview() { LabeledAnimation( label = android.R.string.ok, assetResId = com.igorwojda.showcase.feature.base.R.raw.lottie_building_screen, ) } @Preview @Composable private fun LottieAssetLoaderPreview() { LottieAssetLoader( assetResId = com.igorwojda.showcase.feature.base.R.raw.lottie_building_screen, ) } ================================================ FILE: feature/base/src/main/kotlin/com/igorwojda/showcase/feature/base/presentation/compose/composable/PlaceholderImage.kt ================================================ package com.igorwojda.showcase.feature.base.presentation.compose.composable import androidx.compose.material3.Surface import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview import coil.compose.AsyncImage import coil.request.ImageRequest import com.igorwojda.showcase.feature.base.R private val PLACEHOLDER_IMAGES = listOf( R.drawable.image_placeholder_1, R.drawable.image_placeholder_2, R.drawable.image_placeholder_3, ) @Composable fun PlaceholderImage( url: Any?, contentDescription: String?, modifier: Modifier = Modifier, ) { Surface(modifier = modifier) { val randomPlaceHolder by rememberSaveable { mutableStateOf(PLACEHOLDER_IMAGES.random()) } val model = ImageRequest .Builder(LocalContext.current) .data(url) .crossfade(true) .build() AsyncImage( model = model, contentDescription = contentDescription, placeholder = painterResource(randomPlaceHolder), ) } } @Preview @Composable private fun PlaceholderImagePreview() { PlaceholderImage( url = "https://github.com/igorwojda/android-showcase/raw/main/misc/image/module_dependencies.png?raw=true", contentDescription = "Sample image", ) } ================================================ FILE: feature/base/src/main/kotlin/com/igorwojda/showcase/feature/base/presentation/compose/composable/TextTitleLarge.kt ================================================ package com.igorwojda.showcase.feature.base.presentation.compose.composable import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview @Composable fun TextTitleLarge( text: String, modifier: Modifier = Modifier, ) { Text( text = text, modifier = modifier, style = MaterialTheme.typography.titleLarge, ) } @Preview @Composable private fun TextTitleLargePreview() { TextTitleLarge(text = "Sample Large Title") } ================================================ FILE: feature/base/src/main/kotlin/com/igorwojda/showcase/feature/base/presentation/compose/composable/TextTitleMedium.kt ================================================ package com.igorwojda.showcase.feature.base.presentation.compose.composable import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview @Composable fun TextTitleMedium( text: String, modifier: Modifier = Modifier, ) { Text( text = text, modifier = modifier, style = MaterialTheme.typography.titleMedium, ) } @Preview @Composable private fun TextTitleMediumPreview() { TextTitleMedium(text = "Sample Medium Title") } ================================================ FILE: feature/base/src/main/kotlin/com/igorwojda/showcase/feature/base/presentation/compose/composable/UnderConstructionAnim.kt ================================================ package com.igorwojda.showcase.feature.base.presentation.compose.composable import androidx.compose.runtime.Composable import androidx.compose.ui.tooling.preview.Preview import com.igorwojda.showcase.feature.base.R @Composable fun UnderConstructionAnim() { LabeledAnimation(R.string.common_under_construction, R.raw.lottie_building_screen) } @Preview @Composable private fun UnderConstructionAnimPreview() { UnderConstructionAnim() } ================================================ FILE: feature/base/src/main/kotlin/com/igorwojda/showcase/feature/base/presentation/viewmodel/BaseAction.kt ================================================ package com.igorwojda.showcase.feature.base.presentation.viewmodel interface BaseAction { fun reduce(state: State): State } ================================================ FILE: feature/base/src/main/kotlin/com/igorwojda/showcase/feature/base/presentation/viewmodel/BaseState.kt ================================================ package com.igorwojda.showcase.feature.base.presentation.viewmodel interface BaseState ================================================ FILE: feature/base/src/main/kotlin/com/igorwojda/showcase/feature/base/presentation/viewmodel/BaseViewModel.kt ================================================ package com.igorwojda.showcase.feature.base.presentation.viewmodel import androidx.lifecycle.ViewModel import com.igorwojda.showcase.feature.base.BuildConfig import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlin.properties.Delegates abstract class BaseViewModel>( initialState: State, ) : ViewModel() { private val _uiStateFlow = MutableStateFlow(initialState) val uiStateFlow = _uiStateFlow.asStateFlow() private var stateTimeTravelDebugger: StateTimeTravelDebugger? = null init { if (BuildConfig.DEBUG) { stateTimeTravelDebugger = StateTimeTravelDebugger(this::class.java.simpleName) } } // Delegate handles state event deduplication (multiple states of the same type holding the same data // will not be emitted multiple times to UI) private var state by Delegates.observable(initialState) { _, old, new -> if (old != new) { _uiStateFlow.value = new stateTimeTravelDebugger?.apply { addStateTransition(old, new) logLast() } } } protected fun sendAction(action: Action) { stateTimeTravelDebugger?.addAction(action) state = action.reduce(state) } } ================================================ FILE: feature/base/src/main/kotlin/com/igorwojda/showcase/feature/base/presentation/viewmodel/StateTimeTravelDebugger.kt ================================================ package com.igorwojda.showcase.feature.base.presentation.viewmodel import com.igorwojda.showcase.feature.base.util.TimberLogTags import kotlin.reflect.full.memberProperties import timber.log.Timber /** * Logs actions and view state transitions to facilitate debugging. */ class StateTimeTravelDebugger( private val viewClassName: String, ) { private val stateTimeline = mutableListOf() private var lastViewAction: BaseAction<*>? = null // Get list of properties from ViewState instances (all have the same type) private val propertyNames by lazy { stateTimeline .first() .oldState.javaClass.kotlin.memberProperties .map { it.name } } fun addAction(viewAction: BaseAction<*>) { lastViewAction = viewAction } fun addStateTransition( oldState: BaseState, newState: BaseState, ) { val lastViewAction = checkNotNull(lastViewAction) { "lastViewAction is null. Please log action before logging state transition" } stateTimeline.add(StateTransition(oldState, lastViewAction, newState)) this.lastViewAction = null } private fun getMessage() = getMessage(stateTimeline) private fun getMessage(stateTimeline: List): String { if (stateTimeline.isEmpty()) return "$viewClassName has no state transitions\n" return stateTimeline.joinToString(separator = "\n", postfix = "\n") { st -> buildString { append("Action: $viewClassName.${st.action.javaClass.simpleName}") if (propertyNames.isNotEmpty()) { append('\n') append( propertyNames.joinToString(separator = "") { prop -> getLogLine(st.oldState, st.newState, prop) }, ) } } } } fun logAll() { Timber.d(getMessage()) } fun logLast() { val states = listOf(stateTimeline.last()) Timber.tag(TimberLogTags.ACTION).d(getMessage(states)) } private fun getLogLine( oldState: BaseState, newState: BaseState, propertyName: String, ): String { val oldValue = getPropertyValue(oldState, propertyName) val newValue = getPropertyValue(newState, propertyName) val indent = "\t" return if (oldValue != newValue) { "$indent*$propertyName: $oldValue -> $newValue\n" } else { "$indent$propertyName: $newValue\n" } } private fun getPropertyValue( baseState: BaseState, propertyName: String, ): String { baseState::class.memberProperties.forEach { if (propertyName == it.name) { var value = it.getter.call(baseState).toString() if (value.isBlank()) { value = "\"\"" } return value } } return "" } private data class StateTransition( val oldState: BaseState, val action: BaseAction<*>, val newState: BaseState, ) } ================================================ FILE: feature/base/src/main/kotlin/com/igorwojda/showcase/feature/base/util/TimberLogTags.kt ================================================ package com.igorwojda.showcase.feature.base.util /** * Centralized log tags for consistent logging throughout the application. * * These tags help filter and identify different types of logs during development and debugging: * - Use with Timber: `Timber.tag(LogTags.NETWORK).d("message")` * - Filter in Logcat by tag to see specific log categories */ object TimberLogTags { /** * Network requests, responses, and HTTP-related logs. **/ const val NETWORK = "Network" /** * User actions and UI state modifications. **/ const val ACTION = "Action" /** * Navigation events and route changes. **/ const val NAVIGATION = "Navigation" } ================================================ FILE: feature/base/src/main/res/drawable/ic_search.xml ================================================ ================================================ FILE: feature/base/src/main/res/drawable/image_placeholder_1.xml ================================================ ================================================ FILE: feature/base/src/main/res/drawable/image_placeholder_2.xml ================================================ ================================================ FILE: feature/base/src/main/res/drawable/image_placeholder_3.xml ================================================ ================================================ FILE: feature/base/src/main/res/raw/lottie_building_screen.json ================================================ { "v": "4.11.0", "fr": 29.9700012207031, "ip": 0, "op": 150.000006109625, "w": 800, "h": 800, "nm": "guincho", "ddd": 0, "assets": [], "layers": [ { "ddd": 0, "ind": 1, "ty": 4, "nm": "Camada de forma 2", "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 0, "k": 0, "ix": 10 }, "p": { "a": 0, "k": [ 103.197, 603, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ -296, 128, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100, 100 ], "ix": 6 } }, "ao": 0, "shapes": [ { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ 0, 0 ] ], "v": [ [ -293, 127 ], [ -352, 127 ] ], "c": false }, "ix": 2 }, "nm": "Caminho 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "tm", "s": { "a": 1, "k": [ { "i": { "x": [ 0.833 ], "y": [ 0.833 ] }, "o": { "x": [ 0.167 ], "y": [ 0.167 ] }, "n": [ "0p833_0p833_0p167_0p167" ], "t": 40, "s": [ 0 ], "e": [ 100 ] }, { "t": 50.0000020365418 } ], "ix": 1 }, "e": { "a": 1, "k": [ { "i": { "x": [ 0.833 ], "y": [ 0.833 ] }, "o": { "x": [ 0.167 ], "y": [ 0.167 ] }, "n": [ "0p833_0p833_0p167_0p167" ], "t": 30, "s": [ 0 ], "e": [ 100 ] }, { "t": 40.0000016292334 } ], "ix": 2 }, "o": { "a": 0, "k": 0, "ix": 3 }, "m": 1, "ix": 2, "nm": "Aparar caminhos 1", "mn": "ADBE Vector Filter - Trim", "hd": false }, { "ty": "st", "c": { "a": 0, "k": [ 0.867509191176, 0.867509191176, 0.867509191176, 1 ], "ix": 3 }, "o": { "a": 0, "k": 100, "ix": 4 }, "w": { "a": 0, "k": 10, "ix": 5 }, "lc": 2, "lj": 1, "ml": 4, "nm": "Traçado 1", "mn": "ADBE Vector Graphic - Stroke", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transformar" } ], "nm": "Forma 1", "np": 4, "cix": 2, "ix": 1, "mn": "ADBE Vector Group", "hd": false } ], "ip": 0, "op": 150.000006109625, "st": 0, "bm": 0 }, { "ddd": 0, "ind": 2, "ty": 4, "nm": "Camada de forma 1", "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 0, "k": 0, "ix": 10 }, "p": { "a": 0, "k": [ 123.197, 576, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ -296, 128, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100, 100 ], "ix": 6 } }, "ao": 0, "shapes": [ { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ 0, 0 ] ], "v": [ [ -293, 127 ], [ -352, 127 ] ], "c": false }, "ix": 2 }, "nm": "Caminho 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "st", "c": { "a": 0, "k": [ 0.867509191176, 0.867509191176, 0.867509191176, 1 ], "ix": 3 }, "o": { "a": 0, "k": 100, "ix": 4 }, "w": { "a": 0, "k": 10, "ix": 5 }, "lc": 2, "lj": 1, "ml": 4, "nm": "Traçado 1", "mn": "ADBE Vector Graphic - Stroke", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transformar" } ], "nm": "Forma 1", "np": 3, "cix": 2, "ix": 1, "mn": "ADBE Vector Group", "hd": false }, { "ty": "tm", "s": { "a": 1, "k": [ { "i": { "x": [ 0.833 ], "y": [ 0.833 ] }, "o": { "x": [ 0.167 ], "y": [ 0.167 ] }, "n": [ "0p833_0p833_0p167_0p167" ], "t": 41, "s": [ 0 ], "e": [ 100 ] }, { "t": 51.0000020772726 } ], "ix": 1 }, "e": { "a": 1, "k": [ { "i": { "x": [ 0.833 ], "y": [ 0.833 ] }, "o": { "x": [ 0.167 ], "y": [ 0.167 ] }, "n": [ "0p833_0p833_0p167_0p167" ], "t": 31, "s": [ 0 ], "e": [ 100 ] }, { "t": 41.0000016699642 } ], "ix": 2 }, "o": { "a": 0, "k": 0, "ix": 3 }, "m": 1, "ix": 2, "nm": "Aparar caminhos 1", "mn": "ADBE Vector Filter - Trim", "hd": false } ], "ip": 0, "op": 150.000006109625, "st": 0, "bm": 0 }, { "ddd": 0, "ind": 3, "ty": 4, "nm": "roldana/crane contornos", "parent": 9, "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 0, "k": 0, "ix": 10 }, "p": { "a": 0, "k": [ 216, 1.5, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 27, 27, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100, 100 ], "ix": 6 } }, "ao": 0, "shapes": [ { "ty": "gr", "it": [ { "d": 1, "ty": "el", "s": { "a": 0, "k": [ 49.293, 49.293 ], "ix": 2 }, "p": { "a": 0, "k": [ 0, 0 ], "ix": 3 }, "nm": "Caminho da elipse 1", "mn": "ADBE Vector Shape - Ellipse", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.736999990426, 0.552999997606, 0.184000007779, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Preenchimento 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 26.871, 26.652 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 107.14, 107.14 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transformar" } ], "nm": "Elipse 1", "np": 3, "cix": 2, "ix": 1, "mn": "ADBE Vector Group", "hd": false } ], "ip": 0, "op": 150.000006109625, "st": 0, "bm": 0 }, { "ddd": 0, "ind": 4, "ty": 4, "nm": "braço-detail/crane contornos", "parent": 9, "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 0, "k": 0, "ix": 10 }, "p": { "a": 0, "k": [ 109.5, 92, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 79.5, 83.5, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100, 100 ], "ix": 6 } }, "ao": 0, "shapes": [ { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 1.766, 0.883 ], [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ -2.648, 0 ], [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ 0, -2.648 ], [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ 1.765, 1.765 ], [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ 79.007, -30.014 ], [ 75.476, -77.682 ], [ 72.827, -82.979 ], [ 58.703, -67.973 ], [ 62.235, -27.366 ], [ 15.448, -21.186 ], [ -2.207, -1.766 ], [ 1.324, 54.732 ], [ -63.118, 63.559 ], [ -79.007, 80.331 ], [ -72.828, 82.979 ], [ -71.945, 82.979 ], [ 3.089, 73.27 ], [ 18.096, 52.965 ], [ 14.566, -1.766 ], [ 64, -7.945 ] ], "c": true }, "ix": 2 }, "nm": "Caminho 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.736999990426, 0.552999997606, 0.184000007779, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Preenchimento 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 79.257, 83.23 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transformar" } ], "nm": "Grupo 1", "np": 2, "cix": 2, "ix": 1, "mn": "ADBE Vector Group", "hd": false } ], "ip": 0, "op": 150.000006109625, "st": 0, "bm": 0 }, { "ddd": 0, "ind": 5, "ty": 4, "nm": "guincho/crane contornos", "parent": 3, "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 1, "k": [ { "i": { "x": [ 0.833 ], "y": [ 0.833 ] }, "o": { "x": [ 0.167 ], "y": [ 0.167 ] }, "n": [ "0p833_0p833_0p167_0p167" ], "t": 5, "s": [ 13 ], "e": [ -22 ] }, { "i": { "x": [ 0.833 ], "y": [ 0.833 ] }, "o": { "x": [ 0.167 ], "y": [ 0.167 ] }, "n": [ "0p833_0p833_0p167_0p167" ], "t": 25, "s": [ -22 ], "e": [ 8 ] }, { "i": { "x": [ 0.833 ], "y": [ 0.833 ] }, "o": { "x": [ 0.167 ], "y": [ 0.167 ] }, "n": [ "0p833_0p833_0p167_0p167" ], "t": 35, "s": [ 8 ], "e": [ -10 ] }, { "i": { "x": [ 0.833 ], "y": [ 0.833 ] }, "o": { "x": [ 0.167 ], "y": [ 0.167 ] }, "n": [ "0p833_0p833_0p167_0p167" ], "t": 45, "s": [ -10 ], "e": [ 6 ] }, { "i": { "x": [ 0.833 ], "y": [ 0.833 ] }, "o": { "x": [ 0.167 ], "y": [ 0.167 ] }, "n": [ "0p833_0p833_0p167_0p167" ], "t": 55, "s": [ 6 ], "e": [ -6 ] }, { "i": { "x": [ 0.833 ], "y": [ 0.833 ] }, "o": { "x": [ 0.167 ], "y": [ 0.167 ] }, "n": [ "0p833_0p833_0p167_0p167" ], "t": 65, "s": [ -6 ], "e": [ 0 ] }, { "t": 75.0000030548126 } ], "ix": 10 }, "p": { "a": 0, "k": [ -4.267, 39.547, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ -1.267, -1.953, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100, 100 ], "ix": 6 } }, "ao": 0, "shapes": [ { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 5.296, 0 ], [ 0, 0 ], [ 0, 5.297 ], [ -5.296, 0 ], [ 0, 0 ], [ 0, -5.296 ] ], "o": [ [ 0, 0 ], [ -5.296, 0 ], [ 0, -5.296 ], [ 0, 0 ], [ 5.296, 0 ], [ 0, 5.297 ] ], "v": [ [ 17.656, 8.827 ], [ -17.655, 8.827 ], [ -26.483, 0 ], [ -17.655, -8.827 ], [ 17.656, -8.827 ], [ 26.483, 0 ] ], "c": true }, "ix": 2 }, "nm": "Caminho 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.635000011968, 0.635000011968, 0.635000011968, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Preenchimento 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 26.733, 188.629 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transformar" } ], "nm": "Grupo 1", "np": 2, "cix": 2, "ix": 1, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 19.421, 0 ], [ 0, 19.421 ], [ -5.297, 0 ], [ 0, -5.295 ], [ -9.71, 0 ], [ 0, 9.711 ], [ 9.711, 0 ], [ 1.766, 1.764 ], [ 0, 2.648 ], [ 0, 0 ], [ -5.296, 0 ], [ 0, -5.296 ], [ 0, 0 ], [ 0, -16.772 ] ], "o": [ [ -19.421, 0 ], [ 0, -5.295 ], [ 5.296, 0 ], [ 0, 9.711 ], [ 9.711, 0 ], [ 0, -9.71 ], [ -2.648, 0 ], [ -1.765, -1.766 ], [ 0, 0 ], [ 0, -5.296 ], [ 5.297, 0 ], [ 0, 0 ], [ 15.007, 3.531 ], [ 0, 19.421 ] ], "v": [ [ 0, 136.827 ], [ -35.311, 101.517 ], [ -26.482, 92.689 ], [ -17.655, 101.517 ], [ 0, 119.173 ], [ 17.655, 101.517 ], [ 0, 83.862 ], [ -6.179, 81.215 ], [ -8.827, 75.035 ], [ -8.827, -128.001 ], [ 0, -136.827 ], [ 8.828, -128.001 ], [ 8.828, 67.09 ], [ 35.311, 101.517 ] ], "c": true }, "ix": 2 }, "nm": "Caminho 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.795999983245, 0.804000016755, 0.819999964097, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Preenchimento 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 35.561, 137.077 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transformar" } ], "nm": "Grupo 2", "np": 2, "cix": 2, "ix": 2, "mn": "ADBE Vector Group", "hd": false } ], "ip": 0, "op": 150.000006109625, "st": 0, "bm": 0 }, { "ddd": 0, "ind": 6, "ty": 4, "nm": "vidro2/crane contornos", "parent": 8, "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 0, "k": 0, "ix": 10 }, "p": { "a": 0, "k": [ 36, 107, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 36, 18, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100, 100 ], "ix": 6 } }, "ao": 0, "shapes": [ { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, -15.007 ], [ 0, 0 ], [ 0, 0 ], [ 0, 14.124 ], [ 0, 0 ] ], "o": [ [ -15.007, 0 ], [ 0, 0 ], [ 0, 0 ], [ 15.007, 0 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ -7.945, -17.656 ], [ -35.31, 9.71 ], [ -35.31, 17.656 ], [ 8.827, 17.656 ], [ 35.31, -8.827 ], [ 35.31, -17.656 ] ], "c": true }, "ix": 2 }, "nm": "Caminho 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.736999990426, 0.552999997606, 0.184000007779, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Preenchimento 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 35.56, 17.906 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transformar" } ], "nm": "Grupo 1", "np": 2, "cix": 2, "ix": 1, "mn": "ADBE Vector Group", "hd": false } ], "ip": 0, "op": 150.000006109625, "st": 0, "bm": 0 }, { "ddd": 0, "ind": 7, "ty": 4, "nm": "vidro/crane contornos", "parent": 8, "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 0, "k": 0, "ix": 10 }, "p": { "a": 0, "k": [ 217, 62.25, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 49, 62.5, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100, 100 ], "ix": 6 } }, "ao": 0, "shapes": [ { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 11.477, 0 ], [ 0, 0 ], [ 0, 0 ], [ -14.124, -5.297 ], [ 0, 0 ], [ 0, 0 ], [ 15.007, 11.476 ] ], "o": [ [ 0, 0 ], [ 0, 0 ], [ 0, 15.007 ], [ 0, 0 ], [ 0, 0 ], [ 0, -17.655 ], [ -9.71, -6.18 ] ], "v": [ [ -7.945, -61.793 ], [ -48.551, -61.793 ], [ -48.551, 1.766 ], [ -24.717, 35.311 ], [ 48.551, 61.793 ], [ 48.551, -7.062 ], [ 24.717, -52.965 ] ], "c": true }, "ix": 2 }, "nm": "Caminho 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.736999990426, 0.552999997606, 0.184000007779, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Preenchimento 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 48.801, 62.043 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transformar" } ], "nm": "Grupo 1", "np": 2, "cix": 2, "ix": 1, "mn": "ADBE Vector Group", "hd": false } ], "ip": 0, "op": 150.000006109625, "st": 0, "bm": 0 }, { "ddd": 0, "ind": 8, "ty": 4, "nm": "base2/crane contornos", "parent": 13, "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 0, "k": 0, "ix": 10 }, "p": { "a": 0, "k": [ -7.5, -74.5, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 133, 80, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100, 100 ], "ix": 6 } }, "ao": 0, "shapes": [ { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, -9.71 ], [ 0, 0 ], [ 0, 0 ], [ 0, -15.007 ], [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ 2.649, 6.179 ], [ 22.952, 0 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ 0, 0 ], [ -15.007, 0 ], [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ 0, -7.062 ], [ -7.944, -21.187 ], [ 0, 0 ], [ -9.71, 0 ] ], "v": [ [ -8.828, -61.794 ], [ -8.828, 8.827 ], [ -105.049, 8.827 ], [ -132.414, 36.193 ], [ -132.414, 79.449 ], [ -8.828, 79.449 ], [ 132.414, 79.449 ], [ 132.414, -24.717 ], [ 128.882, -45.02 ], [ 77.682, -79.449 ], [ 8.827, -79.449 ] ], "c": true }, "ix": 2 }, "nm": "Caminho 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.995999983245, 0.760999971278, 0.298000021542, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Preenchimento 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 132.664, 79.699 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transformar" } ], "nm": "Grupo 1", "np": 2, "cix": 2, "ix": 1, "mn": "ADBE Vector Group", "hd": false } ], "ip": 0, "op": 150.000006109625, "st": 0, "bm": 0 }, { "ddd": 0, "ind": 9, "ty": 4, "nm": "braço/crane contornos", "parent": 8, "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 0, "k": 0, "ix": 10 }, "p": { "a": 0, "k": [ 251.5, -95, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 107.5, 103.5, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100, 100 ], "ix": 6 } }, "ao": 0, "shapes": [ { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 14.124 ], [ 0, 0 ], [ 0, 0 ], [ -5.296, -1.766 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ 0, 0 ], [ 6.18, 0 ], [ 0, 0 ], [ -13.241, -0.882 ] ], "v": [ [ 82.979, -102.842 ], [ -106.814, 100.192 ], [ -45.021, 100.192 ], [ -27.366, 102.842 ], [ 106.814, -76.359 ] ], "c": true }, "ix": 2 }, "nm": "Caminho 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.995999983245, 0.760999971278, 0.298000021542, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Preenchimento 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 107.064, 103.092 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transformar" } ], "nm": "Grupo 1", "np": 2, "cix": 2, "ix": 1, "mn": "ADBE Vector Group", "hd": false } ], "ip": 0, "op": 150.000006109625, "st": 0, "bm": 0 }, { "ddd": 0, "ind": 10, "ty": 4, "nm": "roda3/crane contornos", "parent": 11, "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 0, "k": 0, "ix": 10 }, "p": { "a": 0, "k": [ 124, 22.5, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 150.5, 22.5, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100, 100 ], "ix": 6 } }, "ao": 0, "shapes": [ { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 12.358 ], [ 12.358, 0 ], [ 0, -12.358 ], [ -12.359, 0 ] ], "o": [ [ 0, -12.358 ], [ -12.359, 0 ], [ 0, 12.358 ], [ 12.358, 0 ] ], "v": [ [ 22.069, 0 ], [ 0.001, -22.069 ], [ -22.069, 0 ], [ 0.001, 22.069 ] ], "c": true }, "ix": 2 }, "nm": "Caminho 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.635000011968, 0.635000011968, 0.635000011968, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Preenchimento 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 278.318, 22.319 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transformar" } ], "nm": "Grupo 1", "np": 2, "cix": 2, "ix": 1, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 12.358 ], [ 12.359, 0 ], [ 0, -12.358 ], [ -12.359, 0 ] ], "o": [ [ 0, -12.358 ], [ -12.359, 0 ], [ 0, 12.358 ], [ 12.359, 0 ] ], "v": [ [ 22.069, 0 ], [ 0, -22.069 ], [ -22.069, 0 ], [ 0, 22.069 ] ], "c": true }, "ix": 2 }, "nm": "Caminho 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.635000011968, 0.635000011968, 0.635000011968, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Preenchimento 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 22.319, 22.319 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transformar" } ], "nm": "Grupo 2", "np": 2, "cix": 2, "ix": 2, "mn": "ADBE Vector Group", "hd": false } ], "ip": 0, "op": 150.000006109625, "st": 0, "bm": 0 }, { "ddd": 0, "ind": 11, "ty": 4, "nm": "roda2/crane contornos", "parent": 12, "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 0, "k": 0, "ix": 10 }, "p": { "a": 0, "k": [ 186, 49, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 124, 22.5, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100, 100 ], "ix": 6 } }, "ao": 0, "shapes": [ { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 10.593 ], [ -9.71, 2.648 ], [ 0, 0 ], [ 0, 0 ], [ 0, -11.476 ], [ 9.71, -2.648 ], [ 0, 0 ], [ 0, 0 ] ], "o": [ [ 0, -10.593 ], [ 0, 0 ], [ 0, 0 ], [ 9.71, 1.765 ], [ 0, 11.476 ], [ 0, 0 ], [ 0, 0 ], [ -9.71, -2.648 ] ], "v": [ [ 105.931, 0 ], [ 123.586, -22.069 ], [ 0, -22.069 ], [ -123.586, -22.069 ], [ -105.931, 0 ], [ -123.586, 22.069 ], [ 0, 22.069 ], [ 123.586, 22.069 ] ], "c": true }, "ix": 2 }, "nm": "Caminho 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.795999983245, 0.804000016755, 0.819999964097, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Preenchimento 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 123.836, 22.319 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transformar" } ], "nm": "Grupo 1", "np": 2, "cix": 2, "ix": 1, "mn": "ADBE Vector Group", "hd": false } ], "ip": 0, "op": 150.000006109625, "st": 0, "bm": 0 }, { "ddd": 0, "ind": 12, "ty": 4, "nm": "roda/crane contornos", "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 0, "k": 0, "ix": 10 }, "p": { "a": 1, "k": [ { "i": { "x": 0.591, "y": 1 }, "o": { "x": 0.074, "y": 0 }, "n": "0p591_1_0p074_0", "t": 0, "s": [ -258, 607, 0 ], "e": [ 385, 607, 0 ], "to": [ 107.166664123535, 0, 0 ], "ti": [ -107.166664123535, 0, 0 ] }, { "t": 30.0000012219251 } ], "ix": 2 }, "a": { "a": 0, "k": [ 186, 49, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100, 100 ], "ix": 6 } }, "ao": 0, "shapes": [ { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 26.483, 0 ], [ 0, 0 ], [ 0, 26.483 ], [ -26.483, 0 ], [ 0, 0 ], [ 0, -26.482 ] ], "o": [ [ 0, 0 ], [ -26.483, 0 ], [ 0, -26.482 ], [ 0, 0 ], [ 26.483, 0 ], [ 0, 26.483 ] ], "v": [ [ 136.828, 48.552 ], [ -136.827, 48.552 ], [ -185.379, 0 ], [ -136.827, -48.552 ], [ 136.828, -48.552 ], [ 185.379, 0 ] ], "c": true }, "ix": 2 }, "nm": "Caminho 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.325, 0.325, 0.325, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Preenchimento 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 185.629, 48.802 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transformar" } ], "nm": "Grupo 1", "np": 2, "cix": 2, "ix": 1, "mn": "ADBE Vector Group", "hd": false } ], "ip": 0, "op": 150.000006109625, "st": 0, "bm": 0 }, { "ddd": 0, "ind": 13, "ty": 4, "nm": "base/crane contornos", "parent": 12, "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 0, "k": 0, "ix": 10 }, "p": { "a": 0, "k": [ 187.5, -12, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 44.5, 13.5, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100, 100 ], "ix": 6 } }, "ao": 0, "shapes": [ { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ -44.138, 13.241 ], [ 44.138, 13.241 ], [ 44.138, -13.241 ], [ -44.138, -13.241 ] ], "c": true }, "ix": 2 }, "nm": "Caminho 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.736999990426, 0.552999997606, 0.184000007779, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Preenchimento 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 44.388, 13.491 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transformar" } ], "nm": "Grupo 1", "np": 2, "cix": 2, "ix": 1, "mn": "ADBE Vector Group", "hd": false } ], "ip": 0, "op": 150.000006109625, "st": 0, "bm": 0 } ] } ================================================ FILE: feature/base/src/main/res/raw/lottie_error_screen.json ================================================ { "v": "5.2.1", "fr": 30, "ip": 0, "op": 30, "w": 296, "h": 296, "nm": "error screen", "ddd": 0, "assets": [], "layers": [ { "ddd": 0, "ind": 1, "ty": 4, "nm": "Layer 3", "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 0, "k": 0, "ix": 10 }, "p": { "a": 0, "k": [ 148, 148, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100, 100 ], "ix": 6 } }, "ao": 0, "shapes": [ { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 31.38, -31.38 ], [ 19.95, -4.96 ], [ 22.99, 0.01 ], [ 19.67, 4.85 ], [ 10.03, 10.04 ], [ -39.71, 39.71 ], [ -19.88, 4.96 ], [ -23.05, 0 ], [ -19.64, -4.83 ], [ -10.05, -10.05 ], [ -4.83, -15.64 ] ], "o": [ [ -9.97, 9.97 ], [ -19.75, 4.92 ], [ -22.84, 0 ], [ -20.12, -4.98 ], [ -39.71, -39.7 ], [ 9.95, -9.95 ], [ 19.78, -4.94 ], [ 22.82, -0.02 ], [ 20.16, 4.96 ], [ 8.32, 8.32 ], [ 18.24, 59.02 ] ], "v": [ [ 112.3, 112.31 ], [ 65.74, 134.71 ], [ 0, 142.08 ], [ -65.38, 134.81 ], [ -112.3, 112.3 ], [ -112.3, -112.3 ], [ -65.9, -134.67 ], [ 0, -142.08 ], [ 65.27, -134.83 ], [ 112.3, -112.3 ], [ 132.03, -75.39 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ind": 1, "ty": "sh", "ix": 2, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ -148, -148 ], [ -148, 148 ], [ 148, 148 ], [ 148, -148 ] ], "c": true }, "ix": 2 }, "nm": "Path 2", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0, 0, 0, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 1", "np": 3, "cix": 2, "ix": 1, "mn": "ADBE Vector Group", "hd": false } ], "ip": 0, "op": 0, "st": 0, "bm": 0 }, { "ddd": 0, "ind": 2, "ty": 4, "nm": "Layer 10", "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 1, "k": [ { "i": { "x": [ 0.667 ], "y": [ 1 ] }, "o": { "x": [ 1 ], "y": [ 0 ] }, "n": [ "0p667_1_1_0" ], "t": 6, "s": [ 37 ], "e": [ 0 ] }, { "i": { "x": [ 0 ], "y": [ 1 ] }, "o": { "x": [ 0.333 ], "y": [ 0 ] }, "n": [ "0_1_0p333_0" ], "t": 16, "s": [ 0 ], "e": [ 37 ] }, { "i": { "x": [ 0.667 ], "y": [ 1 ] }, "o": { "x": [ 0.333 ], "y": [ 0 ] }, "n": [ "0p667_1_0p333_0" ], "t": 26, "s": [ 37 ], "e": [ 37 ] }, { "i": { "x": [ 0.667 ], "y": [ 1 ] }, "o": { "x": [ 1 ], "y": [ 0 ] }, "n": [ "0p667_1_1_0" ], "t": 36, "s": [ 37 ], "e": [ 0 ] }, { "i": { "x": [ 0 ], "y": [ 1 ] }, "o": { "x": [ 0.333 ], "y": [ 0 ] }, "n": [ "0_1_0p333_0" ], "t": 46, "s": [ 0 ], "e": [ 37 ] }, { "t": 56 } ], "ix": 10 }, "p": { "a": 0, "k": [ 296.029, 104.11, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 120.04, -65.903, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100, 100 ], "ix": 6 } }, "ao": 0, "shapes": [ { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ -1.893, -0.018 ] ], "o": [ [ 0, 0 ], [ 0.018, -1.893 ], [ 0, 0 ] ], "v": [ [ 29.406, -75.358 ], [ 29.475, -82.703 ], [ 32.935, -86.098 ] ], "c": false }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "st", "c": { "a": 0, "k": [ 1, 0.749019622803, 0.749019622803, 1 ], "ix": 3 }, "o": { "a": 0, "k": 100, "ix": 4 }, "w": { "a": 0, "k": 0.831, "ix": 5 }, "lc": 2, "lj": 2, "nm": "Stroke 1", "mn": "ADBE Vector Graphic - Stroke", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 1", "np": 2, "cix": 2, "ix": 1, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ -0.025, 2.65 ], [ 0, 0 ], [ -2.65, -0.025 ], [ 0.025, -2.65 ], [ 0, 0 ], [ 2.65, 0.025 ] ], "o": [ [ 0, 0 ], [ -2.65, -0.025 ], [ 0, 0 ], [ 0.025, -2.65 ], [ 2.65, 0.025 ], [ 0, 0 ], [ -0.025, 2.65 ], [ 0, 0 ] ], "v": [ [ 32.428, -68.682 ], [ 32.428, -68.682 ], [ 27.675, -73.526 ], [ 27.771, -83.809 ], [ 32.615, -88.562 ], [ 37.369, -83.718 ], [ 37.272, -73.435 ], [ 32.428, -68.682 ] ], "c": false }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.65098041296, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 2", "np": 2, "cix": 2, "ix": 2, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 2.653, 0.025 ], [ -0.025, 2.653 ], [ 0, 0 ], [ -0.466, 0.73 ], [ -2.105, -2.144 ], [ -0.278, -0.345 ] ], "o": [ [ 0, 0 ], [ -0.025, 2.653 ], [ -2.653, -0.025 ], [ 0, 0 ], [ 0.009, -0.933 ], [ 3.087, 0.543 ], [ 0.312, 0.318 ], [ 0, 0 ] ], "v": [ [ 38.704, -79.911 ], [ 38.631, -72.186 ], [ 33.787, -67.432 ], [ 29.034, -72.276 ], [ 29.131, -82.559 ], [ 29.881, -85.089 ], [ 37.822, -80.901 ], [ 38.704, -79.911 ] ], "c": false }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.529411792755, 0.529411792755, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 3", "np": 2, "cix": 2, "ix": 3, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ -0.015, 1.619 ], [ 0, 0 ], [ -1.62, -0.015 ], [ 0, 0 ], [ 0.015, -1.619 ], [ 0, 0 ], [ 1.619, 0.015 ] ], "o": [ [ 0, 0 ], [ -1.62, -0.015 ], [ 0, 0 ], [ 0.015, -1.619 ], [ 0, 0 ], [ 1.619, 0.015 ], [ 0, 0 ], [ -0.015, 1.619 ], [ 0, 0 ] ], "v": [ [ 40.951, -47.483 ], [ 27.62, -47.608 ], [ 24.716, -50.567 ], [ 24.719, -50.874 ], [ 27.679, -53.778 ], [ 41.009, -53.653 ], [ 43.914, -50.694 ], [ 43.911, -50.387 ], [ 40.951, -47.483 ] ], "c": false }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.65098041296, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 4", "np": 2, "cix": 2, "ix": 4, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ -0.015, 1.619 ], [ 0, 0 ], [ -1.62, -0.015 ], [ 0, 0 ], [ 0.015, -1.619 ], [ 0, 0 ], [ 1.619, 0.015 ] ], "o": [ [ 0, 0 ], [ -1.62, -0.015 ], [ 0, 0 ], [ 0.015, -1.619 ], [ 0, 0 ], [ 1.619, 0.015 ], [ 0, 0 ], [ -0.015, 1.619 ], [ 0, 0 ] ], "v": [ [ 42.322, -47.47 ], [ 28.991, -47.596 ], [ 26.087, -50.554 ], [ 26.09, -50.862 ], [ 29.05, -53.765 ], [ 42.38, -53.64 ], [ 45.285, -50.681 ], [ 45.282, -50.374 ], [ 42.322, -47.47 ] ], "c": false }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.529411792755, 0.529411792755, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 5", "np": 2, "cix": 2, "ix": 5, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ -0.015, 1.619 ], [ 0, 0 ], [ -1.62, -0.015 ], [ 0, 0 ], [ 0.015, -1.619 ], [ 0, 0 ], [ 1.619, 0.015 ] ], "o": [ [ 0, 0 ], [ -1.62, -0.015 ], [ 0, 0 ], [ 0.015, -1.619 ], [ 0, 0 ], [ 1.619, 0.015 ], [ 0, 0 ], [ -0.015, 1.619 ], [ 0, 0 ] ], "v": [ [ 41.164, -70.105 ], [ 27.833, -70.231 ], [ 24.929, -73.19 ], [ 24.932, -73.497 ], [ 27.892, -76.401 ], [ 41.222, -76.275 ], [ 44.127, -73.316 ], [ 44.124, -73.009 ], [ 41.164, -70.105 ] ], "c": false }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.65098041296, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 6", "np": 2, "cix": 2, "ix": 6, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ -0.015, 1.619 ], [ 0, 0 ], [ -1.62, -0.015 ], [ 0, 0 ], [ 0.015, -1.619 ], [ 0, 0 ], [ 1.619, 0.015 ] ], "o": [ [ 0, 0 ], [ -1.62, -0.015 ], [ 0, 0 ], [ 0.015, -1.619 ], [ 0, 0 ], [ 1.619, 0.015 ], [ 0, 0 ], [ -0.015, 1.619 ], [ 0, 0 ] ], "v": [ [ 42.535, -70.092 ], [ 29.205, -70.218 ], [ 26.3, -73.177 ], [ 26.303, -73.484 ], [ 29.263, -76.388 ], [ 42.593, -76.262 ], [ 45.498, -73.303 ], [ 45.495, -72.996 ], [ 42.535, -70.092 ] ], "c": false }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.529411792755, 0.529411792755, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 7", "np": 2, "cix": 2, "ix": 7, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ -0.015, 1.619 ], [ 0, 0 ], [ -1.62, -0.015 ], [ 0, 0 ], [ 0.015, -1.619 ], [ 0, 0 ], [ 1.619, 0.015 ] ], "o": [ [ 0, 0 ], [ -1.62, -0.015 ], [ 0, 0 ], [ 0.015, -1.619 ], [ 0, 0 ], [ 1.619, 0.015 ], [ 0, 0 ], [ -0.015, 1.619 ], [ 0, 0 ] ], "v": [ [ 41.093, -62.564 ], [ 27.762, -62.69 ], [ 24.858, -65.649 ], [ 24.861, -65.956 ], [ 27.821, -68.86 ], [ 41.151, -68.734 ], [ 44.056, -65.775 ], [ 44.053, -65.468 ], [ 41.093, -62.564 ] ], "c": false }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.65098041296, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 8", "np": 2, "cix": 2, "ix": 8, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ -0.015, 1.619 ], [ 0, 0 ], [ -1.62, -0.015 ], [ 0, 0 ], [ 0.015, -1.619 ], [ 0, 0 ], [ 1.619, 0.015 ] ], "o": [ [ 0, 0 ], [ -1.62, -0.015 ], [ 0, 0 ], [ 0.015, -1.619 ], [ 0, 0 ], [ 1.619, 0.015 ], [ 0, 0 ], [ -0.015, 1.619 ], [ 0, 0 ] ], "v": [ [ 42.464, -62.552 ], [ 29.134, -62.677 ], [ 26.229, -65.636 ], [ 26.232, -65.943 ], [ 29.192, -68.847 ], [ 42.522, -68.721 ], [ 45.427, -65.762 ], [ 45.424, -65.455 ], [ 42.464, -62.552 ] ], "c": false }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.529411792755, 0.529411792755, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 9", "np": 2, "cix": 2, "ix": 9, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ -0.015, 1.619 ], [ 0, 0 ], [ -1.62, -0.015 ], [ 0, 0 ], [ 0.015, -1.619 ], [ 0, 0 ], [ 1.619, 0.015 ] ], "o": [ [ 0, 0 ], [ -1.62, -0.015 ], [ 0, 0 ], [ 0.015, -1.619 ], [ 0, 0 ], [ 1.619, 0.015 ], [ 0, 0 ], [ -0.015, 1.619 ], [ 0, 0 ] ], "v": [ [ 41.022, -55.024 ], [ 27.691, -55.149 ], [ 24.787, -58.108 ], [ 24.79, -58.415 ], [ 27.75, -61.319 ], [ 41.08, -61.193 ], [ 43.985, -58.234 ], [ 43.982, -57.927 ], [ 41.022, -55.024 ] ], "c": false }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.65098041296, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 10", "np": 2, "cix": 2, "ix": 10, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ -0.015, 1.619 ], [ 0, 0 ], [ -1.62, -0.015 ], [ 0, 0 ], [ 0.015, -1.619 ], [ 0, 0 ], [ 1.619, 0.015 ] ], "o": [ [ 0, 0 ], [ -1.62, -0.015 ], [ 0, 0 ], [ 0.015, -1.619 ], [ 0, 0 ], [ 1.619, 0.015 ], [ 0, 0 ], [ -0.015, 1.619 ], [ 0, 0 ] ], "v": [ [ 42.393, -55.011 ], [ 29.063, -55.136 ], [ 26.158, -58.095 ], [ 26.161, -58.402 ], [ 29.121, -61.306 ], [ 42.451, -61.181 ], [ 45.356, -58.222 ], [ 45.353, -57.914 ], [ 42.393, -55.011 ] ], "c": false }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.529411792755, 0.529411792755, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 11", "np": 2, "cix": 2, "ix": 11, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ -0.005, 0.569 ], [ -0.569, -0.005 ], [ 0, 0 ], [ 0.003, -0.343 ] ], "o": [ [ 0, 0 ], [ 0, 0 ], [ -0.569, -0.005 ], [ 0.005, -0.569 ], [ 0, 0 ], [ 0.024, 0.336 ], [ 0, 0 ] ], "v": [ [ 42.03, -69.069 ], [ 42.02, -68.04 ], [ 25.91, -68.192 ], [ 24.892, -69.23 ], [ 25.93, -70.249 ], [ 41.998, -70.097 ], [ 42.03, -69.069 ] ], "c": false }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.529411792755, 0.529411792755, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 12", "np": 2, "cix": 2, "ix": 12, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ -0.005, 0.569 ], [ -0.569, -0.005 ] ], "o": [ [ 0, 0 ], [ 0, 0 ], [ -0.569, -0.005 ], [ 0.005, -0.569 ], [ 0, 0 ] ], "v": [ [ 41.969, -62.556 ], [ 41.949, -60.5 ], [ 25.839, -60.651 ], [ 24.821, -61.689 ], [ 25.859, -62.708 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.529411792755, 0.529411792755, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 13", "np": 2, "cix": 2, "ix": 13, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0.191, -0.663 ], [ 0, 0 ], [ -0.005, 0.569 ], [ -0.569, -0.005 ] ], "o": [ [ -0.082, 0.699 ], [ 0, 0 ], [ -0.569, -0.005 ], [ 0.005, -0.569 ], [ 0, 0 ] ], "v": [ [ 41.795, -55.016 ], [ 41.385, -52.964 ], [ 25.768, -53.111 ], [ 24.75, -54.149 ], [ 25.788, -55.167 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.529411792755, 0.529411792755, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 14", "np": 2, "cix": 2, "ix": 14, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ -2.851, -0.027 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ 0.047, -2.85 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ 15.54, -59.034 ], [ 15.746, -71.583 ], [ 20.982, -76.684 ], [ 24.495, -76.651 ] ], "c": false }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "st", "c": { "a": 0, "k": [ 1, 0.749019622803, 0.749019622803, 1 ], "ix": 3 }, "o": { "a": 0, "k": 100, "ix": 4 }, "w": { "a": 0, "k": 1.744, "ix": 5 }, "lc": 2, "lj": 2, "nm": "Stroke 1", "mn": "ADBE Vector Graphic - Stroke", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 15", "np": 2, "cix": 2, "ix": 15, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ -0.042, 4.503 ], [ 0, 0 ], [ -4.503, -0.042 ], [ 0, 0 ], [ 0.042, -4.503 ], [ 0, 0 ], [ 4.503, 0.042 ] ], "o": [ [ 0, 0 ], [ -4.503, -0.042 ], [ 0, 0 ], [ 0.042, -4.503 ], [ 0, 0 ], [ 4.503, 0.042 ], [ 0, 0 ], [ -0.042, 4.503 ], [ 0, 0 ] ], "v": [ [ 33.661, -46.18 ], [ 20.488, -46.304 ], [ 12.413, -54.534 ], [ 12.582, -72.505 ], [ 20.811, -80.581 ], [ 33.984, -80.457 ], [ 42.06, -72.227 ], [ 41.89, -54.257 ], [ 33.661, -46.18 ] ], "c": false }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.65098041296, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 16", "np": 2, "cix": 2, "ix": 16, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ -0.042, 4.44 ], [ 0, 0 ], [ -4.44, -0.042 ], [ 0, 0 ], [ 0.042, -4.44 ], [ 0, 0 ], [ 4.44, 0.042 ] ], "o": [ [ 0, 0 ], [ -4.44, -0.042 ], [ 0, 0 ], [ 0.042, -4.44 ], [ 0, 0 ], [ 4.44, 0.042 ], [ 0, 0 ], [ -0.042, 4.44 ], [ 0, 0 ] ], "v": [ [ 32.396, -45.507 ], [ 20.369, -45.62 ], [ 12.405, -53.736 ], [ 12.583, -72.618 ], [ 20.699, -80.582 ], [ 32.726, -80.469 ], [ 40.69, -72.353 ], [ 40.512, -53.471 ], [ 32.396, -45.507 ] ], "c": false }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.529411792755, 0.529411792755, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 17", "np": 2, "cix": 2, "ix": 17, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 2.692, -2.641 ], [ 4.072, 0.038 ], [ 0, 0 ], [ 0.518, 7.602 ], [ -0.004, 0.391 ], [ -2.692, 2.641 ], [ -4.072, -0.038 ], [ 0, 0 ], [ 0.077, -8.137 ] ], "o": [ [ -0.038, 4.071 ], [ -2.692, 2.642 ], [ 0, 0 ], [ -7.746, -0.073 ], [ -0.031, -0.384 ], [ 0.038, -4.071 ], [ 2.692, -2.642 ], [ 0, 0 ], [ 8.137, 0.077 ], [ 0, 0 ] ], "v": [ [ 74.877, -62.589 ], [ 70.46, -52.21 ], [ 59.999, -47.989 ], [ 37.377, -48.202 ], [ 22.814, -61.921 ], [ 22.777, -63.08 ], [ 27.194, -73.459 ], [ 37.655, -77.68 ], [ 60.277, -77.467 ], [ 74.877, -62.589 ] ], "c": false }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.65098041296, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 18", "np": 2, "cix": 2, "ix": 18, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 2.692, -2.641 ], [ 4.072, 0.038 ], [ 0, 0 ], [ 0.518, 7.602 ], [ -0.004, 0.391 ], [ -2.692, 2.641 ], [ -4.072, -0.038 ], [ 0, 0 ], [ 0.077, -8.137 ] ], "o": [ [ -0.038, 4.071 ], [ -2.692, 2.642 ], [ 0, 0 ], [ -7.746, -0.073 ], [ -0.031, -0.384 ], [ 0.038, -4.071 ], [ 2.692, -2.642 ], [ 0, 0 ], [ 8.137, 0.077 ], [ 0, 0 ] ], "v": [ [ 76.934, -62.57 ], [ 72.517, -52.19 ], [ 62.056, -47.97 ], [ 39.434, -48.183 ], [ 24.871, -61.902 ], [ 24.834, -63.06 ], [ 29.251, -73.44 ], [ 39.711, -77.66 ], [ 62.334, -77.447 ], [ 76.934, -62.57 ] ], "c": false }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.529411792755, 0.529411792755, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 19", "np": 2, "cix": 2, "ix": 19, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 2.923, -3.14 ], [ 4.415, 0.042 ], [ 0, 0 ], [ 0.37, 7.271 ], [ -0.005, 0.5 ], [ -2.699, 3.381 ], [ -1.86, 0.935 ], [ 0, 0 ], [ 0.091, -9.652 ] ], "o": [ [ -0.046, 4.833 ], [ -2.923, 3.133 ], [ 0, 0 ], [ -5.041, -2.666 ], [ -0.03, -0.487 ], [ 0.049, -5.21 ], [ 1.401, -1.763 ], [ 0, 0 ], [ 8.83, 0.083 ], [ 0, 0 ] ], "v": [ [ 81.952, -62.522 ], [ 77.146, -50.207 ], [ 65.794, -45.192 ], [ 57.883, -45.267 ], [ 48.861, -61.353 ], [ 48.827, -62.834 ], [ 53.271, -76.12 ], [ 58.212, -80.229 ], [ 66.123, -80.154 ], [ 81.952, -62.522 ] ], "c": false }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.745, 0.173, 0.161, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 20", "np": 2, "cix": 2, "ix": 20, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 1.857, 1.891 ], [ 0, 0 ], [ -1.891, 1.857 ], [ -1.857, -1.891 ], [ 0, 0 ], [ 1.891, -1.857 ] ], "o": [ [ 0, 0 ], [ -1.892, 1.856 ], [ 0, 0 ], [ -1.856, -1.892 ], [ 1.892, -1.856 ], [ 0, 0 ], [ 1.856, 1.891 ], [ 0, 0 ] ], "v": [ [ 51.517, -64.455 ], [ 51.517, -64.455 ], [ 44.731, -64.519 ], [ 29.145, -80.402 ], [ 29.209, -87.189 ], [ 35.995, -87.125 ], [ 51.581, -71.242 ], [ 51.517, -64.455 ] ], "c": false }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.65098041296, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 21", "np": 2, "cix": 2, "ix": 21, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 2.699, -3.381 ], [ 4.072, 0.038 ], [ 0, 0 ], [ 1.692, 0.9 ], [ 0.37, 7.271 ], [ -0.005, 0.5 ], [ -2.699, 3.381 ], [ -1.86, 0.935 ], [ -1.954, -0.018 ], [ 0, 0 ], [ 0.098, -10.406 ] ], "o": [ [ -0.049, 5.21 ], [ -2.699, 3.389 ], [ 0, 0 ], [ -1.954, -0.018 ], [ -5.041, -2.666 ], [ -0.03, -0.487 ], [ 0.049, -5.21 ], [ 1.401, -1.763 ], [ 1.708, -0.868 ], [ 0, 0 ], [ 8.137, 0.077 ], [ 0, 0 ] ], "v": [ [ 120.039, -61.956 ], [ 115.594, -48.671 ], [ 105.122, -43.243 ], [ 63.388, -43.844 ], [ 57.883, -45.267 ], [ 48.861, -61.353 ], [ 48.827, -62.834 ], [ 53.271, -76.12 ], [ 58.212, -80.229 ], [ 63.743, -81.548 ], [ 105.477, -80.947 ], [ 120.039, -61.956 ] ], "c": false }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.984, 0.204, 0.188, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 22", "np": 2, "cix": 2, "ix": 22, "mn": "ADBE Vector Group", "hd": false } ], "ip": 0, "op": 455, "st": 5, "bm": 0 }, { "ddd": 0, "ind": 3, "ty": 4, "nm": "Shape Layer 1", "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 0, "k": 0, "ix": 10 }, "p": { "a": 0, "k": [ 148, 148, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100, 100 ], "ix": 6 } }, "ao": 0, "shapes": [ { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ 0, 0 ] ], "v": [ [ -70, 52 ], [ 49, 52 ] ], "c": false }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "tm", "s": { "a": 1, "k": [ { "i": { "x": [ 0.667 ], "y": [ 1 ] }, "o": { "x": [ 0.333 ], "y": [ 0 ] }, "n": [ "0p667_1_0p333_0" ], "t": 22, "s": [ 0 ], "e": [ 53 ] }, { "t": 28 } ], "ix": 1 }, "e": { "a": 1, "k": [ { "i": { "x": [ 0.667 ], "y": [ 1 ] }, "o": { "x": [ 0.333 ], "y": [ 0 ] }, "n": [ "0p667_1_0p333_0" ], "t": 22, "s": [ 100 ], "e": [ 53 ] }, { "t": 28 } ], "ix": 2 }, "o": { "a": 0, "k": 0, "ix": 3 }, "m": 1, "ix": 2, "nm": "Trim Paths 1", "mn": "ADBE Vector Filter - Trim", "hd": false }, { "ty": "st", "c": { "a": 0, "k": [ 1, 1, 1, 1 ], "ix": 3 }, "o": { "a": 0, "k": 100, "ix": 4 }, "w": { "a": 0, "k": 3, "ix": 5 }, "lc": 2, "lj": 1, "ml": 4, "ml2": { "a": 0, "k": 4, "ix": 8 }, "nm": "Stroke 1", "mn": "ADBE Vector Graphic - Stroke", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0, 0, 0, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": true }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Shape 1", "np": 4, "cix": 2, "ix": 1, "mn": "ADBE Vector Group", "hd": false } ], "ip": 22, "op": 450, "st": 0, "bm": 0 }, { "ddd": 0, "ind": 4, "ty": 4, "nm": "Layer 2", "sr": 1, "ks": { "o": { "a": 0, "k": 10, "ix": 11 }, "r": { "a": 0, "k": 0, "ix": 10 }, "p": { "a": 0, "k": [ 148, 148, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100, 100 ], "ix": 6 } }, "ao": 0, "shapes": [ { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 2.074, 2.073 ], [ 1.881, 1.133 ], [ 0, 0 ], [ -3.37, 0.001 ], [ -1.917, 0.081 ], [ 0, 0 ] ], "o": [ [ -1.35, -1.35 ], [ 0, 0 ], [ 3.314, 0.249 ], [ 1.927, 0.001 ], [ 0, 0 ], [ -1.561, -3.267 ] ], "v": [ [ 37.552, -2.047 ], [ 32.68, -5.77 ], [ -19.935, 105.027 ], [ -9.884, 105.408 ], [ -4.116, 105.275 ], [ 42.998, 6.062 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 1, 1, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 1", "np": 2, "cix": 2, "ix": 1, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 8.66, -0.007 ], [ 1.918, -0.081 ], [ 0, 0 ], [ -2.074, -2.074 ], [ -7.8, -2.094 ], [ 0, 0 ] ], "o": [ [ -1.928, 0 ], [ 0, 0 ], [ 1.561, 3.268 ], [ 3.974, 3.974 ], [ 0, 0 ], [ -7.683, -1.651 ] ], "v": [ [ -9.887, -14.625 ], [ -15.659, -14.491 ], [ -62.778, 84.732 ], [ -57.326, 92.831 ], [ -39.071, 101.932 ], [ 15.103, -12.148 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 1, 1, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 2", "np": 2, "cix": 2, "ix": 2, "mn": "ADBE Vector Group", "hd": false } ], "ip": 0, "op": 450, "st": 0, "bm": 0 }, { "ddd": 0, "ind": 5, "ty": 4, "nm": "Layer 12", "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 0, "k": 0, "ix": 10 }, "p": { "a": 0, "k": [ 138.784, 238.793, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 67.784, 120.793, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100, 100 ], "ix": 6 } }, "ao": 0, "shapes": [ { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 0.75, -0.12 ], [ 0.16, -0.02 ], [ 0.91, -0.11 ], [ 1.31, -0.12 ], [ 0.1, -0.01 ], [ 1.2, -0.08 ], [ 0.21, -0.01 ], [ 1.11, -0.04 ], [ 0.27, -0.01 ], [ 0, 0 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ -0.72, 0.12 ], [ -0.15, 0.02 ], [ -0.89, 0.14 ], [ -1.29, 0.17 ], [ -0.1, 0.01 ], [ -1.18, 0.11 ], [ -0.21, 0.01 ], [ -1.1, 0.07 ], [ -0.27, 0.01 ], [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ 89.689, 106.168 ], [ 89.689, 133.548 ], [ 87.499, 133.918 ], [ 87.039, 133.988 ], [ 84.329, 134.368 ], [ 80.429, 134.808 ], [ 80.129, 134.838 ], [ 76.549, 135.128 ], [ 75.929, 135.168 ], [ 72.609, 135.328 ], [ 71.799, 135.358 ], [ 71.749, 135.358 ], [ 71.749, 106.168 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.850980401039, 0.188235297799, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 1", "np": 2, "cix": 2, "ix": 1, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ 84.33, 134.372 ], [ 84.33, 134.372 ], [ 84.33, 134.372 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.729411780834, 0.929411768913, 0.078431375325, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 2", "np": 2, "cix": 2, "ix": 2, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 1.84, -0.59 ], [ 1.67, -0.41 ], [ 0.98, -0.21 ], [ 0.57, -0.11 ], [ 0.39, -0.07 ], [ 0.04, -0.01 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ -1.63, 0.7 ], [ -1.54, 0.5 ], [ -0.95, 0.24 ], [ -0.56, 0.12 ], [ -0.38, 0.07 ], [ -0.04, 0.01 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ 105.579, 106.168 ], [ 105.579, 128.998 ], [ 100.369, 130.938 ], [ 95.549, 132.308 ], [ 92.659, 132.968 ], [ 90.969, 133.308 ], [ 89.819, 133.528 ], [ 89.689, 133.548 ], [ 89.689, 106.168 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.541176497936, 0.78823530674, 1, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 3", "np": 2, "cix": 2, "ix": 3, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ 84.33, 134.372 ], [ 84.33, 134.372 ], [ 84.33, 134.372 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0, 0.890196084976, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 4", "np": 2, "cix": 2, "ix": 4, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 1.32, 0 ], [ 0, 0 ], [ 1.32, 0.04 ], [ 0.25, 0.01 ], [ 1.1, 0.07 ], [ 0.18, 0.01 ], [ 1.1, 0.11 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ -1.32, 0.04 ], [ 0, 0 ], [ -1.33, 0 ], [ -0.25, -0.01 ], [ -1.11, -0.04 ], [ -0.18, -0.01 ], [ -1.12, -0.07 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ 71.749, 106.168 ], [ 71.749, 135.358 ], [ 67.789, 135.418 ], [ 67.779, 135.418 ], [ 63.809, 135.358 ], [ 63.049, 135.328 ], [ 59.729, 135.168 ], [ 59.179, 135.138 ], [ 55.849, 134.868 ], [ 55.849, 106.168 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.180392161012, 0.454901963472, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 5", "np": 2, "cix": 2, "ix": 5, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ 51.486, 134.402 ], [ 51.487, 134.402 ], [ 51.486, 134.402 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.729411780834, 0.92549020052, 0.870588243008, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 6", "np": 2, "cix": 2, "ix": 6, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 0.09, 0.01 ], [ 0.07, 0.01 ], [ 1.27, 0.17 ], [ 0.92, 0.14 ], [ 0.13, 0.02 ], [ 0.8, 0.14 ], [ 0.36, 0.07 ], [ 0.58, 0.12 ], [ 0.94, 0.24 ], [ 0.74, 0.21 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ -0.09, 0 ], [ -0.07, 0 ], [ -1.3, -0.12 ], [ -0.94, -0.12 ], [ -0.13, -0.02 ], [ -0.81, -0.12 ], [ -0.36, -0.06 ], [ -0.59, -0.12 ], [ -0.98, -0.21 ], [ -0.77, -0.19 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ 55.849, 106.168 ], [ 55.849, 134.868 ], [ 55.579, 134.848 ], [ 55.359, 134.828 ], [ 51.489, 134.398 ], [ 48.699, 134.008 ], [ 48.319, 133.948 ], [ 45.899, 133.548 ], [ 44.829, 133.348 ], [ 43.059, 132.998 ], [ 40.169, 132.338 ], [ 37.909, 131.748 ], [ 37.909, 106.168 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.541176497936, 0.066666670144, 0.447058826685, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 7", "np": 2, "cix": 2, "ix": 7, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ 51.486, 134.402 ], [ 51.486, 134.402 ], [ 51.486, 134.402 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.937254905701, 0.078431375325, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 8", "np": 2, "cix": 2, "ix": 8, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 3.7, -3.7 ], [ 3.95, -1.72 ], [ 0, 0 ] ], "o": [ [ -2.07, 7.09 ], [ -2.4, 2.39 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ 123.879, 106.168 ], [ 115.229, 122.838 ], [ 105.579, 128.998 ], [ 105.579, 106.168 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.541176497936, 0.066666670144, 0.447058826685, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 9", "np": 2, "cix": 2, "ix": 9, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 0.88, 0.29 ], [ 3.32, 2.69 ], [ 0.51, 0.51 ], [ 2.07, 7.09 ] ], "o": [ [ 0, 0 ], [ -0.93, -0.26 ], [ -5.33, -1.71 ], [ -0.59, -0.48 ], [ -3.71, -3.7 ], [ 0, 0 ] ], "v": [ [ 37.909, 106.168 ], [ 37.909, 131.748 ], [ 35.199, 130.928 ], [ 22.009, 124.328 ], [ 20.349, 122.838 ], [ 11.689, 106.168 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.811764717102, 0, 0.882352948189, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 10", "np": 2, "cix": 2, "ix": 10, "mn": "ADBE Vector Group", "hd": false } ], "ip": 18, "op": 19, "st": 18, "bm": 0 }, { "ddd": 0, "ind": 6, "ty": 4, "nm": "Layer 11", "parent": 9, "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 0, "k": 0, "ix": 10 }, "p": { "a": 0, "k": [ -75.783, 60.775, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ -75.783, 60.775, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100, 100 ], "ix": 6 } }, "ao": 0, "shapes": [ { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ 1.307, 0.127 ], [ 0.114, 0.011 ], [ 1.184, 0.079 ], [ 0.226, 0.014 ], [ 1.094, 0.04 ], [ 0.282, 0.008 ], [ 1.336, 0 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ 0, 0 ], [ -1.281, -0.166 ], [ -0.114, -0.011 ], [ -1.168, -0.11 ], [ -0.226, -0.015 ], [ -1.086, -0.067 ], [ -0.282, -0.01 ], [ -1.331, -0.04 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ -59.234, 106.169 ], [ -59.234, 16.43 ], [ -59.234, 16.43 ], [ -63.12, 15.992 ], [ -63.463, 15.96 ], [ -66.993, 15.679 ], [ -67.669, 15.634 ], [ -70.941, 15.479 ], [ -71.785, 15.445 ], [ -75.787, 15.382 ], [ -75.787, 106.169 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.541176497936, 0.78823530674, 1, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 1", "np": 2, "cix": 2, "ix": 1, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0.226, 0.014 ], [ -0.226, -0.015 ] ], "o": [ [ 0.226, 0.014 ], [ -0.226, -0.015 ] ], "v": [ [ -67.669, 15.634 ], [ -66.993, 15.679 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.729411780834, 0.929411768913, 0.078431375325, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 2", "np": 2, "cix": 2, "ix": 2, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0.282, 0.009 ], [ -0.282, -0.01 ] ], "o": [ [ 0.282, 0.008 ], [ -0.282, -0.01 ] ], "v": [ [ -71.785, 15.445 ], [ -70.941, 15.479 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.729411780834, 0.929411768913, 0.078431375325, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 3", "np": 2, "cix": 2, "ix": 3, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ -59.234, 16.43 ], [ -59.234, 16.43 ], [ -59.234, 16.43 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.729411780834, 0.929411768913, 0.078431375325, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 4", "np": 2, "cix": 2, "ix": 4, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0.114, 0.011 ], [ -0.114, -0.011 ] ], "o": [ [ 0.114, 0.011 ], [ -0.114, -0.011 ] ], "v": [ [ -63.463, 15.96 ], [ -63.12, 15.992 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.729411780834, 0.929411768913, 0.078431375325, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 5", "np": 2, "cix": 2, "ix": 5, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ 1.73, 0.42 ], [ 0.967, 0.205 ], [ 0.539, 0.104 ], [ 0.404, 0.073 ], [ 0.744, 0.117 ], [ 0.176, 0.027 ], [ 0.88, 0.114 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ 0, 0 ], [ -1.6, -0.52 ], [ -0.936, -0.231 ], [ -0.53, -0.111 ], [ -0.4, -0.077 ], [ -0.73, -0.131 ], [ -0.176, -0.028 ], [ -0.865, -0.132 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ -59.234, 106.169 ], [ -43.197, 106.169 ], [ -43.197, 19.862 ], [ -48.207, 18.452 ], [ -51.068, 17.802 ], [ -52.676, 17.485 ], [ -53.879, 17.256 ], [ -56.09, 16.884 ], [ -56.618, 16.801 ], [ -59.234, 16.43 ], [ -59.234, 16.43 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.850980401039, 0.188235297799, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 6", "np": 2, "cix": 2, "ix": 6, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ -59.234, 16.43 ], [ -59.234, 16.43 ], [ -59.234, 16.43 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0, 0.890196084976, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 7", "np": 2, "cix": 2, "ix": 7, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0.177, 0.027 ], [ -0.176, -0.028 ] ], "o": [ [ 0.176, 0.027 ], [ -0.176, -0.028 ] ], "v": [ [ -56.618, 16.801 ], [ -56.09, 16.884 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0, 0.890196084976, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 8", "np": 2, "cix": 2, "ix": 8, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0.969, 0.204 ], [ -0.936, -0.231 ] ], "o": [ [ 0.967, 0.205 ], [ -0.936, -0.231 ] ], "v": [ [ -51.068, 17.802 ], [ -48.207, 18.452 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0, 0.890196084976, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 9", "np": 2, "cix": 2, "ix": 9, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0.405, 0.073 ], [ -0.4, -0.077 ] ], "o": [ [ 0.404, 0.073 ], [ -0.4, -0.077 ] ], "v": [ [ -53.879, 17.256 ], [ -52.676, 17.485 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0, 0.890196084976, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 10", "np": 2, "cix": 2, "ix": 10, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ 1.324, -0.04 ], [ 0.241, -0.008 ], [ 1.12, -0.069 ], [ 0.162, -0.01 ], [ 1.211, -0.113 ], [ 0.057, -0.005 ], [ 1.286, -0.163 ] ], "o": [ [ 0, 0 ], [ 0, 0 ], [ -1.328, 0 ], [ -0.241, 0.007 ], [ -1.128, 0.04 ], [ -0.162, 0.01 ], [ -1.227, 0.08 ], [ -0.057, 0.005 ], [ -1.311, 0.125 ], [ 0, 0 ] ], "v": [ [ -92.077, 106.169 ], [ -75.787, 106.169 ], [ -75.787, 15.382 ], [ -79.768, 15.444 ], [ -80.488, 15.473 ], [ -83.863, 15.631 ], [ -84.349, 15.664 ], [ -88.01, 15.95 ], [ -88.18, 15.967 ], [ -92.077, 16.398 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.364705890417, 0.909803926945, 0.690196096897, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 11", "np": 2, "cix": 2, "ix": 11, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0.162, -0.01 ], [ -0.162, 0.01 ] ], "o": [ [ 0.162, -0.01 ], [ -0.162, 0.01 ] ], "v": [ [ -84.349, 15.664 ], [ -83.863, 15.631 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.729411780834, 0.92549020052, 0.870588243008, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 12", "np": 2, "cix": 2, "ix": 12, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0.241, -0.009 ], [ -0.241, 0.007 ] ], "o": [ [ 0.241, -0.008 ], [ -0.241, 0.007 ] ], "v": [ [ -80.488, 15.473 ], [ -79.768, 15.444 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.729411780834, 0.92549020052, 0.870588243008, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 13", "np": 2, "cix": 2, "ix": 13, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0.057, -0.005 ], [ -0.057, 0.005 ] ], "o": [ [ 0.057, -0.005 ], [ -0.057, 0.005 ] ], "v": [ [ -88.18, 15.967 ], [ -88.01, 15.95 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.729411780834, 0.92549020052, 0.870588243008, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 14", "np": 2, "cix": 2, "ix": 14, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 0.952, -0.145 ], [ 0.105, -0.016 ], [ 0.837, -0.151 ], [ 0.339, -0.065 ], [ 0.613, -0.13 ], [ 0.955, -0.239 ], [ 1.52, -0.49 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ -0.97, 0.123 ], [ -0.105, 0.016 ], [ -0.854, 0.133 ], [ -0.342, 0.062 ], [ -0.625, 0.119 ], [ -0.989, 0.211 ], [ -1.64, 0.41 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ -92.077, 106.169 ], [ -92.077, 16.398 ], [ -94.961, 16.801 ], [ -95.276, 16.851 ], [ -97.814, 17.276 ], [ -98.83, 17.471 ], [ -100.695, 17.84 ], [ -103.617, 18.512 ], [ -108.367, 19.862 ], [ -108.367, 106.169 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.180392161012, 0.454901963472, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 15", "np": 2, "cix": 2, "ix": 15, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0.956, -0.239 ], [ -0.989, 0.211 ] ], "o": [ [ 0.955, -0.239 ], [ -0.99, 0.21 ] ], "v": [ [ -103.617, 18.512 ], [ -100.695, 17.84 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.937254905701, 0.078431375325, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 16", "np": 2, "cix": 2, "ix": 16, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0.105, -0.016 ], [ -0.105, 0.016 ] ], "o": [ [ 0.105, -0.016 ], [ -0.106, 0.016 ] ], "v": [ [ -95.276, 16.851 ], [ -94.961, 16.801 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.937254905701, 0.078431375325, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 17", "np": 2, "cix": 2, "ix": 17, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0.339, -0.065 ], [ -0.342, 0.062 ] ], "o": [ [ 0.339, -0.065 ], [ -0.343, 0.062 ] ], "v": [ [ -98.83, 17.471 ], [ -97.814, 17.276 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.937254905701, 0.078431375325, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 18", "np": 2, "cix": 2, "ix": 18, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 5.549, 17.966 ], [ 3.52, 3.52 ], [ 6.28, 2.02 ], [ 0, 0 ] ], "o": [ [ 5.333, -18.29 ], [ -2.04, -6.61 ], [ -3.38, -3.38 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ -19.686, 106.169 ], [ -20.007, 43.552 ], [ -28.347, 27.962 ], [ -43.197, 19.862 ], [ -43.197, 106.169 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.811764717102, 0, 0.882352948189, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 19", "np": 2, "cix": 2, "ix": 19, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 3.38, -3.38 ], [ -7.3, -25.015 ], [ 0, 0 ] ], "o": [ [ -6.28, 2.02 ], [ -13.074, 13.074 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ -108.367, 19.862 ], [ -123.217, 27.962 ], [ -131.878, 106.169 ], [ -108.367, 106.169 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.882352948189, 0.525490224361, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 20", "np": 2, "cix": 2, "ix": 20, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0.743, 0.118 ], [ -0.73, -0.131 ] ], "o": [ [ 0.744, 0.117 ], [ -0.73, -0.132 ] ], "v": [ [ -56.09, 16.884 ], [ -53.879, 17.256 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.701960802078, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 21", "np": 2, "cix": 2, "ix": 21, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0.538, 0.104 ], [ -0.53, -0.111 ] ], "o": [ [ 0.539, 0.104 ], [ -0.529, -0.112 ] ], "v": [ [ -52.676, 17.485 ], [ -51.068, 17.802 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.701960802078, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 22", "np": 2, "cix": 2, "ix": 22, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ -1.311, 0.125 ], [ 2.214, -0.34 ], [ -0.97, 0.123 ] ], "o": [ [ -2.302, 0.217 ], [ 0.952, -0.145 ], [ 1.286, -0.163 ] ], "v": [ [ -88.18, 15.967 ], [ -94.961, 16.801 ], [ -92.077, 16.398 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.701960802078, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 23", "np": 2, "cix": 2, "ix": 23, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 1.095, 0.039 ], [ -1.086, -0.067 ] ], "o": [ [ 1.094, 0.04 ], [ -1.086, -0.066 ] ], "v": [ [ -70.941, 15.479 ], [ -67.669, 15.634 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.701960802078, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 24", "np": 2, "cix": 2, "ix": 24, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 1.336, 0 ], [ -1.331, -0.04 ] ], "o": [ [ 1.336, 0 ], [ -1.332, -0.039 ] ], "v": [ [ -75.787, 15.382 ], [ -71.785, 15.445 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.701960802078, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 25", "np": 2, "cix": 2, "ix": 25, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 1.309, 0.126 ], [ -1.281, -0.166 ] ], "o": [ [ 1.307, 0.127 ], [ -1.282, -0.166 ] ], "v": [ [ -63.12, 15.992 ], [ -59.234, 16.43 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.701960802078, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 26", "np": 2, "cix": 2, "ix": 26, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 1.185, 0.078 ], [ -1.168, -0.11 ] ], "o": [ [ 1.184, 0.079 ], [ -1.169, -0.109 ] ], "v": [ [ -66.993, 15.679 ], [ -63.463, 15.96 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.701960802078, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 27", "np": 2, "cix": 2, "ix": 27, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 1.325, -0.039 ], [ -1.328, 0 ] ], "o": [ [ 1.324, -0.04 ], [ -1.328, 0 ] ], "v": [ [ -79.768, 15.444 ], [ -75.787, 15.382 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.701960802078, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 28", "np": 2, "cix": 2, "ix": 28, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0.88, 0.114 ], [ -0.865, -0.132 ] ], "o": [ [ 0.88, 0.114 ], [ -0.865, -0.133 ] ], "v": [ [ -59.234, 16.43 ], [ -56.618, 16.801 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.701960802078, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 29", "np": 2, "cix": 2, "ix": 29, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0.613, -0.131 ], [ -0.625, 0.119 ] ], "o": [ [ 0.613, -0.13 ], [ -0.625, 0.12 ] ], "v": [ [ -100.695, 17.84 ], [ -98.83, 17.471 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.701960802078, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 30", "np": 2, "cix": 2, "ix": 30, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 1.121, -0.067 ], [ -1.128, 0.04 ] ], "o": [ [ 1.12, -0.069 ], [ -1.129, 0.039 ] ], "v": [ [ -83.863, 15.631 ], [ -80.488, 15.473 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.701960802078, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 31", "np": 2, "cix": 2, "ix": 31, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 1.213, -0.112 ], [ -1.227, 0.08 ] ], "o": [ [ 1.211, -0.113 ], [ -1.228, 0.078 ] ], "v": [ [ -88.01, 15.95 ], [ -84.349, 15.664 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.701960802078, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 32", "np": 2, "cix": 2, "ix": 32, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0.836, -0.152 ], [ -0.854, 0.133 ] ], "o": [ [ 0.837, -0.151 ], [ -0.854, 0.134 ] ], "v": [ [ -97.814, 17.276 ], [ -95.276, 16.851 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.701960802078, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 33", "np": 2, "cix": 2, "ix": 33, "mn": "ADBE Vector Group", "hd": false } ], "ip": 17, "op": 22, "st": 17, "bm": 0 }, { "ddd": 0, "ind": 7, "ty": 4, "nm": "Layer 8", "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 0, "k": 0, "ix": 10 }, "p": { "a": 0, "k": [ 138.784, 238.793, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 67.784, 120.793, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100, 100 ], "ix": 6 } }, "ao": 0, "shapes": [ { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 0.75, -0.12 ], [ 0.16, -0.02 ], [ 0.91, -0.11 ], [ 1.31, -0.12 ], [ 0.1, -0.01 ], [ 1.2, -0.08 ], [ 0.21, -0.01 ], [ 1.11, -0.04 ], [ 0.27, -0.01 ], [ 0, 0 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ -0.72, 0.12 ], [ -0.15, 0.02 ], [ -0.89, 0.14 ], [ -1.29, 0.17 ], [ -0.1, 0.01 ], [ -1.18, 0.11 ], [ -0.21, 0.01 ], [ -1.1, 0.07 ], [ -0.27, 0.01 ], [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ 89.689, 106.168 ], [ 89.689, 133.548 ], [ 87.499, 133.918 ], [ 87.039, 133.988 ], [ 84.329, 134.368 ], [ 80.429, 134.808 ], [ 80.129, 134.838 ], [ 76.549, 135.128 ], [ 75.929, 135.168 ], [ 72.609, 135.328 ], [ 71.799, 135.358 ], [ 71.749, 135.358 ], [ 71.749, 106.168 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.850980401039, 0.188235297799, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 1", "np": 2, "cix": 2, "ix": 1, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ 84.33, 134.372 ], [ 84.33, 134.372 ], [ 84.33, 134.372 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.729411780834, 0.929411768913, 0.078431375325, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 2", "np": 2, "cix": 2, "ix": 2, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 1.84, -0.59 ], [ 1.67, -0.41 ], [ 0.98, -0.21 ], [ 0.57, -0.11 ], [ 0.39, -0.07 ], [ 0.04, -0.01 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ -1.63, 0.7 ], [ -1.54, 0.5 ], [ -0.95, 0.24 ], [ -0.56, 0.12 ], [ -0.38, 0.07 ], [ -0.04, 0.01 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ 105.579, 106.168 ], [ 105.579, 128.998 ], [ 100.369, 130.938 ], [ 95.549, 132.308 ], [ 92.659, 132.968 ], [ 90.969, 133.308 ], [ 89.819, 133.528 ], [ 89.689, 133.548 ], [ 89.689, 106.168 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.541176497936, 0.78823530674, 1, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 3", "np": 2, "cix": 2, "ix": 3, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ 84.33, 134.372 ], [ 84.33, 134.372 ], [ 84.33, 134.372 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0, 0.890196084976, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 4", "np": 2, "cix": 2, "ix": 4, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 1.32, 0 ], [ 0, 0 ], [ 1.32, 0.04 ], [ 0.25, 0.01 ], [ 1.1, 0.07 ], [ 0.18, 0.01 ], [ 1.1, 0.11 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ -1.32, 0.04 ], [ 0, 0 ], [ -1.33, 0 ], [ -0.25, -0.01 ], [ -1.11, -0.04 ], [ -0.18, -0.01 ], [ -1.12, -0.07 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ 71.749, 106.168 ], [ 71.749, 135.358 ], [ 67.789, 135.418 ], [ 67.779, 135.418 ], [ 63.809, 135.358 ], [ 63.049, 135.328 ], [ 59.729, 135.168 ], [ 59.179, 135.138 ], [ 55.849, 134.868 ], [ 55.849, 106.168 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.180392161012, 0.454901963472, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 5", "np": 2, "cix": 2, "ix": 5, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ 51.486, 134.402 ], [ 51.487, 134.402 ], [ 51.486, 134.402 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.729411780834, 0.92549020052, 0.870588243008, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 6", "np": 2, "cix": 2, "ix": 6, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 0.09, 0.01 ], [ 0.07, 0.01 ], [ 1.27, 0.17 ], [ 0.92, 0.14 ], [ 0.13, 0.02 ], [ 0.8, 0.14 ], [ 0.36, 0.07 ], [ 0.58, 0.12 ], [ 0.94, 0.24 ], [ 0.74, 0.21 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ -0.09, 0 ], [ -0.07, 0 ], [ -1.3, -0.12 ], [ -0.94, -0.12 ], [ -0.13, -0.02 ], [ -0.81, -0.12 ], [ -0.36, -0.06 ], [ -0.59, -0.12 ], [ -0.98, -0.21 ], [ -0.77, -0.19 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ 55.849, 106.168 ], [ 55.849, 134.868 ], [ 55.579, 134.848 ], [ 55.359, 134.828 ], [ 51.489, 134.398 ], [ 48.699, 134.008 ], [ 48.319, 133.948 ], [ 45.899, 133.548 ], [ 44.829, 133.348 ], [ 43.059, 132.998 ], [ 40.169, 132.338 ], [ 37.909, 131.748 ], [ 37.909, 106.168 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.541176497936, 0.066666670144, 0.447058826685, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 7", "np": 2, "cix": 2, "ix": 7, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ 51.486, 134.402 ], [ 51.486, 134.402 ], [ 51.486, 134.402 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.937254905701, 0.078431375325, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 8", "np": 2, "cix": 2, "ix": 8, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 3.7, -3.7 ], [ 3.95, -1.72 ], [ 0, 0 ] ], "o": [ [ -2.07, 7.09 ], [ -2.4, 2.39 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ 123.879, 106.168 ], [ 115.229, 122.838 ], [ 105.579, 128.998 ], [ 105.579, 106.168 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.541176497936, 0.066666670144, 0.447058826685, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 9", "np": 2, "cix": 2, "ix": 9, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 0.88, 0.29 ], [ 3.32, 2.69 ], [ 0.51, 0.51 ], [ 2.07, 7.09 ] ], "o": [ [ 0, 0 ], [ -0.93, -0.26 ], [ -5.33, -1.71 ], [ -0.59, -0.48 ], [ -3.71, -3.7 ], [ 0, 0 ] ], "v": [ [ 37.909, 106.168 ], [ 37.909, 131.748 ], [ 35.199, 130.928 ], [ 22.009, 124.328 ], [ 20.349, 122.838 ], [ 11.689, 106.168 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.811764717102, 0, 0.882352948189, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 10", "np": 2, "cix": 2, "ix": 10, "mn": "ADBE Vector Group", "hd": false } ], "ip": 16, "op": 17, "st": 16, "bm": 0 }, { "ddd": 0, "ind": 8, "ty": 4, "nm": "Layer 5", "parent": 9, "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 0, "k": 0, "ix": 10 }, "p": { "a": 0, "k": [ -75.783, 60.775, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ -75.783, 60.775, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100, 100 ], "ix": 6 } }, "ao": 0, "shapes": [ { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ 1.307, 0.127 ], [ 0.114, 0.011 ], [ 1.184, 0.079 ], [ 0.226, 0.014 ], [ 1.094, 0.04 ], [ 0.282, 0.008 ], [ 1.336, 0 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ 0, 0 ], [ -1.281, -0.166 ], [ -0.114, -0.011 ], [ -1.168, -0.11 ], [ -0.226, -0.015 ], [ -1.086, -0.067 ], [ -0.282, -0.01 ], [ -1.331, -0.04 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ -59.234, 106.169 ], [ -59.234, 16.43 ], [ -59.234, 16.43 ], [ -63.12, 15.992 ], [ -63.463, 15.96 ], [ -66.993, 15.679 ], [ -67.669, 15.634 ], [ -70.941, 15.479 ], [ -71.785, 15.445 ], [ -75.787, 15.382 ], [ -75.787, 106.169 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.541176497936, 0.78823530674, 1, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 1", "np": 2, "cix": 2, "ix": 1, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0.226, 0.014 ], [ -0.226, -0.015 ] ], "o": [ [ 0.226, 0.014 ], [ -0.226, -0.015 ] ], "v": [ [ -67.669, 15.634 ], [ -66.993, 15.679 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.729411780834, 0.929411768913, 0.078431375325, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 2", "np": 2, "cix": 2, "ix": 2, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0.282, 0.009 ], [ -0.282, -0.01 ] ], "o": [ [ 0.282, 0.008 ], [ -0.282, -0.01 ] ], "v": [ [ -71.785, 15.445 ], [ -70.941, 15.479 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.729411780834, 0.929411768913, 0.078431375325, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 3", "np": 2, "cix": 2, "ix": 3, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ -59.234, 16.43 ], [ -59.234, 16.43 ], [ -59.234, 16.43 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.729411780834, 0.929411768913, 0.078431375325, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 4", "np": 2, "cix": 2, "ix": 4, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0.114, 0.011 ], [ -0.114, -0.011 ] ], "o": [ [ 0.114, 0.011 ], [ -0.114, -0.011 ] ], "v": [ [ -63.463, 15.96 ], [ -63.12, 15.992 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.729411780834, 0.929411768913, 0.078431375325, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 5", "np": 2, "cix": 2, "ix": 5, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ 1.73, 0.42 ], [ 0.967, 0.205 ], [ 0.539, 0.104 ], [ 0.404, 0.073 ], [ 0.744, 0.117 ], [ 0.176, 0.027 ], [ 0.88, 0.114 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ 0, 0 ], [ -1.6, -0.52 ], [ -0.936, -0.231 ], [ -0.53, -0.111 ], [ -0.4, -0.077 ], [ -0.73, -0.131 ], [ -0.176, -0.028 ], [ -0.865, -0.132 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ -59.234, 106.169 ], [ -43.197, 106.169 ], [ -43.197, 19.862 ], [ -48.207, 18.452 ], [ -51.068, 17.802 ], [ -52.676, 17.485 ], [ -53.879, 17.256 ], [ -56.09, 16.884 ], [ -56.618, 16.801 ], [ -59.234, 16.43 ], [ -59.234, 16.43 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.850980401039, 0.188235297799, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 6", "np": 2, "cix": 2, "ix": 6, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ -59.234, 16.43 ], [ -59.234, 16.43 ], [ -59.234, 16.43 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0, 0.890196084976, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 7", "np": 2, "cix": 2, "ix": 7, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0.177, 0.027 ], [ -0.176, -0.028 ] ], "o": [ [ 0.176, 0.027 ], [ -0.176, -0.028 ] ], "v": [ [ -56.618, 16.801 ], [ -56.09, 16.884 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0, 0.890196084976, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 8", "np": 2, "cix": 2, "ix": 8, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0.969, 0.204 ], [ -0.936, -0.231 ] ], "o": [ [ 0.967, 0.205 ], [ -0.936, -0.231 ] ], "v": [ [ -51.068, 17.802 ], [ -48.207, 18.452 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0, 0.890196084976, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 9", "np": 2, "cix": 2, "ix": 9, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0.405, 0.073 ], [ -0.4, -0.077 ] ], "o": [ [ 0.404, 0.073 ], [ -0.4, -0.077 ] ], "v": [ [ -53.879, 17.256 ], [ -52.676, 17.485 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0, 0.890196084976, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 10", "np": 2, "cix": 2, "ix": 10, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ 1.324, -0.04 ], [ 0.241, -0.008 ], [ 1.12, -0.069 ], [ 0.162, -0.01 ], [ 1.211, -0.113 ], [ 0.057, -0.005 ], [ 1.286, -0.163 ] ], "o": [ [ 0, 0 ], [ 0, 0 ], [ -1.328, 0 ], [ -0.241, 0.007 ], [ -1.128, 0.04 ], [ -0.162, 0.01 ], [ -1.227, 0.08 ], [ -0.057, 0.005 ], [ -1.311, 0.125 ], [ 0, 0 ] ], "v": [ [ -92.077, 106.169 ], [ -75.787, 106.169 ], [ -75.787, 15.382 ], [ -79.768, 15.444 ], [ -80.488, 15.473 ], [ -83.863, 15.631 ], [ -84.349, 15.664 ], [ -88.01, 15.95 ], [ -88.18, 15.967 ], [ -92.077, 16.398 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.364705890417, 0.909803926945, 0.690196096897, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 11", "np": 2, "cix": 2, "ix": 11, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0.162, -0.01 ], [ -0.162, 0.01 ] ], "o": [ [ 0.162, -0.01 ], [ -0.162, 0.01 ] ], "v": [ [ -84.349, 15.664 ], [ -83.863, 15.631 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.729411780834, 0.92549020052, 0.870588243008, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 12", "np": 2, "cix": 2, "ix": 12, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0.241, -0.009 ], [ -0.241, 0.007 ] ], "o": [ [ 0.241, -0.008 ], [ -0.241, 0.007 ] ], "v": [ [ -80.488, 15.473 ], [ -79.768, 15.444 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.729411780834, 0.92549020052, 0.870588243008, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 13", "np": 2, "cix": 2, "ix": 13, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0.057, -0.005 ], [ -0.057, 0.005 ] ], "o": [ [ 0.057, -0.005 ], [ -0.057, 0.005 ] ], "v": [ [ -88.18, 15.967 ], [ -88.01, 15.95 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.729411780834, 0.92549020052, 0.870588243008, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 14", "np": 2, "cix": 2, "ix": 14, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 0.952, -0.145 ], [ 0.105, -0.016 ], [ 0.837, -0.151 ], [ 0.339, -0.065 ], [ 0.613, -0.13 ], [ 0.955, -0.239 ], [ 1.52, -0.49 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ -0.97, 0.123 ], [ -0.105, 0.016 ], [ -0.854, 0.133 ], [ -0.342, 0.062 ], [ -0.625, 0.119 ], [ -0.989, 0.211 ], [ -1.64, 0.41 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ -92.077, 106.169 ], [ -92.077, 16.398 ], [ -94.961, 16.801 ], [ -95.276, 16.851 ], [ -97.814, 17.276 ], [ -98.83, 17.471 ], [ -100.695, 17.84 ], [ -103.617, 18.512 ], [ -108.367, 19.862 ], [ -108.367, 106.169 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.180392161012, 0.454901963472, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 15", "np": 2, "cix": 2, "ix": 15, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0.956, -0.239 ], [ -0.989, 0.211 ] ], "o": [ [ 0.955, -0.239 ], [ -0.99, 0.21 ] ], "v": [ [ -103.617, 18.512 ], [ -100.695, 17.84 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.937254905701, 0.078431375325, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 16", "np": 2, "cix": 2, "ix": 16, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0.105, -0.016 ], [ -0.105, 0.016 ] ], "o": [ [ 0.105, -0.016 ], [ -0.106, 0.016 ] ], "v": [ [ -95.276, 16.851 ], [ -94.961, 16.801 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.937254905701, 0.078431375325, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 17", "np": 2, "cix": 2, "ix": 17, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0.339, -0.065 ], [ -0.342, 0.062 ] ], "o": [ [ 0.339, -0.065 ], [ -0.343, 0.062 ] ], "v": [ [ -98.83, 17.471 ], [ -97.814, 17.276 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.937254905701, 0.078431375325, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 18", "np": 2, "cix": 2, "ix": 18, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 5.549, 17.966 ], [ 3.52, 3.52 ], [ 6.28, 2.02 ], [ 0, 0 ] ], "o": [ [ 5.333, -18.29 ], [ -2.04, -6.61 ], [ -3.38, -3.38 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ -19.686, 106.169 ], [ -20.007, 43.552 ], [ -28.347, 27.962 ], [ -43.197, 19.862 ], [ -43.197, 106.169 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.811764717102, 0, 0.882352948189, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 19", "np": 2, "cix": 2, "ix": 19, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 3.38, -3.38 ], [ -7.3, -25.015 ], [ 0, 0 ] ], "o": [ [ -6.28, 2.02 ], [ -13.074, 13.074 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ -108.367, 19.862 ], [ -123.217, 27.962 ], [ -131.878, 106.169 ], [ -108.367, 106.169 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.882352948189, 0.525490224361, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 20", "np": 2, "cix": 2, "ix": 20, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0.743, 0.118 ], [ -0.73, -0.131 ] ], "o": [ [ 0.744, 0.117 ], [ -0.73, -0.132 ] ], "v": [ [ -56.09, 16.884 ], [ -53.879, 17.256 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.701960802078, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 21", "np": 2, "cix": 2, "ix": 21, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0.538, 0.104 ], [ -0.53, -0.111 ] ], "o": [ [ 0.539, 0.104 ], [ -0.529, -0.112 ] ], "v": [ [ -52.676, 17.485 ], [ -51.068, 17.802 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.701960802078, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 22", "np": 2, "cix": 2, "ix": 22, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ -1.311, 0.125 ], [ 2.214, -0.34 ], [ -0.97, 0.123 ] ], "o": [ [ -2.302, 0.217 ], [ 0.952, -0.145 ], [ 1.286, -0.163 ] ], "v": [ [ -88.18, 15.967 ], [ -94.961, 16.801 ], [ -92.077, 16.398 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.701960802078, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 23", "np": 2, "cix": 2, "ix": 23, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 1.095, 0.039 ], [ -1.086, -0.067 ] ], "o": [ [ 1.094, 0.04 ], [ -1.086, -0.066 ] ], "v": [ [ -70.941, 15.479 ], [ -67.669, 15.634 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.701960802078, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 24", "np": 2, "cix": 2, "ix": 24, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 1.336, 0 ], [ -1.331, -0.04 ] ], "o": [ [ 1.336, 0 ], [ -1.332, -0.039 ] ], "v": [ [ -75.787, 15.382 ], [ -71.785, 15.445 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.701960802078, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 25", "np": 2, "cix": 2, "ix": 25, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 1.309, 0.126 ], [ -1.281, -0.166 ] ], "o": [ [ 1.307, 0.127 ], [ -1.282, -0.166 ] ], "v": [ [ -63.12, 15.992 ], [ -59.234, 16.43 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.701960802078, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 26", "np": 2, "cix": 2, "ix": 26, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 1.185, 0.078 ], [ -1.168, -0.11 ] ], "o": [ [ 1.184, 0.079 ], [ -1.169, -0.109 ] ], "v": [ [ -66.993, 15.679 ], [ -63.463, 15.96 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.701960802078, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 27", "np": 2, "cix": 2, "ix": 27, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 1.325, -0.039 ], [ -1.328, 0 ] ], "o": [ [ 1.324, -0.04 ], [ -1.328, 0 ] ], "v": [ [ -79.768, 15.444 ], [ -75.787, 15.382 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.701960802078, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 28", "np": 2, "cix": 2, "ix": 28, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0.88, 0.114 ], [ -0.865, -0.132 ] ], "o": [ [ 0.88, 0.114 ], [ -0.865, -0.133 ] ], "v": [ [ -59.234, 16.43 ], [ -56.618, 16.801 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.701960802078, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 29", "np": 2, "cix": 2, "ix": 29, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0.613, -0.131 ], [ -0.625, 0.119 ] ], "o": [ [ 0.613, -0.13 ], [ -0.625, 0.12 ] ], "v": [ [ -100.695, 17.84 ], [ -98.83, 17.471 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.701960802078, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 30", "np": 2, "cix": 2, "ix": 30, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 1.121, -0.067 ], [ -1.128, 0.04 ] ], "o": [ [ 1.12, -0.069 ], [ -1.129, 0.039 ] ], "v": [ [ -83.863, 15.631 ], [ -80.488, 15.473 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.701960802078, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 31", "np": 2, "cix": 2, "ix": 31, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 1.213, -0.112 ], [ -1.227, 0.08 ] ], "o": [ [ 1.211, -0.113 ], [ -1.228, 0.078 ] ], "v": [ [ -88.01, 15.95 ], [ -84.349, 15.664 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.701960802078, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 32", "np": 2, "cix": 2, "ix": 32, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0.836, -0.152 ], [ -0.854, 0.133 ] ], "o": [ [ 0.837, -0.151 ], [ -0.854, 0.134 ] ], "v": [ [ -97.814, 17.276 ], [ -95.276, 16.851 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.701960802078, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 33", "np": 2, "cix": 2, "ix": 33, "mn": "ADBE Vector Group", "hd": false } ], "ip": 15, "op": 17, "st": 15, "bm": 0 }, { "ddd": 0, "ind": 9, "ty": 4, "nm": "Layer 9", "parent": 11, "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 0, "k": 0, "ix": 10 }, "p": { "a": 0, "k": [ -10, 91, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ -75.782, 120.795, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100, 100 ], "ix": 6 } }, "ao": 0, "shapes": [ { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ -1.334, 0.041 ], [ -0.272, 0.01 ], [ -1.099, 0.068 ], [ -0.209, 0.014 ], [ -1.185, 0.112 ], [ -0.098, 0.009 ], [ -1.286, 0.167 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ 1.339, 0 ], [ 0.273, -0.008 ], [ 1.108, -0.04 ], [ 0.209, -0.013 ], [ 1.201, -0.08 ], [ 0.098, -0.009 ], [ 1.313, -0.127 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ -59.234, 106.169 ], [ -75.787, 106.169 ], [ -75.787, 135.422 ], [ -75.777, 135.422 ], [ -71.766, 135.358 ], [ -70.95, 135.325 ], [ -67.638, 135.167 ], [ -67.013, 135.125 ], [ -63.431, 134.84 ], [ -63.136, 134.811 ], [ -59.234, 134.372 ], [ -59.234, 134.372 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.850980401039, 0.188235297799, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 1", "np": 2, "cix": 2, "ix": 1, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ -0.209, 0.014 ], [ 0.209, -0.013 ] ], "o": [ [ -0.209, 0.014 ], [ 0.209, -0.013 ] ], "v": [ [ -67.013, 135.125 ], [ -67.638, 135.167 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.729411780834, 0.929411768913, 0.078431375325, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 2", "np": 2, "cix": 2, "ix": 2, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ -0.272, 0.01 ], [ 0.273, -0.008 ] ], "o": [ [ -0.272, 0.01 ], [ 0.273, -0.008 ] ], "v": [ [ -70.95, 135.325 ], [ -71.766, 135.358 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.729411780834, 0.929411768913, 0.078431375325, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 3", "np": 2, "cix": 2, "ix": 3, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ -0.098, 0.01 ], [ 0.098, -0.009 ] ], "o": [ [ -0.098, 0.009 ], [ 0.099, -0.009 ] ], "v": [ [ -63.136, 134.811 ], [ -63.431, 134.84 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.729411780834, 0.929411768913, 0.078431375325, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 4", "np": 2, "cix": 2, "ix": 4, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ -59.234, 134.372 ], [ -59.234, 134.372 ], [ -59.234, 134.372 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.729411780834, 0.929411768913, 0.078431375325, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 5", "np": 2, "cix": 2, "ix": 5, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ -0.897, 0.137 ], [ -0.153, 0.024 ], [ -0.766, 0.138 ], [ -0.38, 0.074 ], [ -0.557, 0.118 ], [ -0.943, 0.234 ], [ -1.54, 0.5 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ 0, 0 ], [ 0.913, -0.118 ], [ 0.154, -0.024 ], [ 0.781, -0.123 ], [ 0.385, -0.07 ], [ 0.567, -0.109 ], [ 0.977, -0.208 ], [ 1.67, -0.41 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ -59.234, 106.169 ], [ -59.234, 134.372 ], [ -59.234, 134.372 ], [ -56.521, 133.987 ], [ -56.06, 133.915 ], [ -53.74, 133.524 ], [ -52.596, 133.305 ], [ -50.903, 132.97 ], [ -48.017, 132.312 ], [ -43.197, 130.942 ], [ -43.197, 106.169 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.541176497936, 0.78823530674, 1, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 6", "np": 2, "cix": 2, "ix": 6, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ -0.154, 0.024 ], [ 0.154, -0.024 ] ], "o": [ [ -0.153, 0.024 ], [ 0.154, -0.024 ] ], "v": [ [ -56.06, 133.915 ], [ -56.521, 133.987 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0, 0.890196084976, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 7", "np": 2, "cix": 2, "ix": 7, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ -0.381, 0.073 ], [ 0.385, -0.07 ] ], "o": [ [ -0.38, 0.074 ], [ 0.385, -0.069 ] ], "v": [ [ -52.596, 133.305 ], [ -53.74, 133.524 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0, 0.890196084976, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 8", "np": 2, "cix": 2, "ix": 8, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ -59.234, 134.372 ], [ -59.234, 134.372 ], [ -59.234, 134.372 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0, 0.890196084976, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 9", "np": 2, "cix": 2, "ix": 9, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ -0.944, 0.234 ], [ 0.977, -0.208 ] ], "o": [ [ -0.943, 0.234 ], [ 0.978, -0.207 ] ], "v": [ [ -48.017, 132.312 ], [ -50.903, 132.97 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0, 0.890196084976, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 10", "np": 2, "cix": 2, "ix": 10, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ -1.302, -0.124 ], [ -0.076, -0.007 ], [ -1.205, -0.079 ], [ -0.185, -0.011 ], [ -1.109, -0.04 ], [ -0.255, -0.008 ], [ -1.324, 0 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ 0, 0 ], [ 1.277, 0.163 ], [ 0.076, 0.007 ], [ 1.189, 0.112 ], [ 0.185, 0.012 ], [ 1.101, 0.067 ], [ 0.255, 0.009 ], [ 1.319, 0.04 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ -92.077, 106.169 ], [ -92.077, 134.402 ], [ -92.077, 134.402 ], [ -88.207, 134.831 ], [ -87.98, 134.853 ], [ -84.386, 135.135 ], [ -83.832, 135.173 ], [ -80.516, 135.329 ], [ -79.753, 135.359 ], [ -75.787, 135.422 ], [ -75.787, 106.169 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.180392161012, 0.454901963472, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 11", "np": 2, "cix": 2, "ix": 11, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ -0.076, -0.007 ], [ 0.076, 0.007 ] ], "o": [ [ -0.076, -0.007 ], [ 0.076, 0.007 ] ], "v": [ [ -87.98, 134.853 ], [ -88.207, 134.831 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.729411780834, 0.92549020052, 0.870588243008, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 12", "np": 2, "cix": 2, "ix": 12, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ -0.255, -0.008 ], [ 0.255, 0.009 ] ], "o": [ [ -0.255, -0.008 ], [ 0.255, 0.009 ] ], "v": [ [ -79.753, 135.359 ], [ -80.516, 135.329 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.729411780834, 0.92549020052, 0.870588243008, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 13", "np": 2, "cix": 2, "ix": 13, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ -92.077, 134.402 ], [ -92.077, 134.402 ], [ -92.077, 134.402 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.729411780834, 0.92549020052, 0.870588243008, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 14", "np": 2, "cix": 2, "ix": 14, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ -0.185, -0.011 ], [ 0.185, 0.012 ] ], "o": [ [ -0.185, -0.011 ], [ 0.185, 0.012 ] ], "v": [ [ -83.832, 135.173 ], [ -84.386, 135.135 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.729411780834, 0.92549020052, 0.870588243008, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 15", "np": 2, "cix": 2, "ix": 15, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ -1.72, -0.42 ], [ -0.978, -0.207 ], [ -0.594, -0.113 ], [ -0.359, -0.065 ], [ -0.817, -0.127 ], [ -0.124, -0.019 ], [ -0.939, -0.12 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ 0, 0 ], [ 1.59, 0.52 ], [ 0.945, 0.233 ], [ 0.584, 0.123 ], [ 0.356, 0.068 ], [ 0.801, 0.143 ], [ 0.124, 0.019 ], [ 0.922, 0.14 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ -92.077, 106.169 ], [ -108.367, 106.169 ], [ -108.367, 130.932 ], [ -103.397, 132.342 ], [ -100.508, 132.997 ], [ -98.734, 133.345 ], [ -97.667, 133.548 ], [ -95.24, 133.953 ], [ -94.869, 134.011 ], [ -92.078, 134.402 ], [ -92.077, 134.402 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.541176497936, 0.066666670144, 0.447058826685, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 16", "np": 2, "cix": 2, "ix": 16, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ -0.979, -0.206 ], [ 0.945, 0.233 ] ], "o": [ [ -0.978, -0.207 ], [ 0.945, 0.233 ] ], "v": [ [ -100.508, 132.997 ], [ -103.397, 132.342 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.937254905701, 0.078431375325, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 17", "np": 2, "cix": 2, "ix": 17, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ -92.078, 134.402 ], [ -92.077, 134.402 ], [ -92.077, 134.402 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.937254905701, 0.078431375325, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 18", "np": 2, "cix": 2, "ix": 18, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ -0.124, -0.019 ], [ 0.124, 0.019 ] ], "o": [ [ -0.124, -0.019 ], [ 0.124, 0.019 ] ], "v": [ [ -94.869, 134.011 ], [ -95.24, 133.953 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.937254905701, 0.078431375325, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 19", "np": 2, "cix": 2, "ix": 19, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ -0.36, -0.064 ], [ 0.356, 0.068 ] ], "o": [ [ -0.359, -0.065 ], [ 0.356, 0.068 ] ], "v": [ [ -97.667, 133.548 ], [ -98.734, 133.345 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.937254905701, 0.078431375325, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 20", "np": 2, "cix": 2, "ix": 20, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ -3.39, 3.38 ], [ -2.067, 7.089 ] ], "o": [ [ 0, 0 ], [ 6.28, -2.02 ], [ 3.701, -3.704 ], [ 0, 0 ] ], "v": [ [ -43.197, 106.169 ], [ -43.197, 130.942 ], [ -28.337, 122.842 ], [ -19.686, 106.169 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.541176497936, 0.066666670144, 0.447058826685, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 21", "np": 2, "cix": 2, "ix": 21, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ -3.706, -3.703 ], [ -6.28, -2.01 ], [ 0, 0 ] ], "o": [ [ 2.069, 7.09 ], [ 3.38, 3.38 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ -131.878, 106.169 ], [ -123.217, 122.842 ], [ -108.367, 130.932 ], [ -108.367, 106.169 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.807843148708, 0, 0.854901969433, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 22", "np": 2, "cix": 2, "ix": 22, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ -1.323, 0 ], [ 1.319, 0.04 ] ], "o": [ [ -1.324, 0 ], [ 1.32, 0.039 ] ], "v": [ [ -75.787, 135.422 ], [ -79.753, 135.359 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.701960802078, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 23", "np": 2, "cix": 2, "ix": 23, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ -1.335, 0.039 ], [ 1.339, 0 ] ], "o": [ [ -1.334, 0.041 ], [ 1.339, 0 ] ], "v": [ [ -71.766, 135.358 ], [ -75.777, 135.422 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.701960802078, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 24", "np": 2, "cix": 2, "ix": 24, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ -1.109, -0.039 ], [ 1.101, 0.067 ] ], "o": [ [ -1.109, -0.04 ], [ 1.102, 0.066 ] ], "v": [ [ -80.516, 135.329 ], [ -83.832, 135.173 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.701960802078, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 25", "np": 2, "cix": 2, "ix": 25, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ -1.206, -0.078 ], [ 1.189, 0.112 ] ], "o": [ [ -1.205, -0.079 ], [ 1.19, 0.111 ] ], "v": [ [ -84.386, 135.135 ], [ -87.98, 134.853 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.701960802078, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 26", "np": 2, "cix": 2, "ix": 26, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ -1.303, -0.123 ], [ 1.277, 0.163 ] ], "o": [ [ -1.302, -0.124 ], [ 1.277, 0.163 ] ], "v": [ [ -88.207, 134.831 ], [ -92.077, 134.402 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.701960802078, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 27", "np": 2, "cix": 2, "ix": 27, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ -0.557, 0.118 ], [ 0.567, -0.109 ] ], "o": [ [ -0.557, 0.118 ], [ 0.566, -0.11 ] ], "v": [ [ -50.903, 132.97 ], [ -52.596, 133.305 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.701960802078, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 28", "np": 2, "cix": 2, "ix": 28, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ -0.765, 0.139 ], [ 0.781, -0.123 ] ], "o": [ [ -0.766, 0.138 ], [ 0.78, -0.124 ] ], "v": [ [ -53.74, 133.524 ], [ -56.06, 133.915 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.701960802078, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 29", "np": 2, "cix": 2, "ix": 29, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ -0.939, -0.12 ], [ 0.922, 0.14 ] ], "o": [ [ -0.939, -0.12 ], [ 0.922, 0.141 ] ], "v": [ [ -92.078, 134.402 ], [ -94.869, 134.011 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.701960802078, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 30", "np": 2, "cix": 2, "ix": 30, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ -1.287, 0.167 ], [ 1.313, -0.127 ] ], "o": [ [ -1.286, 0.167 ], [ 1.314, -0.127 ] ], "v": [ [ -59.234, 134.372 ], [ -63.136, 134.811 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.701960802078, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 31", "np": 2, "cix": 2, "ix": 31, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ -1.186, 0.111 ], [ 1.201, -0.08 ] ], "o": [ [ -1.185, 0.112 ], [ 1.202, -0.079 ] ], "v": [ [ -63.431, 134.84 ], [ -67.013, 135.125 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.701960802078, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 32", "np": 2, "cix": 2, "ix": 32, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ -0.896, 0.138 ], [ 0.913, -0.118 ] ], "o": [ [ -0.897, 0.137 ], [ 0.913, -0.118 ] ], "v": [ [ -56.521, 133.987 ], [ -59.234, 134.372 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.701960802078, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 33", "np": 2, "cix": 2, "ix": 33, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ -1.1, 0.067 ], [ 1.108, -0.04 ] ], "o": [ [ -1.099, 0.068 ], [ 1.108, -0.04 ] ], "v": [ [ -67.638, 135.167 ], [ -70.95, 135.325 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.701960802078, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 34", "np": 2, "cix": 2, "ix": 34, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ -0.594, -0.114 ], [ 0.584, 0.123 ] ], "o": [ [ -0.594, -0.113 ], [ 0.583, 0.123 ] ], "v": [ [ -98.734, 133.345 ], [ -100.508, 132.997 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.701960802078, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 35", "np": 2, "cix": 2, "ix": 35, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ -0.816, -0.128 ], [ 0.801, 0.143 ] ], "o": [ [ -0.817, -0.127 ], [ 0.8, 0.144 ] ], "v": [ [ -95.24, 133.953 ], [ -97.667, 133.548 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 0.701960802078, 0.65098041296, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 36", "np": 2, "cix": 2, "ix": 36, "mn": "ADBE Vector Group", "hd": false } ], "ip": 15, "op": 22, "st": 15, "bm": 0 }, { "ddd": 0, "ind": 10, "ty": 4, "nm": "stroke", "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 0, "k": -2, "ix": 10 }, "p": { "a": 0, "k": [ 206.657, 70.872, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 134.09, 13.571, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 79.5, 100, 100 ], "ix": 6 } }, "ao": 0, "shapes": [ { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ 0, 0 ] ], "v": [ [ 109.353, -21.362 ], [ 121.95, 4.931 ] ], "c": false }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "tm", "s": { "a": 1, "k": [ { "i": { "x": [ 0.667 ], "y": [ 1 ] }, "o": { "x": [ 1 ], "y": [ 0 ] }, "n": [ "0p667_1_1_0" ], "t": 14, "s": [ 100 ], "e": [ 0 ] }, { "t": 19 } ], "ix": 1 }, "e": { "a": 1, "k": [ { "i": { "x": [ 0 ], "y": [ 1 ] }, "o": { "x": [ 0.333 ], "y": [ 0 ] }, "n": [ "0_1_0p333_0" ], "t": 19, "s": [ 100 ], "e": [ 0 ] }, { "t": 24 } ], "ix": 2 }, "o": { "a": 0, "k": 0, "ix": 3 }, "m": 1, "ix": 2, "nm": "Trim Paths 1", "mn": "ADBE Vector Filter - Trim", "hd": false }, { "ty": "st", "c": { "a": 0, "k": [ 1, 1, 1, 1 ], "ix": 3 }, "o": { "a": 0, "k": 100, "ix": 4 }, "w": { "a": 0, "k": 3, "ix": 5 }, "lc": 2, "lj": 1, "ml": 10, "ml2": { "a": 0, "k": 10, "ix": 8 }, "nm": "Stroke 1", "mn": "ADBE Vector Graphic - Stroke", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 1", "np": 3, "cix": 2, "ix": 1, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ 0, 0 ] ], "v": [ [ 99.533, 2.308 ], [ 112.512, 13.571 ] ], "c": false }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "st", "c": { "a": 0, "k": [ 1, 1, 1, 1 ], "ix": 3 }, "o": { "a": 0, "k": 100, "ix": 4 }, "w": { "a": 0, "k": 3, "ix": 5 }, "lc": 2, "lj": 1, "ml": 10, "ml2": { "a": 0, "k": 10, "ix": 8 }, "nm": "Stroke 1", "mn": "ADBE Vector Graphic - Stroke", "hd": false }, { "ty": "tm", "s": { "a": 1, "k": [ { "i": { "x": [ 0.667 ], "y": [ 1 ] }, "o": { "x": [ 1 ], "y": [ 0 ] }, "n": [ "0p667_1_1_0" ], "t": 14, "s": [ 100 ], "e": [ 0 ] }, { "t": 19 } ], "ix": 1 }, "e": { "a": 1, "k": [ { "i": { "x": [ 0 ], "y": [ 1 ] }, "o": { "x": [ 0.333 ], "y": [ 0 ] }, "n": [ "0_1_0p333_0" ], "t": 19, "s": [ 100 ], "e": [ 0 ] }, { "t": 24 } ], "ix": 2 }, "o": { "a": 0, "k": 0, "ix": 3 }, "m": 1, "ix": 3, "nm": "Trim Paths 1", "mn": "ADBE Vector Filter - Trim", "hd": false }, { "ty": "st", "c": { "a": 0, "k": [ 1, 1, 1, 1 ], "ix": 3 }, "o": { "a": 0, "k": 100, "ix": 4 }, "w": { "a": 0, "k": 2, "ix": 5 }, "lc": 1, "lj": 1, "ml": 4, "ml2": { "a": 0, "k": 4, "ix": 8 }, "nm": "Stroke 2", "mn": "ADBE Vector Graphic - Stroke", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 2", "np": 4, "cix": 2, "ix": 2, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ 0, 0 ] ], "v": [ [ 134.09, -15.014 ], [ 134.09, 2.172 ] ], "c": false }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "st", "c": { "a": 0, "k": [ 1, 1, 1, 1 ], "ix": 3 }, "o": { "a": 0, "k": 100, "ix": 4 }, "w": { "a": 0, "k": 3, "ix": 5 }, "lc": 2, "lj": 1, "ml": 10, "ml2": { "a": 0, "k": 10, "ix": 8 }, "nm": "Stroke 1", "mn": "ADBE Vector Graphic - Stroke", "hd": false }, { "ty": "tm", "s": { "a": 1, "k": [ { "i": { "x": [ 0.667 ], "y": [ 1 ] }, "o": { "x": [ 1 ], "y": [ 0 ] }, "n": [ "0p667_1_1_0" ], "t": 14, "s": [ 100 ], "e": [ 0 ] }, { "t": 19 } ], "ix": 1 }, "e": { "a": 1, "k": [ { "i": { "x": [ 0 ], "y": [ 1 ] }, "o": { "x": [ 0.333 ], "y": [ 0 ] }, "n": [ "0_1_0p333_0" ], "t": 19, "s": [ 100 ], "e": [ 0 ] }, { "t": 24 } ], "ix": 2 }, "o": { "a": 0, "k": 0, "ix": 3 }, "m": 1, "ix": 3, "nm": "Trim Paths 1", "mn": "ADBE Vector Filter - Trim", "hd": false }, { "ty": "st", "c": { "a": 0, "k": [ 1, 1, 1, 1 ], "ix": 3 }, "o": { "a": 0, "k": 100, "ix": 4 }, "w": { "a": 0, "k": 2, "ix": 5 }, "lc": 1, "lj": 1, "ml": 4, "ml2": { "a": 0, "k": 4, "ix": 8 }, "nm": "Stroke 2", "mn": "ADBE Vector Graphic - Stroke", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 3", "np": 4, "cix": 2, "ix": 3, "mn": "ADBE Vector Group", "hd": false } ], "ip": -1, "op": 449, "st": -1, "bm": 0 }, { "ddd": 0, "ind": 11, "ty": 4, "nm": "Layer 1", "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 1, "k": [ { "i": { "x": [ 0.667 ], "y": [ 1 ] }, "o": { "x": [ 1 ], "y": [ 0 ] }, "n": [ "0p667_1_1_0" ], "t": 15, "s": [ 0 ], "e": [ 2 ] }, { "i": { "x": [ 0 ], "y": [ 1 ] }, "o": { "x": [ 0.333 ], "y": [ 0 ] }, "n": [ "0_1_0p333_0" ], "t": 20, "s": [ 2 ], "e": [ 0 ] }, { "t": 25 } ], "ix": 10 }, "p": { "a": 1, "k": [ { "i": { "x": 0.667, "y": 1 }, "o": { "x": 1, "y": 0 }, "n": "0p667_1_1_0", "t": 15, "s": [ 57.935, 269.325, 0 ], "e": [ 57.935, 277.325, 0 ], "to": [ 0, 1.33333337306976, 0 ], "ti": [ 0, 0, 0 ] }, { "i": { "x": 0, "y": 1 }, "o": { "x": 0.333, "y": 0 }, "n": "0_1_0p333_0", "t": 20, "s": [ 57.935, 277.325, 0 ], "e": [ 57.935, 269.325, 0 ], "to": [ 0, 0, 0 ], "ti": [ 0, 1.33333337306976, 0 ] }, { "t": 25 } ], "ix": 2 }, "a": { "a": 0, "k": [ -90.065, 121.325, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100, 100 ], "ix": 6 } }, "ao": 0, "shapes": [ { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 1.89, 0 ], [ 0, 1.89 ], [ -1.89, 0 ], [ 0, -1.89 ] ], "o": [ [ 0, 1.89 ], [ -1.89, 0 ], [ 0, -1.89 ], [ 1.89, 0 ], [ 0, 0 ] ], "v": [ [ 84.575, 85.183 ], [ 81.154, 88.604 ], [ 77.733, 85.183 ], [ 81.154, 81.761 ], [ 84.575, 85.183 ] ], "c": false }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.239215686917, 0.239215686917, 0.239215686917, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 1", "np": 2, "cix": 2, "ix": 1, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 2.165, 0 ], [ 0, 2.166 ], [ -2.166, 0 ], [ 0, -2.168 ] ], "o": [ [ 0, 2.166 ], [ -2.166, 0 ], [ 0, -2.168 ], [ 2.165, 0 ], [ 0, 0 ] ], "v": [ [ 85.077, 85.183 ], [ 81.155, 89.104 ], [ 77.232, 85.183 ], [ 81.155, 81.261 ], [ 85.077, 85.183 ] ], "c": false }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 1, 1, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 2", "np": 2, "cix": 2, "ix": 2, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 1.89, 0 ], [ 0, 1.89 ], [ -1.89, 0 ], [ 0, -1.89 ] ], "o": [ [ 0, 1.89 ], [ -1.89, 0 ], [ 0, -1.89 ], [ 1.89, 0 ], [ 0, 0 ] ], "v": [ [ 65.636, 85.183 ], [ 62.215, 88.604 ], [ 58.795, 85.183 ], [ 62.215, 81.761 ], [ 65.636, 85.183 ] ], "c": false }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.239215686917, 0.239215686917, 0.239215686917, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 3", "np": 2, "cix": 2, "ix": 3, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 2.165, 0 ], [ 0, 2.166 ], [ -2.168, 0 ], [ 0, -2.168 ] ], "o": [ [ 0, 2.166 ], [ -2.168, 0 ], [ 0, -2.168 ], [ 2.165, 0 ], [ 0, 0 ] ], "v": [ [ 66.137, 85.183 ], [ 62.216, 89.104 ], [ 58.292, 85.183 ], [ 62.216, 81.261 ], [ 66.137, 85.183 ] ], "c": false }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 1, 1, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 4", "np": 2, "cix": 2, "ix": 4, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 4.852, 0 ], [ 0, 4.852 ], [ -4.852, 0 ], [ 0, -4.852 ] ], "o": [ [ 0, 4.852 ], [ -4.852, 0 ], [ 0, -4.852 ], [ 4.852, 0 ], [ 0, 0 ] ], "v": [ [ 80.469, 50.173 ], [ 71.685, 58.958 ], [ 62.901, 50.173 ], [ 71.685, 41.389 ], [ 80.469, 50.173 ] ], "c": false }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.286274522543, 0.286274522543, 0.286274522543, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 5", "np": 2, "cix": 2, "ix": 5, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 5.619, 0 ], [ 0, 5.621 ], [ -5.619, 0 ], [ 0, -5.619 ] ], "o": [ [ 0, 5.621 ], [ -5.619, 0 ], [ 0, -5.619 ], [ 5.619, 0 ], [ 0, 0 ] ], "v": [ [ 81.86, 50.173 ], [ 71.685, 60.349 ], [ 61.51, 50.173 ], [ 71.685, 39.998 ], [ 81.86, 50.173 ] ], "c": false }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 1, 1, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ind": 2, "ty": "sh", "ix": 3, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 5.619, 0 ], [ 0, 5.619 ], [ -5.619, 0 ], [ 0, -5.619 ] ], "o": [ [ 0, 5.619 ], [ -5.619, 0 ], [ 0, -5.619 ], [ 5.619, 0 ], [ 0, 0 ] ], "v": [ [ 81.86, 19.259 ], [ 71.685, 29.434 ], [ 61.51, 19.259 ], [ 71.685, 9.084 ], [ 81.86, 19.259 ] ], "c": false }, "ix": 2 }, "nm": "Path 2", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "ring 2", "np": 3, "cix": 2, "ix": 6, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 6.327, 0 ], [ 0, 6.327 ], [ -6.327, 0 ], [ 0, -6.327 ] ], "o": [ [ 0, 6.327 ], [ -6.327, 0 ], [ 0, -6.327 ], [ 6.327, 0 ], [ 0, 0 ] ], "v": [ [ 83.14, 50.173 ], [ 71.685, 61.629 ], [ 60.229, 50.173 ], [ 71.685, 38.718 ], [ 83.14, 50.173 ] ], "c": false }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.239215686917, 0.239215686917, 0.239215686917, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 7", "np": 2, "cix": 2, "ix": 7, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 4.852, 0 ], [ 0, 4.852 ], [ -4.852, 0 ], [ 0, -4.852 ] ], "o": [ [ 0, 4.852 ], [ -4.852, 0 ], [ 0, -4.852 ], [ 4.852, 0 ], [ 0, 0 ] ], "v": [ [ 80.469, 19.259 ], [ 71.685, 28.043 ], [ 62.901, 19.259 ], [ 71.685, 10.475 ], [ 80.469, 19.259 ] ], "c": false }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.286274522543, 0.286274522543, 0.286274522543, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 8", "np": 2, "cix": 2, "ix": 8, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 5.619, 0 ], [ 0, 5.619 ], [ -5.619, 0 ], [ 0, -5.619 ] ], "o": [ [ 0, 5.619 ], [ -5.619, 0 ], [ 0, -5.619 ], [ 5.619, 0 ], [ 0, 0 ] ], "v": [ [ 81.86, 19.259 ], [ 71.685, 29.434 ], [ 61.51, 19.259 ], [ 71.685, 9.084 ], [ 81.86, 19.259 ] ], "c": false }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 1, 1, 1, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "ring", "np": 2, "cix": 2, "ix": 9, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 6.327, 0 ], [ 0, 6.327 ], [ -6.327, 0 ], [ 0, -6.327 ] ], "o": [ [ 0, 6.327 ], [ -6.327, 0 ], [ 0, -6.327 ], [ 6.327, 0 ], [ 0, 0 ] ], "v": [ [ 83.14, 19.259 ], [ 71.685, 30.715 ], [ 60.229, 19.259 ], [ 71.685, 7.804 ], [ 83.14, 19.259 ] ], "c": false }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.239215686917, 0.239215686917, 0.239215686917, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 10", "np": 2, "cix": 2, "ix": 10, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 7.703, 24.927 ], [ 3.516, 3.514 ], [ 8.515, 2.094 ], [ 9.641, -0.008 ], [ 8.361, -2.091 ], [ 4.204, -4.202 ], [ -16.772, -16.774 ], [ -8.498, -2.1 ], [ -9.651, 0.003 ], [ -8.342, 2.079 ], [ -4.215, 4.215 ] ], "o": [ [ 13.255, -13.255 ], [ -2.039, -6.611 ], [ -4.247, -4.247 ], [ -8.294, -2.046 ], [ -9.738, -0.002 ], [ -8.393, 2.092 ], [ -16.774, 16.775 ], [ 4.238, 4.238 ], [ 8.308, 2.05 ], [ 9.712, 0.003 ], [ 8.429, -2.097 ], [ 0, 0 ] ], "v": [ [ 37.551, 92.832 ], [ 45.883, 13.547 ], [ 37.549, -2.046 ], [ 17.682, -11.562 ], [ -9.89, -14.624 ], [ -37.729, -11.494 ], [ -57.329, -2.048 ], [ -57.329, 92.832 ], [ -37.506, 102.337 ], [ -9.886, 105.408 ], [ 17.879, 102.298 ], [ 37.551, 92.832 ] ], "c": false }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.153890931373, 0.148028085746, 0.148028085746, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "screen", "np": 2, "cix": 2, "ix": 11, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 3.709, 3.709 ], [ 8.987, 2.21 ], [ 10.172, -0.008 ], [ 8.819, -2.206 ], [ 4.435, -4.435 ], [ -17.697, -17.695 ], [ -8.966, -2.216 ], [ -10.183, 0.003 ], [ -8.8, 2.194 ], [ -4.445, 4.448 ], [ 8.128, 26.299 ] ], "o": [ [ -2.152, -6.973 ], [ -4.48, -4.482 ], [ -8.747, -2.157 ], [ -10.273, -0.002 ], [ -8.856, 2.206 ], [ -17.697, 17.697 ], [ 4.469, 4.471 ], [ 8.766, 2.163 ], [ 10.246, 0.003 ], [ 8.892, -2.213 ], [ 13.985, -13.983 ], [ 0, 0 ] ], "v": [ [ 48.953, 11.794 ], [ 40.161, -4.656 ], [ 19.198, -14.697 ], [ -9.89, -17.927 ], [ -39.261, -14.624 ], [ -59.939, -4.658 ], [ -59.939, 95.442 ], [ -39.026, 105.471 ], [ -9.887, 108.711 ], [ 19.407, 105.429 ], [ 40.161, 95.444 ], [ 48.953, 11.794 ] ], "c": false }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.37331495098, 0.37331495098, 0.37331495098, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 12", "np": 2, "cix": 2, "ix": 12, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 8.869, 29.673 ], [ 4.048, 4.185 ], [ 12.936, 3.303 ], [ 27.307, -9.402 ], [ 6.201, -6.41 ], [ -19.311, -19.966 ], [ -9.321, -3.178 ], [ -30.476, 10.498 ], [ -5.735, 3.831 ] ], "o": [ [ 15.26, -15.778 ], [ -2.348, -7.869 ], [ -4.281, -4.426 ], [ -13.562, -3.466 ], [ -9.204, 3.17 ], [ -19.311, 19.966 ], [ 6.259, 6.471 ], [ 7.012, 2.391 ], [ 9.347, -3.22 ], [ 0, 0 ] ], "v": [ [ 80.546, 101.767 ], [ 90.139, 7.387 ], [ 80.544, -11.175 ], [ 57.668, -22.502 ], [ -48.545, -22.421 ], [ -71.112, -11.175 ], [ -71.11, 101.765 ], [ -48.288, 113.079 ], [ 57.898, 113.034 ], [ 80.546, 101.767 ] ], "c": false }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.286274522543, 0.286274522543, 0.286274522543, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0.141, 0.33 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 13", "np": 2, "cix": 2, "ix": 13, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 9.238, 30.907 ], [ 4.215, 4.358 ], [ 13.472, 3.442 ], [ 28.44, -9.792 ], [ 6.457, -6.677 ], [ -20.112, -20.797 ], [ -9.709, -3.311 ], [ -31.743, 10.936 ], [ -5.975, 3.988 ] ], "o": [ [ 15.896, -16.434 ], [ -2.446, -8.195 ], [ -4.458, -4.611 ], [ -14.13, -3.609 ], [ -9.588, 3.302 ], [ -20.114, 20.797 ], [ 6.52, 6.74 ], [ 7.304, 2.491 ], [ 9.738, -3.355 ], [ 0, 0 ] ], "v": [ [ 82.983, 104.111 ], [ 92.975, 5.806 ], [ 82.982, -13.526 ], [ 59.156, -25.326 ], [ -51.475, -25.24 ], [ -74.98, -13.528 ], [ -74.98, 104.11 ], [ -51.209, 115.895 ], [ 59.392, 115.847 ], [ 82.983, 104.111 ] ], "c": false }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.239215686917, 0.239215686917, 0.239215686917, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 14", "np": 2, "cix": 2, "ix": 14, "mn": "ADBE Vector Group", "hd": false }, { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 0, 14.329 ], [ -14.331, 0 ], [ 0, -14.331 ], [ 14.331, 0 ] ], "o": [ [ 0, 0 ], [ -14.331, 0 ], [ 0, -14.331 ], [ 14.331, 0 ], [ 0, 14.329 ], [ 0, 0 ] ], "v": [ [ 2.214, 6.783 ], [ 2.214, 6.783 ], [ -23.733, -19.164 ], [ 2.214, -45.112 ], [ 28.163, -19.164 ], [ 2.214, 6.783 ] ], "c": false }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.239215686917, 0.239215686917, 0.239215686917, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 19", "np": 2, "cix": 2, "ix": 15, "mn": "ADBE Vector Group", "hd": false } ], "ip": 0, "op": 450, "st": 0, "bm": 0 }, { "ddd": 0, "ind": 12, "ty": 4, "nm": "left shoe", "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 1, "k": [ { "i": { "x": [ 0.667 ], "y": [ 1 ] }, "o": { "x": [ 1 ], "y": [ 0 ] }, "n": [ "0p667_1_1_0" ], "t": 15, "s": [ 0 ], "e": [ 5 ] }, { "i": { "x": [ 0 ], "y": [ 1 ] }, "o": { "x": [ 0.333 ], "y": [ 0 ] }, "n": [ "0_1_0p333_0" ], "t": 20, "s": [ 5 ], "e": [ 0 ] }, { "t": 25 } ], "ix": 10 }, "p": { "a": 0, "k": [ 106.319, 266.292, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ -41.681, 118.292, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100, 100 ], "ix": 6 } }, "ao": 0, "shapes": [ { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ -2.118, 3.794 ] ], "o": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ 1.174, 4.183 ], [ 0, 0 ] ], "v": [ [ -40.571, 132.473 ], [ -25.49, 105.443 ], [ -57.873, 101.613 ], [ -49.513, 131.417 ], [ -40.571, 132.473 ] ], "c": false }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.239215686917, 0.239215686917, 0.239215686917, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 1", "np": 2, "cix": 2, "ix": 1, "mn": "ADBE Vector Group", "hd": false } ], "ip": 0, "op": 450, "st": 0, "bm": 0 }, { "ddd": 0, "ind": 13, "ty": 4, "nm": "right shoe", "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 1, "k": [ { "i": { "x": [ 0.667 ], "y": [ 1 ] }, "o": { "x": [ 1 ], "y": [ 0 ] }, "n": [ "0p667_1_1_0" ], "t": 15, "s": [ 0 ], "e": [ -15 ] }, { "i": { "x": [ 0.667 ], "y": [ 1 ] }, "o": { "x": [ 0.333 ], "y": [ 0 ] }, "n": [ "0p667_1_0p333_0" ], "t": 20, "s": [ -15 ], "e": [ 0 ] }, { "t": 25 } ], "ix": 10 }, "p": { "a": 0, "k": [ 201.432, 266.292, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 53.432, 118.292, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100, 100 ], "ix": 6 } }, "ao": 0, "shapes": [ { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ 2.116, 3.794 ] ], "o": [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ -1.176, 4.183 ], [ 0, 0 ] ], "v": [ [ 52.323, 132.473 ], [ 37.242, 105.443 ], [ 69.623, 101.613 ], [ 61.265, 131.417 ], [ 52.323, 132.473 ] ], "c": false }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.239215686917, 0.239215686917, 0.239215686917, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 1", "np": 2, "cix": 2, "ix": 1, "mn": "ADBE Vector Group", "hd": false } ], "ip": 0, "op": 450, "st": 0, "bm": 0 }, { "ddd": 0, "ind": 14, "ty": 4, "nm": "left anteena", "parent": 11, "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 1, "k": [ { "i": { "x": [ 0.667 ], "y": [ 1 ] }, "o": { "x": [ 0.333 ], "y": [ 0 ] }, "n": [ "0p667_1_0p333_0" ], "t": 15, "s": [ 0 ], "e": [ -8 ] }, { "i": { "x": [ 0.667 ], "y": [ 1 ] }, "o": { "x": [ 0.333 ], "y": [ 0 ] }, "n": [ "0p667_1_0p333_0" ], "t": 17.143, "s": [ -8 ], "e": [ 8 ] }, { "i": { "x": [ 0.667 ], "y": [ 1 ] }, "o": { "x": [ 0.333 ], "y": [ 0 ] }, "n": [ "0p667_1_0p333_0" ], "t": 19.286, "s": [ 8 ], "e": [ -4 ] }, { "i": { "x": [ 0.667 ], "y": [ 1 ] }, "o": { "x": [ 0.333 ], "y": [ 0 ] }, "n": [ "0p667_1_0p333_0" ], "t": 21.429, "s": [ -4 ], "e": [ 4 ] }, { "i": { "x": [ 0.667 ], "y": [ 1 ] }, "o": { "x": [ 0.333 ], "y": [ 0 ] }, "n": [ "0p667_1_0p333_0" ], "t": 23.571, "s": [ 4 ], "e": [ -2 ] }, { "i": { "x": [ 0.667 ], "y": [ 1 ] }, "o": { "x": [ 0.333 ], "y": [ 0 ] }, "n": [ "0p667_1_0p333_0" ], "t": 25.714, "s": [ -2 ], "e": [ 2 ] }, { "i": { "x": [ 0.667 ], "y": [ 1 ] }, "o": { "x": [ 0.333 ], "y": [ 0 ] }, "n": [ "0p667_1_0p333_0" ], "t": 27.857, "s": [ 2 ], "e": [ 0 ] }, { "t": 30 } ], "ix": 10 }, "p": { "a": 0, "k": [ 0, 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100, 100 ], "ix": 6 } }, "ao": 0, "shapes": [ { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 0, 0 ], [ 0.162, 2.252 ], [ 3.792, -0.273 ], [ -0.273, -3.792 ], [ -3.193, -0.398 ], [ 0, 0 ] ], "o": [ [ 0, 0 ], [ 1.584, -1.373 ], [ -0.275, -3.792 ], [ -3.792, 0.273 ], [ 0.242, 3.332 ], [ 0, 0 ], [ 0, 0 ] ], "v": [ [ 15.619, -7.348 ], [ -30.297, -78.134 ], [ -27.93, -83.83 ], [ -35.293, -90.2 ], [ -41.663, -82.837 ], [ -35.654, -76.504 ], [ 9.491, -6.906 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.239215686917, 0.239215686917, 0.239215686917, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 1", "np": 2, "cix": 2, "ix": 1, "mn": "ADBE Vector Group", "hd": false } ], "ip": 0, "op": 450, "st": 0, "bm": 0 }, { "ddd": 0, "ind": 15, "ty": 4, "nm": "right anteena", "parent": 11, "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 1, "k": [ { "i": { "x": [ 0.667 ], "y": [ 1 ] }, "o": { "x": [ 0.333 ], "y": [ 0 ] }, "n": [ "0p667_1_0p333_0" ], "t": 15, "s": [ 0 ], "e": [ 8 ] }, { "i": { "x": [ 0.667 ], "y": [ 1 ] }, "o": { "x": [ 0.333 ], "y": [ 0 ] }, "n": [ "0p667_1_0p333_0" ], "t": 17.143, "s": [ 8 ], "e": [ -8 ] }, { "i": { "x": [ 0.667 ], "y": [ 1 ] }, "o": { "x": [ 0.333 ], "y": [ 0 ] }, "n": [ "0p667_1_0p333_0" ], "t": 19.286, "s": [ -8 ], "e": [ -4 ] }, { "i": { "x": [ 0.667 ], "y": [ 1 ] }, "o": { "x": [ 0.333 ], "y": [ 0 ] }, "n": [ "0p667_1_0p333_0" ], "t": 21.429, "s": [ -4 ], "e": [ 4 ] }, { "i": { "x": [ 0.667 ], "y": [ 1 ] }, "o": { "x": [ 0.333 ], "y": [ 0 ] }, "n": [ "0p667_1_0p333_0" ], "t": 23.571, "s": [ 4 ], "e": [ -2 ] }, { "i": { "x": [ 0.667 ], "y": [ 1 ] }, "o": { "x": [ 0.333 ], "y": [ 0 ] }, "n": [ "0p667_1_0p333_0" ], "t": 25.714, "s": [ -2 ], "e": [ 2 ] }, { "i": { "x": [ 0.667 ], "y": [ 1 ] }, "o": { "x": [ 0.333 ], "y": [ 0 ] }, "n": [ "0p667_1_0p333_0" ], "t": 27.857, "s": [ 2 ], "e": [ 0 ] }, { "t": 30 } ], "ix": 10 }, "p": { "a": 0, "k": [ -19.243, 7.804, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ -19.243, 7.804, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100, 100 ], "ix": 6 } }, "ao": 0, "shapes": [ { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 3.721, 0.777 ], [ 0.777, -3.721 ], [ -0.912, -1.454 ], [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ -0.745, 3.577 ] ], "o": [ [ -3.723, -0.777 ], [ -0.378, 1.813 ], [ 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ 3.568, 0.514 ], [ 0.777, -3.721 ] ], "v": [ [ 30.836, -73.957 ], [ 22.691, -68.624 ], [ 23.601, -63.564 ], [ -19.243, 6.549 ], [ -13.23, 7.804 ], [ 28.455, -60.411 ], [ 36.167, -65.811 ] ], "c": true }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0.239215686917, 0.239215686917, 0.239215686917, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 1", "np": 2, "cix": 2, "ix": 1, "mn": "ADBE Vector Group", "hd": false } ], "ip": 0, "op": 450, "st": 0, "bm": 0 }, { "ddd": 0, "ind": 16, "ty": 4, "nm": "Layer 7", "sr": 1, "ks": { "o": { "a": 0, "k": 100, "ix": 11 }, "r": { "a": 0, "k": 0, "ix": 10 }, "p": { "a": 0, "k": [ 148, 148, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100, 100 ], "ix": 6 } }, "ao": 0, "shapes": [ { "ty": "gr", "it": [ { "ind": 0, "ty": "sh", "ix": 1, "ks": { "a": 0, "k": { "i": [ [ 0, 0 ], [ 18.24, 59.015 ], [ 8.322, 8.322 ], [ 20.161, 4.959 ], [ 22.824, -0.019 ], [ 19.787, -4.947 ], [ 9.952, -9.952 ], [ -39.706, -39.706 ], [ -20.117, -4.976 ], [ -22.843, 0.004 ], [ -19.746, 4.922 ], [ -9.977, 9.977 ] ], "o": [ [ 31.38, -31.38 ], [ -4.824, -15.641 ], [ -10.054, -10.054 ], [ -19.635, -4.837 ], [ -23.052, 0.001 ], [ -19.875, 4.952 ], [ -39.708, 39.708 ], [ 10.033, 10.033 ], [ 19.67, 4.855 ], [ 22.992, 0.007 ], [ 19.953, -4.961 ], [ 0, 0 ] ], "v": [ [ 112.305, 112.306 ], [ 132.028, -75.39 ], [ 112.3, -112.299 ], [ 65.267, -134.827 ], [ 0, -142.079 ], [ -65.902, -134.666 ], [ -112.301, -112.299 ], [ -112.301, 112.302 ], [ -65.378, 134.806 ], [ 0.002, 142.079 ], [ 65.737, 134.711 ], [ 112.305, 112.306 ] ], "c": false }, "ix": 2 }, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false }, { "ty": "fl", "c": { "a": 0, "k": [ 0, 0, 0, 1 ], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false }, { "ty": "tr", "p": { "a": 0, "k": [ 0, 0 ], "ix": 2 }, "a": { "a": 0, "k": [ 0, 0 ], "ix": 1 }, "s": { "a": 0, "k": [ 100, 100 ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } ], "nm": "Group 1", "np": 2, "cix": 2, "ix": 1, "mn": "ADBE Vector Group", "hd": false } ], "ip": 0, "op": 0, "st": 0, "bm": 0 } ], "markers": [] } ================================================ FILE: feature/base/src/main/res/values/color_palete.xml ================================================ #2B2E30 #1A1E21 #3D3D3D #FB3430 #FFFFFF ================================================ FILE: feature/base/src/main/res/values/ids.xml ================================================ ================================================ FILE: feature/base/src/main/res/values/strings.xml ================================================ Under construction Data not found ================================================ FILE: feature/base/src/main/res/values/styles.xml ================================================ ================================================ FILE: feature/favourite/build.gradle.kts ================================================ plugins { id("com.igorwojda.showcase.convention.feature") } android { namespace = "com.igorwojda.showcase.feature.favourite" } ================================================ FILE: feature/favourite/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: feature/favourite/src/main/AndroidManifest.xml ================================================ ================================================ FILE: feature/favourite/src/main/kotlin/com/igorwojda/showcase/feature/favourite/FavouriteKoinModule.kt ================================================ package com.igorwojda.showcase.feature.favourite import com.igorwojda.showcase.feature.favourite.data.dataModule import com.igorwojda.showcase.feature.favourite.domain.domainModule import com.igorwojda.showcase.feature.favourite.presentation.presentationModule val featureFavouriteModules = listOf( presentationModule, domainModule, dataModule, ) ================================================ FILE: feature/favourite/src/main/kotlin/com/igorwojda/showcase/feature/favourite/data/DataModule.kt ================================================ package com.igorwojda.showcase.feature.favourite.data import org.koin.dsl.module internal val dataModule = module { } ================================================ FILE: feature/favourite/src/main/kotlin/com/igorwojda/showcase/feature/favourite/domain/DomainModule.kt ================================================ package com.igorwojda.showcase.feature.favourite.domain import org.koin.dsl.module internal val domainModule = module { } ================================================ FILE: feature/favourite/src/main/kotlin/com/igorwojda/showcase/feature/favourite/presentation/PresentationModule.kt ================================================ package com.igorwojda.showcase.feature.favourite.presentation import org.koin.dsl.module internal val presentationModule = module { } ================================================ FILE: feature/favourite/src/main/kotlin/com/igorwojda/showcase/feature/favourite/presentation/screen/favourite/FavouriteScreen.kt ================================================ package com.igorwojda.showcase.feature.favourite.presentation.screen.favourite import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import com.igorwojda.showcase.feature.base.presentation.compose.composable.UnderConstructionAnim @Composable fun FavouriteScreen(modifier: Modifier = Modifier) { Box( modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center, ) { UnderConstructionAnim() } } @Preview @Composable private fun FavouriteScreenPreview() { FavouriteScreen() } ================================================ FILE: feature/settings/build.gradle.kts ================================================ plugins { id("com.igorwojda.showcase.convention.feature") } android { namespace = "com.igorwojda.showcase.feature.settings" } dependencies { implementation(libs.aboutlibraries.compose) } ================================================ FILE: feature/settings/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: feature/settings/src/main/AndroidManifest.xml ================================================ ================================================ FILE: feature/settings/src/main/kotlin/com/igorwojda/showcase/feature/settings/SettingsKoinModule.kt ================================================ package com.igorwojda.showcase.feature.settings import com.igorwojda.showcase.feature.settings.data.dataModule import com.igorwojda.showcase.feature.settings.domain.domainModule import com.igorwojda.showcase.feature.settings.presentation.presentationModule val featureSettingsModules = listOf( presentationModule, domainModule, dataModule, ) ================================================ FILE: feature/settings/src/main/kotlin/com/igorwojda/showcase/feature/settings/data/DataModule.kt ================================================ package com.igorwojda.showcase.feature.settings.data import org.koin.dsl.module internal val dataModule = module { } ================================================ FILE: feature/settings/src/main/kotlin/com/igorwojda/showcase/feature/settings/domain/DomainModule.kt ================================================ package com.igorwojda.showcase.feature.settings.domain import org.koin.dsl.module internal val domainModule = module { } ================================================ FILE: feature/settings/src/main/kotlin/com/igorwojda/showcase/feature/settings/presentation/PresentationModule.kt ================================================ package com.igorwojda.showcase.feature.settings.presentation import com.igorwojda.showcase.feature.settings.presentation.screen.aboutlibraries.AboutLibrariesViewModel import com.igorwojda.showcase.feature.settings.presentation.screen.settings.SettingsViewModel import org.koin.core.module.dsl.viewModelOf import org.koin.dsl.module internal val presentationModule = module { viewModelOf(::SettingsViewModel) viewModelOf(::AboutLibrariesViewModel) } ================================================ FILE: feature/settings/src/main/kotlin/com/igorwojda/showcase/feature/settings/presentation/screen/aboutlibraries/AboutLibrariesAction.kt ================================================ package com.igorwojda.showcase.feature.settings.presentation.screen.aboutlibraries import com.igorwojda.showcase.feature.base.presentation.viewmodel.BaseAction internal sealed class AboutLibrariesAction : BaseAction ================================================ FILE: feature/settings/src/main/kotlin/com/igorwojda/showcase/feature/settings/presentation/screen/aboutlibraries/AboutLibrariesScreen.kt ================================================ package com.igorwojda.showcase.feature.settings.presentation.screen.aboutlibraries import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.igorwojda.showcase.feature.settings.R import com.mikepenz.aboutlibraries.ui.compose.m3.LibrariesContainer import org.koin.androidx.compose.koinViewModel @OptIn(ExperimentalMaterial3Api::class) @Composable fun AboutLibrariesScreen( onBackClick: () -> Unit, modifier: Modifier = Modifier, ) { val viewModel: AboutLibrariesViewModel = koinViewModel() val uiState by viewModel.uiStateFlow.collectAsStateWithLifecycle() when (uiState) { is AboutLibrariesUiState.Content -> { AboutLibrariesContent( onBackClick = onBackClick, modifier = modifier, ) } } } @OptIn(ExperimentalMaterial3Api::class) @Composable private fun AboutLibrariesContent( onBackClick: () -> Unit, modifier: Modifier = Modifier, ) { Scaffold( modifier = modifier, topBar = { TopAppBar( title = { Text(stringResource(R.string.about_libraries_screen_title)) }, navigationIcon = { IconButton(onClick = onBackClick) { Icon( imageVector = Icons.AutoMirrored.Filled.ArrowBack, contentDescription = stringResource(R.string.about_libraries_screen_back), ) } }, ) }, ) { paddingValues -> LibrariesContainer( modifier = Modifier .fillMaxSize() .padding(paddingValues), ) } } @Preview @Composable private fun AboutLibrariesScreenPreview() { AboutLibrariesContent( onBackClick = { }, ) } ================================================ FILE: feature/settings/src/main/kotlin/com/igorwojda/showcase/feature/settings/presentation/screen/aboutlibraries/AboutLibrariesUiState.kt ================================================ package com.igorwojda.showcase.feature.settings.presentation.screen.aboutlibraries import androidx.compose.runtime.Immutable import com.igorwojda.showcase.feature.base.presentation.viewmodel.BaseState @Immutable internal sealed interface AboutLibrariesUiState : BaseState { @Immutable data object Content : AboutLibrariesUiState } ================================================ FILE: feature/settings/src/main/kotlin/com/igorwojda/showcase/feature/settings/presentation/screen/aboutlibraries/AboutLibrariesViewModel.kt ================================================ package com.igorwojda.showcase.feature.settings.presentation.screen.aboutlibraries import com.igorwojda.showcase.feature.base.presentation.viewmodel.BaseViewModel internal class AboutLibrariesViewModel : BaseViewModel(AboutLibrariesUiState.Content) ================================================ FILE: feature/settings/src/main/kotlin/com/igorwojda/showcase/feature/settings/presentation/screen/settings/SettingsAction.kt ================================================ package com.igorwojda.showcase.feature.settings.presentation.screen.settings import com.igorwojda.showcase.feature.base.presentation.viewmodel.BaseAction internal sealed class SettingsAction : BaseAction ================================================ FILE: feature/settings/src/main/kotlin/com/igorwojda/showcase/feature/settings/presentation/screen/settings/SettingsScreen.kt ================================================ package com.igorwojda.showcase.feature.settings.presentation.screen.settings import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowForward import androidx.compose.material.icons.filled.Info import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.ListItem import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.igorwojda.showcase.feature.settings.R import org.koin.androidx.compose.koinViewModel @OptIn(ExperimentalMaterial3Api::class) @Composable fun SettingsScreen( onNavigateToAboutLibraries: () -> Unit, modifier: Modifier = Modifier, ) { val viewModel: SettingsViewModel = koinViewModel() val uiState by viewModel.uiStateFlow.collectAsStateWithLifecycle() Column( modifier = modifier .fillMaxSize() .verticalScroll(rememberScrollState()), ) { when (uiState) { SettingsUiState.Content -> SettingsContent(onNavigateToAboutLibraries) } } } @Composable private fun SettingsContent(onNavigateToAboutLibraries: () -> Unit) { Column( modifier = Modifier.padding(vertical = 8.dp), ) { Card( modifier = Modifier .fillMaxWidth(), colors = CardDefaults.cardColors( containerColor = MaterialTheme.colorScheme.surface, ), ) { SettingsItem( title = stringResource(R.string.settings_screen_open_source_licenses), subtitle = stringResource(R.string.settings_screen_view_licenses_description), icon = { Icon( imageVector = Icons.Default.Info, contentDescription = stringResource(R.string.settings_screen_licenses), tint = MaterialTheme.colorScheme.primary, ) }, onClick = onNavigateToAboutLibraries, ) } } } @Composable private fun SettingsItem( title: String, subtitle: String? = null, icon: (@Composable () -> Unit)? = null, enabled: Boolean = true, onClick: () -> Unit, ) { ListItem( modifier = Modifier.clickable( enabled = enabled, onClick = onClick, ), headlineContent = { Text( text = title, style = MaterialTheme.typography.bodyLarge, ) }, supportingContent = subtitle?.let { { Text( text = it, style = MaterialTheme.typography.bodyMedium, ) } }, leadingContent = icon, trailingContent = if (enabled) { { Icon( imageVector = Icons.AutoMirrored.Filled.ArrowForward, contentDescription = stringResource(R.string.settings_screen_navigate), tint = MaterialTheme.colorScheme.onSurfaceVariant, ) } } else { null }, ) } @Preview @Composable private fun SettingsScreenPreview() { SettingsContent( onNavigateToAboutLibraries = { }, ) } ================================================ FILE: feature/settings/src/main/kotlin/com/igorwojda/showcase/feature/settings/presentation/screen/settings/SettingsUiState.kt ================================================ package com.igorwojda.showcase.feature.settings.presentation.screen.settings import androidx.compose.runtime.Immutable import com.igorwojda.showcase.feature.base.presentation.viewmodel.BaseState @Immutable internal sealed interface SettingsUiState : BaseState { @Immutable data object Content : SettingsUiState } ================================================ FILE: feature/settings/src/main/kotlin/com/igorwojda/showcase/feature/settings/presentation/screen/settings/SettingsViewModel.kt ================================================ package com.igorwojda.showcase.feature.settings.presentation.screen.settings import com.igorwojda.showcase.feature.base.presentation.viewmodel.BaseViewModel internal class SettingsViewModel : BaseViewModel(SettingsUiState.Content) ================================================ FILE: feature/settings/src/main/res/values/strings.xml ================================================ Settings About Open Source Licenses View licenses of third-party libraries Licenses Navigate Open Source Licenses Back ================================================ FILE: feature/settings/src/test/kotlin/com/igorwojda/showcase/feature/settings/presentation/screen/aboutlibraries/AboutLibrariesViewModelTest.kt ================================================ package com.igorwojda.showcase.feature.settings.presentation.screen.aboutlibraries import com.igorwojda.showcase.library.testutils.CoroutinesTestDispatcherExtension import com.igorwojda.showcase.library.testutils.InstantTaskExecutorExtension import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.amshove.kluent.shouldBeEqualTo import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(InstantTaskExecutorExtension::class, CoroutinesTestDispatcherExtension::class) class AboutLibrariesViewModelTest { private val sut = AboutLibrariesViewModel() @Test fun `initial state should be Content`() = runTest { // then sut.uiStateFlow.value shouldBeEqualTo AboutLibrariesUiState.Content } } ================================================ FILE: feature/settings/src/test/kotlin/com/igorwojda/showcase/feature/settings/presentation/screen/settings/SettingsViewModelTest.kt ================================================ package com.igorwojda.showcase.feature.settings.presentation.screen.settings import com.igorwojda.showcase.library.testutils.CoroutinesTestDispatcherExtension import com.igorwojda.showcase.library.testutils.InstantTaskExecutorExtension import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.amshove.kluent.shouldBeEqualTo import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(InstantTaskExecutorExtension::class, CoroutinesTestDispatcherExtension::class) class SettingsViewModelTest { private val sut = SettingsViewModel() @Test fun `initial state should be Content`() = runTest { // then sut.uiStateFlow.value shouldBeEqualTo SettingsUiState.Content } } ================================================ FILE: gradle/libs.versions.toml ================================================ [versions] kotlin = "2.3.20" # KSP depends on specific Kotlin version, so it must be upgraded together with Kotlin (disabled in Renovate) # https://repo.maven.apache.org/maven2/com/google/devtools/ksp/symbol-processing-gradle-plugin/ kotlin-symbol-processing = "2.3.6" # Java and JVM Versions are also stored in buildSrc/src/main/kotlin/config/JavaBuildConfig.kt java = "17" # Android SDK versions are marked as unused, however these are used in the `build.gradle.kts` files compile-sdk = "36" min-sdk = "28" target-sdk = "35" navigation = "2.9.7" test-logger = "4.0.0" coroutines = "1.10.2" retrofit = "3.0.0" okhttp = "5.3.2" koin = "4.2.0" coil = "2.7.0" lifecycle = "2.10.0" room = "2.8.4" serialization-json = "1.10.0" kotlinx-serialization-converter = "1.0.0" timber = "5.0.1" compose = "1.10.5" compose-bom = "2025.12.01" material-compose = "1.4.0" material = "1.13.0" material-icons = "1.7.8" lottie = "6.7.1" core-ktx = "1.18.0" core-splashscreen = "1.2.0" spotless = "8.1.0" detekt = "1.23.8" android-gradle-plugin = "8.13.2" easylauncher = "6.4.1" about-libraries = "13.2.1" # ktlint-rules ktlint-ruleset-standard = "1.7.1" nlopez-compose-rules = "0.4.27" twitter-compose-rules = "0.0.26" # Test junit-jupiter = "6.0.3" android-junit5 = "1.14.0.0" kluent = "1.73" test-runner = "1.7.0" mockk = "1.14.9" espresso = "3.7.0" core-testing = "2.2.0" konsist = "0.17.3" [plugins] # Search Gradle Plugins https://plugins.gradle.org/ android-application = { id = "com.android.application", version.ref = "android-gradle-plugin" } android-library = { id = "com.android.library", version.ref = "android-gradle-plugin" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } kotlin-symbol-processing = { id = "com.google.devtools.ksp", version.ref = "kotlin-symbol-processing" } detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } spotless = { id = "com.diffplug.spotless", version.ref = "spotless" } junit5-android = { id = "de.mannodermaus.android-junit5", version.ref = "android-junit5" } test-logger = { id = "com.adarshr.test-logger", version.ref = "test-logger" } compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } [libraries] # Gradle plugins for buildLogic module android-gradlePlugin = { module = "com.android.tools.build:gradle", version.ref = "android-gradle-plugin" } kotlin-gradlePlugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } ksp-gradlePlugin = { module = "com.google.devtools.ksp:symbol-processing-gradle-plugin", version.ref = "kotlin-symbol-processing" } spotless-gradlePlugin = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "spotless" } detekt-gradlePlugin = { module = "io.gitlab.arturbosch.detekt:detekt-gradle-plugin", version.ref = "detekt" } test-logger-gradlePlugin = { module = "com.adarshr:gradle-test-logger-plugin", version.ref = "test-logger" } compose-gradlePlugin = { module = "org.jetbrains.kotlin:compose-compiler-gradle-plugin", version.ref = "kotlin" } junit5-gradlePlugin = { module = "de.mannodermaus.gradle.plugins:android-junit5", version.ref = "android-junit5" } easy-launcher-gradlePlugin = { module = "com.starter.easylauncher:com.starter.easylauncher.gradle.plugin", version.ref = "easylauncher" } about-libraries-gradlePlugin = { module = "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin", version.ref = "about-libraries" } # Dependencies aboutlibraries-compose = { module = "com.mikepenz:aboutlibraries-compose-m3", version.ref = "about-libraries" } kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" } coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutines" } retrofit-core = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" } kotlinx-serialization-converter = { module = "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter", version.ref = "kotlinx-serialization-converter" } serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialization-json" } # Retrofit will use okhttp 4 (it has binary capability with okhttp 3) # See: https://square.github.io/okhttp/upgrading_to_okhttp_4/ okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" } okhttp-interceptor = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "okhttp" } koin-bom = { module = "io.insert-koin:koin-bom", version.ref = "koin" } koin = { module = "io.insert-koin:koin-android" } koin-compose = { module = "io.insert-koin:koin-androidx-compose" } koin-navigation = { module = "io.insert-koin:koin-androidx-navigation" } timber = { module = "com.jakewharton.timber:timber", version.ref = "timber" } compose-material = { module = "androidx.compose.material3:material3", version.ref = "material-compose" } material-material = { module = "com.google.android.material:material", version.ref = "material" } material-icons = { module = "androidx.compose.material:material-icons-extended", version.ref = "material-icons" } lottie-compose = { module = "com.airbnb.android:lottie-compose", version.ref = "lottie" } coil-compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" } core-ktx = { module = "androidx.core:core-ktx", version.ref = "core-ktx" } core-splashscreen = { module = "androidx.core:core-splashscreen", version.ref = "core-splashscreen" } viewmodel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "lifecycle" } compose-ui = { module = "androidx.compose.ui:ui", version.ref = "compose" } tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview", version.ref = "compose" } room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" } room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" } room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" } konsist = { module = "com.lemonappdev:konsist", version.ref = "konsist" } # Ktlint Rules ktlint-ruleset-standard = { module = "com.pinterest.ktlint:ktlint-ruleset-standard", version.ref = "ktlint-ruleset-standard" } nlopez-compose-rules = { module = "io.nlopez.compose.rules:ktlint", version.ref = "nlopez-compose-rules" } twitter-compose-rules = { module = "com.twitter.compose.rules:ktlint", version.ref = "twitter-compose-rules" } # Test dependencies test-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutines" } kluent = { module = "org.amshove.kluent:kluent-android", version.ref = "kluent" } test-runner = { module = "androidx.test:runner", version.ref = "test-runner" } espresso = { module = "androidx.test.espresso:espresso-core", version.ref = "espresso" } mockk = { module = "io.mockk:mockk", version.ref = "mockk" } core-testing = { module = "androidx.arch.core:core-testing", version.ref = "core-testing" } junit-jupiter-api = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "junit-jupiter" } junit-jupiter-engine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit-jupiter" } junit-platform-launcher = { module = "org.junit.platform:junit-platform-launcher", version = "1.14.3" } navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigation" } compose-bom = { module = "androidx.compose:compose-bom", version.ref = "compose-bom" } compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling", version.ref = "compose" } compose-ui-test-manifest = { module = "androidx.compose.ui:ui-test-manifest", version.ref = "compose" } [bundles] retrofit = [ "retrofit-core", "kotlinx-serialization-converter", "serialization-json", "okhttp", "okhttp-interceptor" ] koin = ["koin", "koin-compose", "koin-navigation"] compose = [ "navigation-compose", "compose-ui", "tooling-preview", "lottie-compose", "coil-compose" ] room = [ "room-ktx", "room-runtime" ] test = [ "test-coroutines", "kluent", "test-runner", "espresso", "mockk", "core-testing", "junit-jupiter-api", "junit-jupiter-engine", "junit-platform-launcher", ] ================================================ FILE: gradle/wrapper/gradle-wrapper.properties ================================================ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists ================================================ FILE: gradle.properties ================================================ # Project-wide Gradle settings. # IDE (e.g. Android Studio) users: # Gradle settings configured through the IDE *will override* # any settings specified in this file. # For more details on how to configure your build environment visit # http://www.gradle.org/docs/current/userguide/build_environment.html # -------Gradle-------- # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. org.gradle.jvmargs=-Xmx4g -Dfile.encoding=UTF-8 org.gradle.daemon=true org.gradle.parallel=true org.gradle.caching=true org.gradle.configuration-cache=true # -------Build parameters-------- # Values may be overridden in CI using gradlew "-Pname=value" param apiBaseUrl="http://ws.audioscrobbler.com/2.0/" # Typically we shouldn't store token in public repository, however this is just a sample project, so # we can favour convenience (app can be compiled and launched after checkout) over security (each person who # checkouts the project must generate own api key and change app configuration before running it). # In real-live setup this key could be provided\overriden by CI. apiToken="70696db59158cb100370ad30a7a705c1" # -------Kotlin-------- # Kotlin code style for this project: "official" or "obsolete": kotlin.code.style=official kapt.use.worker.api=true # Enable Compile Avoidance, which skips annotation processing if only method bodies are changed in dependencies # To turn on Compile Avoidance we need to turn off AP discovery in compile path. kapt.include.compile.classpath=false # -------Android------- android.useAndroidX=true android.enableJetifier=true android.nonTransitiveRClass=true ================================================ FILE: gradlew ================================================ #!/bin/sh # # Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # SPDX-License-Identifier: Apache-2.0 # ############################################################################## # # Gradle start up script for POSIX generated by Gradle. # # Important for running: # # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is # noncompliant, but you have some other compliant shell such as ksh or # bash, then to run this script, type that shell name before the whole # command line, like: # # ksh Gradle # # Busybox and similar reduced shells will NOT work, because this script # requires all of these POSIX shell features: # * functions; # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», # «${var#prefix}», «${var%suffix}», and «$( cmd )»; # * compound commands having a testable exit status, especially «case»; # * various built-in commands including «command», «set», and «ulimit». # # Important for patching: # # (2) This script targets any POSIX shell, so it avoids extensions provided # by Bash, Ksh, etc; in particular arrays are avoided. # # The "traditional" practice of packing multiple parameters into a # space-separated string is a well documented source of bugs and security # problems, so this is (mostly) avoided, by progressively accumulating # options in "$@", and eventually passing that to Java. # # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; # see the in-line comments for details. # # There are tweaks for specific operating systems such as AIX, CygWin, # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. # ############################################################################## # Attempt to set APP_HOME # Resolve links: $0 may be a link app_path=$0 # Need this for daisy-chained symlinks. while APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path [ -h "$app_path" ] do ls=$( ls -ld "$app_path" ) link=${ls#*' -> '} case $link in #( /*) app_path=$link ;; #( *) app_path=$APP_HOME$link ;; esac done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s ' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum warn () { echo "$*" } >&2 die () { echo echo "$*" echo exit 1 } >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false case "$( uname )" in #( CYGWIN* ) cygwin=true ;; #( Darwin* ) darwin=true ;; #( MSYS* | MINGW* ) msys=true ;; #( NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD=$JAVA_HOME/jre/sh/java else JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD=java if ! command -v java >/dev/null 2>&1 then die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac fi # Collect all arguments for the java command, stacking in reverse order: # * args from the command line # * the main class name # * -classpath # * -D...appname settings # * --module-path (only if needed) # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) # Now convert the arguments - kludge to limit ourselves to /bin/sh for arg do if case $arg in #( -*) false ;; # don't mess with options #( /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath [ -e "$t" ] ;; #( *) false ;; esac then arg=$( cygpath --path --ignore --mixed "$arg" ) fi # Roll the args list around exactly as many times as the number of # args, so each arg winds up back in the position where it started, but # possibly modified. # # NB: a `for` loop captures its iteration list before it begins, so # changing the positional parameters here affects neither the number of # iterations, nor the values presented in `arg`. shift # remove old arg set -- "$@" "$arg" # push replacement arg done fi # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Collect all arguments for the java command: # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # and any embedded shellness will be escaped. # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ -classpath "$CLASSPATH" \ org.gradle.wrapper.GradleWrapperMain \ "$@" # Stop when "xargs" is not available. if ! command -v xargs >/dev/null 2>&1 then die "xargs is not available" fi # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. # # In Bash we could simply go: # # readarray ARGS < <( xargs -n1 <<<"$var" ) && # set -- "${ARGS[@]}" "$@" # # but POSIX shell has neither arrays nor command substitution, so instead we # post-process each arg (as a line of input to sed) to backslash-escape any # character that might be a shell metacharacter, then use eval to reverse # that process (while maintaining the separation between arguments), and wrap # the whole thing up as a single "set" statement. # # This will of course break if any of these variables contains a newline or # an unmatched quote. # eval "set -- $( printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | xargs -n1 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | tr '\n' ' ' )" '"$@"' exec "$JAVACMD" "$@" ================================================ FILE: gradlew.bat ================================================ @rem @rem Copyright 2015 the original author or authors. @rem @rem Licensed under the Apache License, Version 2.0 (the "License"); @rem you may not use this file except in compliance with the License. @rem You may obtain a copy of the License at @rem @rem https://www.apache.org/licenses/LICENSE-2.0 @rem @rem Unless required by applicable law or agreed to in writing, software @rem distributed under the License is distributed on an "AS IS" BASIS, @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem @rem SPDX-License-Identifier: Apache-2.0 @rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%"=="" set DIRNAME=. @rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Resolve any "." and ".." in APP_HOME to make it shorter. for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute echo. 1>&2 echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 echo. 1>&2 echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo location of your Java installation. 1>&2 goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute echo. 1>&2 echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 echo. 1>&2 echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo location of your Java installation. 1>&2 goto fail :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! set EXIT_CODE=%ERRORLEVEL% if %EXIT_CODE% equ 0 set EXIT_CODE=1 if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ================================================ FILE: konsist-test/build.gradle.kts ================================================ plugins { id("com.igorwojda.showcase.convention.test.library") } android { namespace = "com.igorwojda.showcase.konsist.test" } dependencies { implementation(projects.feature.base) testImplementation(projects.library.testUtils) testImplementation(libs.bundles.test) testImplementation(libs.konsist) testImplementation(libs.viewmodel.ktx) } ================================================ FILE: konsist-test/src/test/kotlin/com/igorwojda/showcase/konsisttest/AndroidKonsistTest.kt ================================================ package com.igorwojda.showcase.konsisttest import androidx.lifecycle.ViewModel import com.lemonappdev.konsist.api.Konsist import com.lemonappdev.konsist.api.ext.list.withParentClassOf import com.lemonappdev.konsist.api.verify.assertTrue import org.junit.jupiter.api.Test // Check Android specific coding rules. class AndroidKonsistTest { @Test fun `classes extending 'ViewModel' should have 'ViewModel' suffix`() { Konsist .scopeFromProject() .classes() .withParentClassOf(ViewModel::class) .assertTrue { it.name.endsWith("ViewModel") } } } ================================================ FILE: konsist-test/src/test/kotlin/com/igorwojda/showcase/konsisttest/CleanArchitectureKonsistTest.kt ================================================ package com.igorwojda.showcase.konsisttest import com.lemonappdev.konsist.api.Konsist import com.lemonappdev.konsist.api.architecture.KoArchitectureCreator.assertArchitecture import com.lemonappdev.konsist.api.architecture.Layer import org.junit.jupiter.api.Test // Check architecture coding rules. class CleanArchitectureKonsistTest { @Test fun `clean architecture layers have correct dependencies`() { Konsist .scopeFromProduction() .assertArchitecture { // Define layers val packagePrefix = "com.igorwojda.showcase" val domain = Layer("Domain", "$packagePrefix..domain..") val presentation = Layer("Presentation", "$packagePrefix..presentation..") val data = Layer("Data", "$packagePrefix..data..") // Define architecture assertions domain.dependsOnNothing() presentation.dependsOn(domain) data.dependsOn(domain) } } } ================================================ FILE: konsist-test/src/test/kotlin/com/igorwojda/showcase/konsisttest/GeneralKonsistTest.kt ================================================ package com.igorwojda.showcase.konsisttest import com.lemonappdev.konsist.api.Konsist import com.lemonappdev.konsist.api.ext.list.properties import com.lemonappdev.konsist.api.verify.assertFalse import com.lemonappdev.konsist.api.verify.assertTrue import org.junit.jupiter.api.Test // Check General coding rules. class GeneralKonsistTest { @Test fun `package name must match file path`() { Konsist .scopeFromProject() .packages .assertTrue { it.hasMatchingPath } } @Test fun `no field should have 'm' prefix`() { Konsist .scopeFromProject() .classes() .properties() .assertFalse { val secondCharacterIsUppercase = it.name.getOrNull(1)?.isUpperCase() ?: false it.name.startsWith('m') && secondCharacterIsUppercase } } @Test fun `no class should use Android util logging`() { Konsist .scopeFromProject() .files .assertFalse { it.hasImportWithName("android.util.Log") } } @Test fun `no empty files allowed`() { Konsist .scopeFromProject() .files .assertFalse { it.text.isEmpty() } } } ================================================ FILE: konsist-test/src/test/kotlin/com/igorwojda/showcase/konsisttest/ModuleKonsistTest.kt ================================================ package com.igorwojda.showcase.konsisttest import com.lemonappdev.konsist.api.Konsist import com.lemonappdev.konsist.api.verify.assertTrue import org.junit.jupiter.api.Test // Check architecture coding rules. class ModuleKonsistTest { @Test fun `every file in module reside in module specific package`() { Konsist .scopeFromProject() .files .assertTrue { val modulePackageName = it.moduleName .lowercase() .replace("/", ".") .replace("-", "") val fullyQualifiedPackageName = "com.igorwojda.showcase.$modulePackageName" it.packagee?.name?.startsWith(fullyQualifiedPackageName) } } } ================================================ FILE: konsist-test/src/test/kotlin/com/igorwojda/showcase/konsisttest/TestKonsistTest.kt ================================================ package com.igorwojda.showcase.konsisttest import com.lemonappdev.konsist.api.Konsist import com.lemonappdev.konsist.api.ext.list.functions import com.lemonappdev.konsist.api.verify.assertFalse import org.junit.jupiter.api.Test // Check test coding rules. class TestKonsistTest { @Test fun `don't use JUnit4 Test annotation`() { Konsist .scopeFromProject() .classes() .functions() .assertFalse { it.hasAnnotationsWithAllNames("org.junit.Test") } // should be only org.junit.jupiter.api.Test } } ================================================ FILE: konsist-test/src/test/kotlin/com/igorwojda/showcase/konsisttest/UseCaseKonsistTest.kt ================================================ package com.igorwojda.showcase.konsisttest import com.lemonappdev.konsist.api.Konsist import com.lemonappdev.konsist.api.ext.list.withNameEndingWith import com.lemonappdev.konsist.api.verify.assertTrue import java.util.Locale import org.junit.jupiter.api.Test // Check test coding rules. class UseCaseKonsistTest { @Test fun `every use case class has test`() { Konsist .scopeFromProduction() .classes() .withNameEndingWith("UseCase") .assertTrue { it.hasTestClasses(testPropertyName = "sut") } } @Test fun `every use case constructor has alphabetically ordered parameters`() { Konsist .scopeFromProject() .classes() .withNameEndingWith("UseCase") .flatMap { it.constructors } .assertTrue { val names = it.parameters.map { parameter -> parameter.name } val sortedNames = names.sorted() names == sortedNames } } @Test fun `classes with 'UseCase' suffix should reside in 'domain' and 'usecase' packages`() { Konsist .scopeFromProject() .classes() .withNameEndingWith("UseCase") .assertTrue { it.resideInPackage("..domain..usecase..") } } @Test fun `classes with 'UseCase' suffix should have single public operator method named 'invoke'`() { Konsist .scopeFromProject() .classes() .withNameEndingWith("UseCase") .assertTrue { val hasSingleInvokeOperatorMethod = it.hasFunction { function -> function.name == "invoke" && function.hasPublicOrDefaultModifier && function.hasOperatorModifier } hasSingleInvokeOperatorMethod && it.numPublicOrDefaultDeclarations() == 1 } } @Test fun `every use case constructor parameter has name derived from parameter type`() { Konsist .scopeFromProject() .classes() .withNameEndingWith("UseCase") .flatMap { it.constructors } .flatMap { it.parameters } .assertTrue { val nameTitleCase = it.name.replaceFirstChar { char -> char.titlecase(Locale.getDefault()) } nameTitleCase == it.type.sourceType } } } ================================================ FILE: konsist-test/src/test/kotlin/com/igorwojda/showcase/konsisttest/ViewModelKonsistTest.kt ================================================ package com.igorwojda.showcase.konsisttest import com.igorwojda.showcase.feature.base.presentation.viewmodel.BaseViewModel import com.lemonappdev.konsist.api.KoModifier import com.lemonappdev.konsist.api.Konsist import com.lemonappdev.konsist.api.ext.list.modifierprovider.withoutAllModifiers import com.lemonappdev.konsist.api.ext.list.withDeclarations import com.lemonappdev.konsist.api.ext.list.withNameEndingWith import com.lemonappdev.konsist.api.ext.list.withParentClassOf import com.lemonappdev.konsist.api.verify.assertTrue import java.util.Locale import org.junit.jupiter.api.Test // Check test coding rules. class ViewModelKonsistTest { @Test fun `every view model has test`() { Konsist .scopeFromProduction() .classes() .withParentClassOf(BaseViewModel::class) .withDeclarations() // Filter out empty view models .assertTrue { it.hasTestClasses(testPropertyName = "sut") } } @Test fun `every view model constructor parameter has name derived from parameter type`() { Konsist .scopeFromProject() .classes() .withNameEndingWith("ViewModel") .withoutAllModifiers(KoModifier.ABSTRACT) .flatMap { it.constructors } .flatMap { it.parameters } .assertTrue { val nameTitleCase = it.name.replaceFirstChar { char -> char.titlecase(Locale.getDefault()) } nameTitleCase == it.type.sourceType } } } ================================================ FILE: library/test-utils/build.gradle.kts ================================================ plugins { id("com.igorwojda.showcase.convention.library") } android { namespace = "com.igorwojda.showcase.library.testutils" } dependencies { // implementation configuration is used here (instead of testImplementation) because this module is added as // testImplementation dependency inside other modules. Using implementation allows to write tests for test utilities. implementation(libs.kotlin.reflect) implementation(libs.bundles.test) implementation(libs.bundles.compose) runtimeOnly(libs.junit.jupiter.engine) } ================================================ FILE: library/test-utils/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: library/test-utils/src/main/AndroidManifest.xml ================================================ ================================================ FILE: library/test-utils/src/main/kotlin/com/igorwojda/showcase/library/testutils/CoroutinesTestDispatcherExtension.kt ================================================ package com.igorwojda.showcase.library.testutils import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.resetMain import kotlinx.coroutines.test.setMain import org.junit.jupiter.api.extension.AfterEachCallback import org.junit.jupiter.api.extension.BeforeEachCallback import org.junit.jupiter.api.extension.ExtensionContext /** * A JUnit Test Extension that swaps the coroutine dispatcher for the TestDispatcher. * * Add this JUnit 5 extension to your test class using * @JvmField * @RegisterExtension * val coroutinesTestExtension = CoroutinesTestExtension() */ @ExperimentalCoroutinesApi class CoroutinesTestDispatcherExtension : BeforeEachCallback, AfterEachCallback { override fun beforeEach(context: ExtensionContext) { Dispatchers.setMain(StandardTestDispatcher()) } override fun afterEach(context: ExtensionContext) { Dispatchers.resetMain() } } ================================================ FILE: library/test-utils/src/main/kotlin/com/igorwojda/showcase/library/testutils/InstantTaskExecutorExtension.kt ================================================ package com.igorwojda.showcase.library.testutils import android.annotation.SuppressLint import androidx.arch.core.executor.ArchTaskExecutor import androidx.arch.core.executor.TaskExecutor import org.junit.jupiter.api.extension.AfterEachCallback import org.junit.jupiter.api.extension.BeforeEachCallback import org.junit.jupiter.api.extension.ExtensionContext /** * A JUnit Test Extension that swaps the background executor used by the Architecture Components with a * different one which executes each task synchronously. * * Extension can be used for your host side tests that use Architecture Components. */ @SuppressLint("RestrictedApi") class InstantTaskExecutorExtension : BeforeEachCallback, AfterEachCallback { override fun beforeEach(context: ExtensionContext) { ArchTaskExecutor.getInstance().setDelegate( object : TaskExecutor() { override fun executeOnDiskIO(runnable: Runnable) { runnable.run() } override fun postToMainThread(runnable: Runnable) { runnable.run() } override fun isMainThread() = true }, ) } override fun afterEach(context: ExtensionContext) { ArchTaskExecutor.getInstance().setDelegate(null) } } ================================================ FILE: renovate.json ================================================ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ "config:base" ], "lockFileMaintenance": { "enabled": true, "automerge": true, "automergeType": "pr", "platformAutomerge": true }, "packageRules": [ { "matchUpdateTypes": [ "minor", "patch" ], "matchCurrentVersion": "!/^0/", "automerge": true, "automergeType": "pr", "platformAutomerge": true }, { "matchPackagePatterns": [ "androidx.compose.ui:ui", "androidx.compose.ui:ui-tooling-preview" ], "groupName": "androidx compose ui updates" }, { "matchPackageNames": [ "org.jetbrains.kotlin.*", "com.google.devtools.ksp", "com.android.library", "com.android.application" ], "enabled": false } ] } ================================================ FILE: settings.gradle.kts ================================================ rootProject.name = "android-showcase" include( ":app", ":feature:album", ":feature:settings", ":feature:favourite", ":feature:base", ":library:test-utils", ":konsist-test", ) pluginManagement { includeBuild("build-logic") repositories { gradlePluginPortal() google() mavenCentral() } } @Suppress("UnstableApiUsage") dependencyResolutionManagement { repositories { google() // Added for testing local Konsist artifacts mavenLocal() mavenCentral() } } // Generate type safe accessors when referring to other projects eg. // Before: implementation(project(":feature_album")) // After: implementation(projects.featureAlbum) enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")