├── wear-os
├── .gitignore
├── src
│ └── main
│ │ ├── res
│ │ ├── values-round
│ │ │ └── strings.xml
│ │ ├── mipmap-hdpi
│ │ │ └── ic_launcher.webp
│ │ ├── mipmap-mdpi
│ │ │ └── ic_launcher.webp
│ │ ├── mipmap-xhdpi
│ │ │ └── ic_launcher.webp
│ │ ├── mipmap-xxhdpi
│ │ │ └── ic_launcher.webp
│ │ ├── mipmap-xxxhdpi
│ │ │ └── ic_launcher.webp
│ │ └── values
│ │ │ └── strings.xml
│ │ ├── java
│ │ └── com
│ │ │ └── breens
│ │ │ └── wear_os
│ │ │ └── presentation
│ │ │ ├── theme
│ │ │ ├── Theme.kt
│ │ │ ├── Color.kt
│ │ │ └── Type.kt
│ │ │ └── MainActivity.kt
│ │ └── AndroidManifest.xml
├── proguard-rules.pro
└── build.gradle.kts
├── shared
├── src
│ ├── androidMain
│ │ ├── AndroidManifest.xml
│ │ └── kotlin
│ │ │ ├── main.android.kt
│ │ │ ├── di
│ │ │ └── PlatformModule.kt
│ │ │ └── utils
│ │ │ └── ImageLoader.kt
│ ├── commonMain
│ │ ├── kotlin
│ │ │ ├── di
│ │ │ │ ├── AppModule.kt
│ │ │ │ ├── PlatformModule.kt
│ │ │ │ ├── Koin.kt
│ │ │ │ └── CommonModule.kt
│ │ │ ├── data
│ │ │ │ └── remote
│ │ │ │ │ ├── model
│ │ │ │ │ ├── Links.kt
│ │ │ │ │ ├── Genres.kt
│ │ │ │ │ ├── Quotes.kt
│ │ │ │ │ ├── Reviews.kt
│ │ │ │ │ ├── Staff.kt
│ │ │ │ │ ├── Castings.kt
│ │ │ │ │ ├── Episodes.kt
│ │ │ │ │ ├── Mappings.kt
│ │ │ │ │ ├── AnimeStaff.kt
│ │ │ │ │ ├── Categories.kt
│ │ │ │ │ ├── Characters.kt
│ │ │ │ │ ├── Installments.kt
│ │ │ │ │ ├── Meta.kt
│ │ │ │ │ ├── Productions.kt
│ │ │ │ │ ├── MetaX.kt
│ │ │ │ │ ├── StreamingLinks.kt
│ │ │ │ │ ├── AnimeCharacters.kt
│ │ │ │ │ ├── AnimeProductions.kt
│ │ │ │ │ ├── AnimeResponse.kt
│ │ │ │ │ ├── MediaRelationships.kt
│ │ │ │ │ ├── Tiny.kt
│ │ │ │ │ ├── Large.kt
│ │ │ │ │ ├── Medium.kt
│ │ │ │ │ ├── Small.kt
│ │ │ │ │ ├── LinksX.kt
│ │ │ │ │ ├── Dimensions.kt
│ │ │ │ │ ├── DimensionsX.kt
│ │ │ │ │ ├── CoverImage.kt
│ │ │ │ │ ├── Data.kt
│ │ │ │ │ ├── Titles.kt
│ │ │ │ │ ├── PosterImage.kt
│ │ │ │ │ ├── RatingFrequencies.kt
│ │ │ │ │ ├── Relationships.kt
│ │ │ │ │ └── Attributes.kt
│ │ │ │ │ ├── trendinganime
│ │ │ │ │ ├── AnimeRepository.kt
│ │ │ │ │ └── AnimeRepositoryImpl.kt
│ │ │ │ │ └── KitsuApi.kt
│ │ │ ├── utils
│ │ │ │ ├── Constants.kt
│ │ │ │ ├── NetworkResult.kt
│ │ │ │ ├── ImageLoader.kt
│ │ │ │ └── UtilFunctions.kt
│ │ │ ├── App.kt
│ │ │ ├── presentation
│ │ │ │ ├── components
│ │ │ │ │ ├── DetailScreenLoadingComponent.kt
│ │ │ │ │ ├── HomeScreenLoadingComponent.kt
│ │ │ │ │ └── AnimeComponent.kt
│ │ │ │ ├── utlis
│ │ │ │ │ └── Shimmer.kt
│ │ │ │ ├── home
│ │ │ │ │ ├── HomeScreenViewModel.kt
│ │ │ │ │ └── HomeScreen.kt
│ │ │ │ └── details
│ │ │ │ │ └── AnimeDetailsScreen.kt
│ │ │ └── domain
│ │ │ │ ├── dto
│ │ │ │ └── AnimeDto.kt
│ │ │ │ └── mappers
│ │ │ │ └── Mappers.kt
│ │ └── resources
│ │ │ └── compose-multiplatform.xml
│ ├── main
│ │ └── res
│ │ │ └── drawable
│ │ │ └── placeholder.png
│ └── iosMain
│ │ └── kotlin
│ │ ├── Helper.kt
│ │ ├── main.ios.kt
│ │ ├── di
│ │ └── PlatformModule.kt
│ │ └── utils
│ │ ├── ImageUtils.kt
│ │ └── ImageLoader.kt
└── build.gradle.kts
├── images
├── ios_home.png
├── ios_details.png
├── android_home.png
└── android_details.png
├── readme_images
├── banner.png
├── run_on_android.png
├── target_device.png
├── edit_run_config.png
├── hello_world_ios.png
├── open_project_view.png
├── text_field_added.png
└── android_app_running.png
├── iosApp
├── Configuration
│ └── Config.xcconfig
├── Podfile
├── iosApp
│ ├── Assets.xcassets
│ │ ├── Contents.json
│ │ ├── AppIcon.appiconset
│ │ │ ├── app-icon-1024.png
│ │ │ └── Contents.json
│ │ └── AccentColor.colorset
│ │ │ └── Contents.json
│ ├── Preview Content
│ │ └── Preview Assets.xcassets
│ │ │ └── Contents.json
│ ├── iOSApp.swift
│ ├── ContentView.swift
│ └── Info.plist
└── iosApp.xcodeproj
│ └── project.pbxproj
├── androidApp
├── src
│ └── androidMain
│ │ ├── res
│ │ ├── values
│ │ │ ├── strings.xml
│ │ │ └── ic_launcher_background.xml
│ │ ├── mipmap-hdpi
│ │ │ ├── ic_launcher.webp
│ │ │ ├── ic_launcher_round.webp
│ │ │ └── ic_launcher_foreground.webp
│ │ ├── mipmap-mdpi
│ │ │ ├── ic_launcher.webp
│ │ │ ├── ic_launcher_round.webp
│ │ │ └── ic_launcher_foreground.webp
│ │ ├── mipmap-xhdpi
│ │ │ ├── ic_launcher.webp
│ │ │ ├── ic_launcher_round.webp
│ │ │ └── ic_launcher_foreground.webp
│ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_launcher.webp
│ │ │ ├── ic_launcher_round.webp
│ │ │ └── ic_launcher_foreground.webp
│ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_launcher.webp
│ │ │ ├── ic_launcher_round.webp
│ │ │ └── ic_launcher_foreground.webp
│ │ ├── mipmap-anydpi-v26
│ │ │ ├── ic_launcher.xml
│ │ │ └── ic_launcher_round.xml
│ │ ├── drawable-v24
│ │ │ └── ic_launcher_foreground.xml
│ │ └── drawable
│ │ │ └── ic_launcher_background.xml
│ │ ├── ic_launcher-playstore.png
│ │ ├── kotlin
│ │ └── com
│ │ │ └── breensmbaka
│ │ │ ├── MainActivity.kt
│ │ │ └── AnimeFollowApp.kt
│ │ └── AndroidManifest.xml
└── build.gradle.kts
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── cleanup.sh
├── .gitignore
├── gradle.properties
├── settings.gradle.kts
├── README.md
├── gradlew.bat
├── gradlew
└── LICENSE.txt
/wear-os/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/shared/src/androidMain/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/images/ios_home.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Breens-Mbaka/AnimeFollowKMM/HEAD/images/ios_home.png
--------------------------------------------------------------------------------
/images/ios_details.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Breens-Mbaka/AnimeFollowKMM/HEAD/images/ios_details.png
--------------------------------------------------------------------------------
/images/android_home.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Breens-Mbaka/AnimeFollowKMM/HEAD/images/android_home.png
--------------------------------------------------------------------------------
/readme_images/banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Breens-Mbaka/AnimeFollowKMM/HEAD/readme_images/banner.png
--------------------------------------------------------------------------------
/images/android_details.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Breens-Mbaka/AnimeFollowKMM/HEAD/images/android_details.png
--------------------------------------------------------------------------------
/iosApp/Configuration/Config.xcconfig:
--------------------------------------------------------------------------------
1 | TEAM_ID=
2 | BUNDLE_ID=com.appplication.MyApplication
3 | APP_NAME=MyApplication
4 |
--------------------------------------------------------------------------------
/readme_images/run_on_android.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Breens-Mbaka/AnimeFollowKMM/HEAD/readme_images/run_on_android.png
--------------------------------------------------------------------------------
/readme_images/target_device.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Breens-Mbaka/AnimeFollowKMM/HEAD/readme_images/target_device.png
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/di/AppModule.kt:
--------------------------------------------------------------------------------
1 | package di
2 |
3 | fun appModule() = listOf(commonModule(), platformModule())
4 |
--------------------------------------------------------------------------------
/androidApp/src/androidMain/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | AnimeFollow
3 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Breens-Mbaka/AnimeFollowKMM/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/iosApp/Podfile:
--------------------------------------------------------------------------------
1 | target 'iosApp' do
2 | use_frameworks!
3 | platform :ios, '14.1'
4 | pod 'shared', :path => '../shared'
5 | end
--------------------------------------------------------------------------------
/iosApp/iosApp/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/readme_images/edit_run_config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Breens-Mbaka/AnimeFollowKMM/HEAD/readme_images/edit_run_config.png
--------------------------------------------------------------------------------
/readme_images/hello_world_ios.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Breens-Mbaka/AnimeFollowKMM/HEAD/readme_images/hello_world_ios.png
--------------------------------------------------------------------------------
/readme_images/open_project_view.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Breens-Mbaka/AnimeFollowKMM/HEAD/readme_images/open_project_view.png
--------------------------------------------------------------------------------
/readme_images/text_field_added.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Breens-Mbaka/AnimeFollowKMM/HEAD/readme_images/text_field_added.png
--------------------------------------------------------------------------------
/readme_images/android_app_running.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Breens-Mbaka/AnimeFollowKMM/HEAD/readme_images/android_app_running.png
--------------------------------------------------------------------------------
/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/di/PlatformModule.kt:
--------------------------------------------------------------------------------
1 | package di
2 |
3 | import org.koin.core.module.Module
4 |
5 | expect fun platformModule(): Module
6 |
--------------------------------------------------------------------------------
/shared/src/main/res/drawable/placeholder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Breens-Mbaka/AnimeFollowKMM/HEAD/shared/src/main/res/drawable/placeholder.png
--------------------------------------------------------------------------------
/wear-os/src/main/res/values-round/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | From the Round world,\nHello, %1$s!
3 |
--------------------------------------------------------------------------------
/wear-os/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Breens-Mbaka/AnimeFollowKMM/HEAD/wear-os/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/wear-os/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Breens-Mbaka/AnimeFollowKMM/HEAD/wear-os/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/wear-os/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Breens-Mbaka/AnimeFollowKMM/HEAD/wear-os/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/androidApp/src/androidMain/ic_launcher-playstore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Breens-Mbaka/AnimeFollowKMM/HEAD/androidApp/src/androidMain/ic_launcher-playstore.png
--------------------------------------------------------------------------------
/wear-os/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Breens-Mbaka/AnimeFollowKMM/HEAD/wear-os/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/wear-os/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Breens-Mbaka/AnimeFollowKMM/HEAD/wear-os/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/shared/src/iosMain/kotlin/Helper.kt:
--------------------------------------------------------------------------------
1 | import org.koin.core.context.startKoin
2 |
3 | fun initKoin() {
4 | startKoin {
5 | modules(di.appModule())
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/androidApp/src/androidMain/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Breens-Mbaka/AnimeFollowKMM/HEAD/androidApp/src/androidMain/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/androidApp/src/androidMain/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Breens-Mbaka/AnimeFollowKMM/HEAD/androidApp/src/androidMain/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/androidApp/src/androidMain/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Breens-Mbaka/AnimeFollowKMM/HEAD/androidApp/src/androidMain/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/androidApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Breens-Mbaka/AnimeFollowKMM/HEAD/androidApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/androidApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Breens-Mbaka/AnimeFollowKMM/HEAD/androidApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/androidApp/src/androidMain/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Breens-Mbaka/AnimeFollowKMM/HEAD/androidApp/src/androidMain/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/androidApp/src/androidMain/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Breens-Mbaka/AnimeFollowKMM/HEAD/androidApp/src/androidMain/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/shared/src/androidMain/kotlin/main.android.kt:
--------------------------------------------------------------------------------
1 | import androidx.compose.runtime.Composable
2 |
3 | actual fun getPlatformName(): String = "Android"
4 |
5 | @Composable fun MainView() = App()
--------------------------------------------------------------------------------
/androidApp/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Breens-Mbaka/AnimeFollowKMM/HEAD/androidApp/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/androidApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Breens-Mbaka/AnimeFollowKMM/HEAD/androidApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/androidApp/src/androidMain/res/values/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #4285F4
4 |
--------------------------------------------------------------------------------
/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Breens-Mbaka/AnimeFollowKMM/HEAD/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png
--------------------------------------------------------------------------------
/androidApp/src/androidMain/res/mipmap-hdpi/ic_launcher_foreground.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Breens-Mbaka/AnimeFollowKMM/HEAD/androidApp/src/androidMain/res/mipmap-hdpi/ic_launcher_foreground.webp
--------------------------------------------------------------------------------
/androidApp/src/androidMain/res/mipmap-mdpi/ic_launcher_foreground.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Breens-Mbaka/AnimeFollowKMM/HEAD/androidApp/src/androidMain/res/mipmap-mdpi/ic_launcher_foreground.webp
--------------------------------------------------------------------------------
/androidApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Breens-Mbaka/AnimeFollowKMM/HEAD/androidApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/androidApp/src/androidMain/res/mipmap-xhdpi/ic_launcher_foreground.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Breens-Mbaka/AnimeFollowKMM/HEAD/androidApp/src/androidMain/res/mipmap-xhdpi/ic_launcher_foreground.webp
--------------------------------------------------------------------------------
/androidApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher_foreground.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Breens-Mbaka/AnimeFollowKMM/HEAD/androidApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher_foreground.webp
--------------------------------------------------------------------------------
/androidApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_foreground.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Breens-Mbaka/AnimeFollowKMM/HEAD/androidApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_foreground.webp
--------------------------------------------------------------------------------
/shared/src/iosMain/kotlin/main.ios.kt:
--------------------------------------------------------------------------------
1 | import androidx.compose.ui.window.ComposeUIViewController
2 |
3 | actual fun getPlatformName(): String = "iOS"
4 |
5 | fun MainViewController() = ComposeUIViewController { App() }
6 |
--------------------------------------------------------------------------------
/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/iosApp/iosApp/iOSApp.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 | import shared
3 |
4 | @main
5 | struct iOSApp: App {
6 | init() {
7 | HelperKt.doInitKoin()
8 | }
9 | var body: some Scene {
10 | WindowGroup {
11 | ContentView()
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/cleanup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | rm -rf .idea
3 | ./gradlew clean
4 | rm -rf .gradle
5 | rm -rf build
6 | rm -rf */build
7 | rm -rf iosApp/iosApp.xcworkspace
8 | rm -rf iosApp/Pods
9 | rm -rf iosApp/iosApp.xcodeproj/project.xcworkspace
10 | rm -rf iosApp/iosApp.xcodeproj/xcuserdata
11 |
--------------------------------------------------------------------------------
/shared/src/androidMain/kotlin/di/PlatformModule.kt:
--------------------------------------------------------------------------------
1 | package di
2 |
3 | import org.koin.core.module.Module
4 | import org.koin.dsl.module
5 | import io.ktor.client.engine.android.*
6 |
7 |
8 | actual fun platformModule(): Module = module {
9 | single { Android.create() }
10 | }
--------------------------------------------------------------------------------
/shared/src/iosMain/kotlin/di/PlatformModule.kt:
--------------------------------------------------------------------------------
1 | package di
2 |
3 | import io.ktor.client.engine.darwin.Darwin
4 | import org.koin.core.module.Module
5 | import org.koin.dsl.module
6 |
7 | actual fun platformModule(): Module = module {
8 | single { Darwin.create() }
9 | }
10 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/data/remote/model/Links.kt:
--------------------------------------------------------------------------------
1 | package data.remote.model
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class Links(
8 | @SerialName("self")
9 | val self: String?,
10 | )
11 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/data/remote/model/Genres.kt:
--------------------------------------------------------------------------------
1 | package data.remote.model
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class Genres(
8 | @SerialName("links")
9 | val links: LinksX?,
10 | )
11 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/data/remote/model/Quotes.kt:
--------------------------------------------------------------------------------
1 | package data.remote.model
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class Quotes(
8 | @SerialName("links")
9 | val links: LinksX?,
10 | )
11 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/data/remote/model/Reviews.kt:
--------------------------------------------------------------------------------
1 | package data.remote.model
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class Reviews(
8 | @SerialName("links")
9 | val links: LinksX?,
10 | )
11 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/data/remote/model/Staff.kt:
--------------------------------------------------------------------------------
1 | package data.remote.model
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class Staff(
8 | @SerialName("links")
9 | val links: LinksX?,
10 | )
11 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/data/remote/model/Castings.kt:
--------------------------------------------------------------------------------
1 | package data.remote.model
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class Castings(
8 | @SerialName("links")
9 | val links: LinksX?,
10 | )
11 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/data/remote/model/Episodes.kt:
--------------------------------------------------------------------------------
1 | package data.remote.model
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class Episodes(
8 | @SerialName("links")
9 | val links: LinksX?,
10 | )
11 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/data/remote/model/Mappings.kt:
--------------------------------------------------------------------------------
1 | package data.remote.model
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class Mappings(
8 | @SerialName("links")
9 | val links: LinksX?,
10 | )
11 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/data/remote/model/AnimeStaff.kt:
--------------------------------------------------------------------------------
1 | package data.remote.model
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class AnimeStaff(
8 | @SerialName("links")
9 | val links: LinksX?,
10 | )
11 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/data/remote/model/Categories.kt:
--------------------------------------------------------------------------------
1 | package data.remote.model
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class Categories(
8 | @SerialName("links")
9 | val links: LinksX?,
10 | )
11 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/data/remote/model/Characters.kt:
--------------------------------------------------------------------------------
1 | package data.remote.model
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class Characters(
8 | @SerialName("links")
9 | val links: LinksX?,
10 | )
11 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/data/remote/model/Installments.kt:
--------------------------------------------------------------------------------
1 | package data.remote.model
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class Installments(
8 | @SerialName("links")
9 | val links: LinksX?,
10 | )
11 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/data/remote/model/Meta.kt:
--------------------------------------------------------------------------------
1 | package data.remote.model
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class Meta(
8 | @SerialName("dimensions")
9 | val dimensions: Dimensions?,
10 | )
11 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/data/remote/model/Productions.kt:
--------------------------------------------------------------------------------
1 | package data.remote.model
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class Productions(
8 | @SerialName("links")
9 | val links: LinksX?,
10 | )
11 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/di/Koin.kt:
--------------------------------------------------------------------------------
1 | package di
2 |
3 | import org.koin.core.context.startKoin
4 | import org.koin.dsl.KoinAppDeclaration
5 |
6 | fun initKoin(appDeclaration: KoinAppDeclaration = {}) =
7 | startKoin {
8 | appDeclaration
9 | modules(appModule())
10 | }
11 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/data/remote/model/MetaX.kt:
--------------------------------------------------------------------------------
1 | package data.remote.model
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class MetaX(
8 | @SerialName("dimensions")
9 | val dimensions: DimensionsX?,
10 | )
11 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/data/remote/model/StreamingLinks.kt:
--------------------------------------------------------------------------------
1 | package data.remote.model
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class StreamingLinks(
8 | @SerialName("links")
9 | val links: LinksX?,
10 | )
11 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/data/remote/model/AnimeCharacters.kt:
--------------------------------------------------------------------------------
1 | package data.remote.model
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class AnimeCharacters(
8 | @SerialName("links")
9 | val links: LinksX?,
10 | )
11 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/data/remote/model/AnimeProductions.kt:
--------------------------------------------------------------------------------
1 | package data.remote.model
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class AnimeProductions(
8 | @SerialName("links")
9 | val links: LinksX?,
10 | )
11 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/data/remote/model/AnimeResponse.kt:
--------------------------------------------------------------------------------
1 | package data.remote.model
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class AnimeResponse(
8 | @SerialName("data")
9 | val `data`: List?,
10 | )
11 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/data/remote/model/MediaRelationships.kt:
--------------------------------------------------------------------------------
1 | package data.remote.model
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class MediaRelationships(
8 | @SerialName("links")
9 | val links: LinksX?,
10 | )
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea
5 | .DS_Store
6 | build/
7 | /captures
8 | .externalNativeBuild
9 | .cxx
10 | iosApp/Podfile.lock
11 | iosApp/Pods/*
12 | iosApp/iosApp.xcworkspace/*
13 | iosApp/iosApp.xcodeproj/*
14 | !iosApp/iosApp.xcodeproj/project.pbxproj
15 | shared/shared.podspec
16 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/utils/Constants.kt:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | object Constants {
4 | const val BASE_URL = "kitsu.io"
5 |
6 | const val TRENDING_ANIME = "/api/edge/trending/anime"
7 |
8 | const val UPCOMING_ANIME = "/api/edge/anime"
9 |
10 | const val HEADER_TYPE = "application/vnd.api+json"
11 | }
12 |
--------------------------------------------------------------------------------
/androidApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/androidApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "app-icon-1024.png",
5 | "idiom" : "universal",
6 | "platform" : "ios",
7 | "size" : "1024x1024"
8 | }
9 | ],
10 | "info" : {
11 | "author" : "xcode",
12 | "version" : 1
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/data/remote/model/Tiny.kt:
--------------------------------------------------------------------------------
1 | package data.remote.model
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class Tiny(
8 | @SerialName("height")
9 | val height: Int?,
10 | @SerialName("width")
11 | val width: Int?,
12 | )
13 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/data/remote/model/Large.kt:
--------------------------------------------------------------------------------
1 | package data.remote.model
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class Large(
8 | @SerialName("height")
9 | val height: Int?,
10 | @SerialName("width")
11 | val width: Int?,
12 | )
13 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/data/remote/model/Medium.kt:
--------------------------------------------------------------------------------
1 | package data.remote.model
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class Medium(
8 | @SerialName("height")
9 | val height: Int?,
10 | @SerialName("width")
11 | val width: Int?,
12 | )
13 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/data/remote/model/Small.kt:
--------------------------------------------------------------------------------
1 | package data.remote.model
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class Small(
8 | @SerialName("height")
9 | val height: Int?,
10 | @SerialName("width")
11 | val width: Int?,
12 | )
13 |
--------------------------------------------------------------------------------
/wear-os/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | wear-os
3 |
7 | From the Square world,\nHello, %1$s!
8 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/data/remote/model/LinksX.kt:
--------------------------------------------------------------------------------
1 | package data.remote.model
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class LinksX(
8 | @SerialName("related")
9 | val related: String?,
10 | @SerialName("self")
11 | val self: String?,
12 | )
13 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/App.kt:
--------------------------------------------------------------------------------
1 | import androidx.compose.material.MaterialTheme
2 | import androidx.compose.runtime.Composable
3 | import cafe.adriel.voyager.navigator.Navigator
4 | import presentation.home.HomeScreen
5 |
6 | @Composable
7 | fun App() {
8 | MaterialTheme {
9 | Navigator(HomeScreen())
10 | }
11 | }
12 |
13 | expect fun getPlatformName(): String
14 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/data/remote/model/Dimensions.kt:
--------------------------------------------------------------------------------
1 | package data.remote.model
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class Dimensions(
8 | @SerialName("large")
9 | val large: Large?,
10 | @SerialName("small")
11 | val small: Small?,
12 | @SerialName("tiny")
13 | val tiny: Tiny?,
14 | )
15 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/data/remote/trendinganime/AnimeRepository.kt:
--------------------------------------------------------------------------------
1 | package data.remote.trendinganime
2 |
3 | import domain.dto.DataDto
4 | import kotlinx.coroutines.flow.Flow
5 | import utils.NetworkResult
6 |
7 | interface AnimeRepository {
8 | suspend fun getTrendingAnime(): Flow>>
9 |
10 | suspend fun getUpcomingAnime(): Flow>>
11 | }
12 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/utils/NetworkResult.kt:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | sealed class NetworkResult(
4 | val data: T? = null,
5 | val errorCode: Int? = null,
6 | val errorMessage: String? = null
7 | ) {
8 | class Success(data: T) : NetworkResult(data)
9 | class Error(errorCode: Int, errorMessage: String?) :
10 | NetworkResult(errorCode = errorCode, errorMessage = errorMessage)
11 | }
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/data/remote/model/DimensionsX.kt:
--------------------------------------------------------------------------------
1 | package data.remote.model
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class DimensionsX(
8 | @SerialName("large")
9 | val large: Large?,
10 | @SerialName("medium")
11 | val medium: Medium?,
12 | @SerialName("small")
13 | val small: Small?,
14 | @SerialName("tiny")
15 | val tiny: Tiny?,
16 | )
17 |
--------------------------------------------------------------------------------
/androidApp/src/androidMain/kotlin/com/breensmbaka/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.breensmbaka
2 |
3 | import MainView
4 | import android.os.Bundle
5 | import androidx.activity.compose.setContent
6 | import androidx.appcompat.app.AppCompatActivity
7 |
8 | class MainActivity : AppCompatActivity() {
9 | override fun onCreate(savedInstanceState: Bundle?) {
10 | super.onCreate(savedInstanceState)
11 |
12 | setContent {
13 | MainView()
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/data/remote/model/CoverImage.kt:
--------------------------------------------------------------------------------
1 | package data.remote.model
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class CoverImage(
8 | @SerialName("large")
9 | val large: String?,
10 | @SerialName("meta")
11 | val meta: Meta?,
12 | @SerialName("original")
13 | val original: String?,
14 | @SerialName("small")
15 | val small: String?,
16 | @SerialName("tiny")
17 | val tiny: String?,
18 | )
19 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/data/remote/model/Data.kt:
--------------------------------------------------------------------------------
1 | package data.remote.model
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class Data(
8 | @SerialName("attributes")
9 | val attributes: Attributes?,
10 | @SerialName("id")
11 | val id: String?,
12 | @SerialName("links")
13 | val links: Links?,
14 | @SerialName("relationships")
15 | val relationships: Relationships?,
16 | @SerialName("type")
17 | val type: String?,
18 | )
19 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/data/remote/model/Titles.kt:
--------------------------------------------------------------------------------
1 | package data.remote.model
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class Titles(
8 | @SerialName("en")
9 | val en: String? = null,
10 | @SerialName("en_jp")
11 | val enJp: String? = null,
12 | @SerialName("en_us")
13 | val enUs: String? = null,
14 | @SerialName("ja_jp")
15 | val jaJp: String? = null,
16 | @SerialName("zh_cn")
17 | val zh_cn: String? = null
18 | )
19 |
--------------------------------------------------------------------------------
/iosApp/iosApp/ContentView.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import SwiftUI
3 | import shared
4 |
5 | struct ComposeView: UIViewControllerRepresentable {
6 | func makeUIViewController(context: Context) -> UIViewController {
7 | Main_iosKt.MainViewController()
8 | }
9 |
10 | func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
11 | }
12 |
13 | struct ContentView: View {
14 | var body: some View {
15 | ComposeView()
16 | .ignoresSafeArea(.keyboard) // Compose has own keyboard handler
17 | }
18 | }
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/data/remote/model/PosterImage.kt:
--------------------------------------------------------------------------------
1 | package data.remote.model
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class PosterImage(
8 | @SerialName("large")
9 | val large: String?,
10 | @SerialName("medium")
11 | val medium: String?,
12 | @SerialName("meta")
13 | val meta: MetaX?,
14 | @SerialName("original")
15 | val original: String?,
16 | @SerialName("small")
17 | val small: String?,
18 | @SerialName("tiny")
19 | val tiny: String?,
20 | )
21 |
--------------------------------------------------------------------------------
/wear-os/src/main/java/com/breens/wear_os/presentation/theme/Theme.kt:
--------------------------------------------------------------------------------
1 | package com.breens.wear_os.presentation.theme
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.wear.compose.material.MaterialTheme
5 |
6 | @Composable
7 | fun AnimeFollowTheme(
8 | content: @Composable () -> Unit
9 | ) {
10 | MaterialTheme(
11 | colors = wearColorPalette,
12 | typography = Typography,
13 | // For shapes, we generally recommend using the default Material Wear shapes which are
14 | // optimized for round and non-round devices.
15 | content = content
16 | )
17 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | #Gradle
2 | org.gradle.jvmargs=-Xmx2048M -Dkotlin.daemon.jvm.options\="-Xmx2048M"
3 |
4 | #Kotlin
5 | kotlin.code.style=official
6 |
7 | #MPP
8 | kotlin.mpp.stability.nowarn=true
9 | kotlin.mpp.enableCInteropCommonization=true
10 | kotlin.mpp.androidSourceSetLayoutVersion=2
11 |
12 | #Compose
13 | org.jetbrains.compose.experimental.uikit.enabled=true
14 | kotlin.native.cacheKind=none
15 |
16 | #Android
17 | android.useAndroidX=true
18 | android.compileSdk=33
19 | android.targetSdk=33
20 | android.minSdk=24
21 |
22 | #Versions
23 | kotlin.version=1.8.20
24 | agp.version=7.4.2
25 | compose.version=1.4.0
--------------------------------------------------------------------------------
/androidApp/src/androidMain/kotlin/com/breensmbaka/AnimeFollowApp.kt:
--------------------------------------------------------------------------------
1 | package com.breensmbaka
2 |
3 | import android.app.Application
4 | import di.initKoin
5 | import org.koin.android.BuildConfig
6 | import org.koin.android.ext.koin.androidContext
7 | import org.koin.android.ext.koin.androidLogger
8 | import org.koin.core.logger.Level
9 | class AnimeFollowApp : Application() {
10 | override fun onCreate() {
11 | super.onCreate()
12 |
13 | initKoin {
14 | androidLogger(level = if (BuildConfig.DEBUG) Level.ERROR else Level.NONE)
15 | androidContext(androidContext = this@AnimeFollowApp)
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/wear-os/src/main/java/com/breens/wear_os/presentation/theme/Color.kt:
--------------------------------------------------------------------------------
1 | package com.breens.wear_os.presentation.theme
2 |
3 | import androidx.compose.ui.graphics.Color
4 | import androidx.wear.compose.material.Colors
5 |
6 | val Purple200 = Color(0xFFBB86FC)
7 | val Purple500 = Color(0xFF6200EE)
8 | val Purple700 = Color(0xFF3700B3)
9 | val Teal200 = Color(0xFF03DAC5)
10 | val Red400 = Color(0xFFCF6679)
11 |
12 | internal val wearColorPalette: Colors = Colors(
13 | primary = Purple200,
14 | primaryVariant = Purple700,
15 | secondary = Teal200,
16 | secondaryVariant = Teal200,
17 | error = Red400,
18 | onPrimary = Color.Black,
19 | onSecondary = Color.Black,
20 | onError = Color.Black
21 | )
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/presentation/components/DetailScreenLoadingComponent.kt:
--------------------------------------------------------------------------------
1 | package presentation.components
2 |
3 | import androidx.compose.foundation.layout.Box
4 | import androidx.compose.foundation.layout.fillMaxWidth
5 | import androidx.compose.foundation.layout.height
6 | import androidx.compose.foundation.shape.RoundedCornerShape
7 | import androidx.compose.runtime.Composable
8 | import androidx.compose.ui.Modifier
9 | import androidx.compose.ui.draw.clip
10 | import androidx.compose.ui.unit.dp
11 | import presentation.utlis.shimmer
12 |
13 | @Composable
14 | fun DetailImageLoadingComponent() {
15 | Box(
16 | modifier = Modifier.height(350.dp).fillMaxWidth().clip(RoundedCornerShape(6.dp))
17 | .shimmer(),
18 | )
19 | }
20 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/utils/ImageLoader.kt:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import androidx.compose.foundation.layout.BoxScope
4 | import androidx.compose.runtime.Composable
5 | import androidx.compose.ui.Alignment
6 | import androidx.compose.ui.Modifier
7 | import androidx.compose.ui.graphics.ColorFilter
8 | import androidx.compose.ui.graphics.FilterQuality
9 | import androidx.compose.ui.layout.ContentScale
10 |
11 | expect fun AsyncImage(
12 | imageUrl: String,
13 | loadingPlaceHolder: @Composable BoxScope.() -> Unit,
14 | errorPlaceHolder: @Composable BoxScope.() -> Unit,
15 | contentDescription: String?,
16 | modifier: Modifier,
17 | alignment: Alignment,
18 | contentScale: ContentScale,
19 | alpha: Float,
20 | coloFilter: ColorFilter?,
21 | filterQuality: FilterQuality,
22 | )
23 |
--------------------------------------------------------------------------------
/wear-os/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/wear-os/src/main/java/com/breens/wear_os/presentation/theme/Type.kt:
--------------------------------------------------------------------------------
1 | package com.breens.wear_os.presentation.theme
2 |
3 | import androidx.compose.ui.text.TextStyle
4 | import androidx.compose.ui.text.font.FontFamily
5 | import androidx.compose.ui.text.font.FontWeight
6 | import androidx.compose.ui.unit.sp
7 | import androidx.wear.compose.material.Typography
8 |
9 | // Set of Material typography styles to start with
10 | val Typography = Typography(
11 | body1 = TextStyle(
12 | fontFamily = FontFamily.Default,
13 | fontWeight = FontWeight.Normal,
14 | fontSize = 16.sp
15 | )
16 | /* Other default text styles to override
17 | button = TextStyle(
18 | fontFamily = FontFamily.Default,
19 | fontWeight = FontWeight.W500,
20 | fontSize = 14.sp
21 | ),
22 | caption = TextStyle(
23 | fontFamily = FontFamily.Default,
24 | fontWeight = FontWeight.Normal,
25 | fontSize = 12.sp
26 | )
27 | */
28 | )
--------------------------------------------------------------------------------
/androidApp/src/androidMain/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
14 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/shared/src/iosMain/kotlin/utils/ImageUtils.kt:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import androidx.compose.ui.graphics.ImageBitmap
4 | import androidx.compose.ui.graphics.toComposeImageBitmap
5 | import io.ktor.client.* // ktlint-disable no-wildcard-imports
6 | import io.ktor.client.request.* // ktlint-disable no-wildcard-imports
7 | import io.ktor.client.statement.* // ktlint-disable no-wildcard-imports
8 | import kotlinx.coroutines.Dispatchers
9 | import kotlinx.coroutines.withContext
10 | import org.jetbrains.skia.Image
11 |
12 | object ImageUtils {
13 |
14 | private val client = HttpClient()
15 |
16 | private val inMemoryCache = mutableMapOf()
17 |
18 | suspend fun getImageBitmapByUrl(url: String): ImageBitmap {
19 | val bytes = inMemoryCache.getOrPut(url) {
20 | client.get(url).readBytes()
21 | }
22 | val bitmap = withContext(Dispatchers.Default) {
23 | Image.makeFromEncoded(bytes).toComposeImageBitmap()
24 | }
25 | return bitmap
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/data/remote/KitsuApi.kt:
--------------------------------------------------------------------------------
1 | package data.remote
2 |
3 | import data.remote.model.AnimeResponse
4 | import io.ktor.client.HttpClient
5 | import io.ktor.client.request.get
6 | import io.ktor.client.request.parameter
7 | import io.ktor.client.request.url
8 | import io.ktor.client.statement.HttpResponse
9 | import io.ktor.client.statement.bodyAsText
10 | import kotlinx.serialization.decodeFromString
11 | import kotlinx.serialization.json.Json
12 | import utils.Constants.TRENDING_ANIME
13 | import utils.Constants.UPCOMING_ANIME
14 |
15 | class KitsuApi(
16 | private val httpClient: HttpClient,
17 | ) {
18 |
19 | suspend fun getTrendingAnime(): AnimeResponse {
20 | return httpClient.get(urlString = TRENDING_ANIME).bodyAsText().let { json ->
21 | Json.decodeFromString(json)
22 | }
23 | }
24 |
25 | suspend fun getUpcomingAnime(): HttpResponse {
26 | return httpClient.get {
27 | url(UPCOMING_ANIME)
28 | parameter("status", "upcoming")
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/domain/dto/AnimeDto.kt:
--------------------------------------------------------------------------------
1 | package domain.dto
2 |
3 | data class AnimeDto(
4 | val data: List = emptyList(),
5 | )
6 |
7 | data class DataDto(
8 | val id: String? = null,
9 | val type: String? = null,
10 | val attributes: AttributesDto? = null,
11 | )
12 |
13 | data class AttributesDto(
14 | val createdAt: String? = null,
15 | val synopsis: String? = null,
16 | val abbreviatedTitles: List?,
17 | val canonicalTitle: String? = null,
18 | val averageRating: String? = null,
19 | val tba: String? = null,
20 | val posterImage: PosterImageDto? = null,
21 | val coverImage: CoverImageDto? = null,
22 | val episodeCount: Int = 0,
23 | val episodeLength: Int = 0,
24 | val youtubeVideoId: String? = null,
25 | val showType: String? = null,
26 | )
27 |
28 | data class PosterImageDto(
29 | val small: String? = null,
30 | val medium: String? = null,
31 | )
32 |
33 | data class CoverImageDto(
34 | val small: String? = null,
35 | val tiny: String? = null,
36 | val original: String? = null
37 | )
38 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/data/remote/trendinganime/AnimeRepositoryImpl.kt:
--------------------------------------------------------------------------------
1 | package data.remote.trendinganime
2 |
3 | import data.remote.KitsuApi
4 | import data.remote.model.AnimeResponse
5 | import domain.dto.DataDto
6 | import domain.mappers.toDomain
7 | import io.ktor.client.call.body
8 | import kotlinx.coroutines.flow.Flow
9 | import kotlinx.coroutines.flow.flow
10 | import utils.NetworkResult
11 | import utils.safeApiCall
12 |
13 | class AnimeRepositoryImpl(
14 | private val kitsuApi: KitsuApi,
15 | ) : AnimeRepository {
16 |
17 | override suspend fun getTrendingAnime(): Flow>> = flow {
18 | val response = safeApiCall {
19 | kitsuApi.getTrendingAnime().data?.map { it.toDomain() } ?: emptyList()
20 | }
21 | emit(response)
22 | }
23 |
24 | override suspend fun getUpcomingAnime(): Flow>> = flow {
25 | val response = safeApiCall {
26 | kitsuApi.getUpcomingAnime().body().data?.map { it.toDomain() } ?: emptyList()
27 | }
28 | emit(response)
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | rootProject.name = "AnimeFollow"
2 |
3 | include(":androidApp")
4 | include(":shared")
5 |
6 | pluginManagement {
7 | repositories {
8 | gradlePluginPortal()
9 | maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
10 | google()
11 | }
12 |
13 | plugins {
14 | val kotlinVersion = extra["kotlin.version"] as String
15 | val agpVersion = extra["agp.version"] as String
16 | val composeVersion = extra["compose.version"] as String
17 |
18 | kotlin("jvm").version(kotlinVersion)
19 | kotlin("multiplatform").version(kotlinVersion)
20 | kotlin("android").version(kotlinVersion)
21 |
22 | id("com.android.application").version(agpVersion)
23 | id("com.android.library").version(agpVersion)
24 |
25 | id("org.jetbrains.compose").version(composeVersion)
26 | id("com.android.application") version "7.4.2"
27 | }
28 | }
29 |
30 | dependencyResolutionManagement {
31 | repositories {
32 | google()
33 | mavenCentral()
34 | maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
35 | }
36 | }
37 | include(":wear-os")
38 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/data/remote/model/RatingFrequencies.kt:
--------------------------------------------------------------------------------
1 | package data.remote.model
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class RatingFrequencies(
8 | @SerialName("10")
9 | val x10: String?,
10 | @SerialName("11")
11 | val x11: String?,
12 | @SerialName("12")
13 | val x12: String?,
14 | @SerialName("13")
15 | val x13: String?,
16 | @SerialName("14")
17 | val x14: String?,
18 | @SerialName("15")
19 | val x15: String?,
20 | @SerialName("16")
21 | val x16: String?,
22 | @SerialName("17")
23 | val x17: String?,
24 | @SerialName("18")
25 | val x18: String?,
26 | @SerialName("19")
27 | val x19: String?,
28 | @SerialName("2")
29 | val x2: String?,
30 | @SerialName("20")
31 | val x20: String?,
32 | @SerialName("3")
33 | val x3: String?,
34 | @SerialName("4")
35 | val x4: String?,
36 | @SerialName("5")
37 | val x5: String?,
38 | @SerialName("6")
39 | val x6: String?,
40 | @SerialName("7")
41 | val x7: String?,
42 | @SerialName("8")
43 | val x8: String?,
44 | @SerialName("9")
45 | val x9: String?,
46 | )
47 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/utils/UtilFunctions.kt:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import io.ktor.client.plugins.ClientRequestException
4 | import io.ktor.client.plugins.RedirectResponseException
5 | import io.ktor.client.plugins.ServerResponseException
6 |
7 | suspend fun safeApiCall(apiCall: suspend () -> T): NetworkResult {
8 | return try {
9 | NetworkResult.Success(data = apiCall.invoke())
10 | } catch (e: RedirectResponseException) { // 3xx errors
11 |
12 | NetworkResult.Error(
13 | errorCode = e.response.status.value,
14 | errorMessage = e.message,
15 | )
16 | } catch (e: ClientRequestException) { // 4xx errors
17 |
18 | NetworkResult.Error(
19 | errorCode = e.response.status.value,
20 | errorMessage = e.message,
21 | )
22 | } catch (e: ServerResponseException) { // 5xx errors
23 |
24 | NetworkResult.Error(
25 | errorCode = e.response.status.value,
26 | errorMessage = e.message,
27 | )
28 | } catch (e: Exception) {
29 | NetworkResult.Error(
30 | errorCode = 0,
31 | errorMessage = e.message ?: "An unknown error occurred",
32 | )
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/androidApp/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | kotlin("multiplatform")
3 | id("com.android.application")
4 | id("org.jetbrains.compose")
5 | }
6 |
7 | kotlin {
8 | android()
9 | sourceSets {
10 | val androidMain by getting {
11 | dependencies {
12 | implementation(project(":shared"))
13 | }
14 | }
15 | }
16 | }
17 |
18 | android {
19 | compileSdk = (findProperty("android.compileSdk") as String).toInt()
20 | namespace = "com.breensmbaka"
21 |
22 | sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
23 |
24 | defaultConfig {
25 | applicationId = "com.breensmbaka.animefollow"
26 | minSdk = (findProperty("android.minSdk") as String).toInt()
27 | targetSdk = (findProperty("android.targetSdk") as String).toInt()
28 | versionCode = 1
29 | versionName = "1.0"
30 | }
31 | compileOptions {
32 | sourceCompatibility = JavaVersion.VERSION_11
33 | targetCompatibility = JavaVersion.VERSION_11
34 | }
35 | kotlin {
36 | jvmToolchain(11)
37 | }
38 | }
39 |
40 | dependencies {
41 | implementation("io.insert-koin:koin-core:3.2.2")
42 | implementation("io.insert-koin:koin-android:3.2.3")
43 | }
--------------------------------------------------------------------------------
/shared/src/androidMain/kotlin/utils/ImageLoader.kt:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import androidx.compose.foundation.layout.BoxScope
4 | import androidx.compose.runtime.Composable
5 | import androidx.compose.ui.Alignment
6 | import androidx.compose.ui.Modifier
7 | import androidx.compose.ui.graphics.ColorFilter
8 | import androidx.compose.ui.graphics.FilterQuality
9 | import androidx.compose.ui.layout.ContentScale
10 | import coil.compose.SubcomposeAsyncImage
11 |
12 | @Composable
13 | actual fun AsyncImage(
14 | imageUrl: String,
15 | loadingPlaceHolder: @Composable BoxScope.() -> Unit,
16 | errorPlaceHolder: @Composable BoxScope.() -> Unit,
17 | contentDescription: String?,
18 | modifier: Modifier,
19 | alignment: Alignment,
20 | contentScale: ContentScale,
21 | alpha: Float,
22 | coloFilter: ColorFilter?,
23 | filterQuality: FilterQuality,
24 | ) {
25 | SubcomposeAsyncImage(
26 | model = imageUrl,
27 | contentDescription = contentDescription,
28 | modifier = modifier,
29 | alignment = alignment,
30 | contentScale = contentScale,
31 | alpha = alpha,
32 | colorFilter = coloFilter,
33 | filterQuality = filterQuality,
34 | loading = { loadingPlaceHolder() },
35 | error = { errorPlaceHolder() },
36 | )
37 | }
38 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/data/remote/model/Relationships.kt:
--------------------------------------------------------------------------------
1 | package data.remote.model
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class Relationships(
8 | @SerialName("animeCharacters")
9 | val animeCharacters: AnimeCharacters?,
10 | @SerialName("animeProductions")
11 | val animeProductions: AnimeProductions?,
12 | @SerialName("animeStaff")
13 | val animeStaff: AnimeStaff?,
14 | @SerialName("castings")
15 | val castings: Castings?,
16 | @SerialName("categories")
17 | val categories: Categories?,
18 | @SerialName("characters")
19 | val characters: Characters?,
20 | @SerialName("episodes")
21 | val episodes: Episodes?,
22 | @SerialName("genres")
23 | val genres: Genres?,
24 | @SerialName("installments")
25 | val installments: Installments?,
26 | @SerialName("mappings")
27 | val mappings: Mappings?,
28 | @SerialName("mediaRelationships")
29 | val mediaRelationships: MediaRelationships?,
30 | @SerialName("productions")
31 | val productions: Productions?,
32 | @SerialName("quotes")
33 | val quotes: Quotes?,
34 | @SerialName("reviews")
35 | val reviews: Reviews?,
36 | @SerialName("staff")
37 | val staff: Staff?,
38 | @SerialName("streamingLinks")
39 | val streamingLinks: StreamingLinks?,
40 | )
41 |
--------------------------------------------------------------------------------
/wear-os/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
14 |
17 |
18 |
22 |
25 |
26 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/domain/mappers/Mappers.kt:
--------------------------------------------------------------------------------
1 | package domain.mappers
2 |
3 | import data.remote.model.AnimeResponse
4 | import data.remote.model.Attributes
5 | import data.remote.model.CoverImage
6 | import data.remote.model.Data
7 | import data.remote.model.PosterImage
8 | import domain.dto.AnimeDto
9 | import domain.dto.AttributesDto
10 | import domain.dto.CoverImageDto
11 | import domain.dto.DataDto
12 | import domain.dto.PosterImageDto
13 |
14 | fun AnimeResponse.toDomain() =
15 | data?.map { it.toDomain() }?.let {
16 | AnimeDto(
17 | data = it,
18 | )
19 | }
20 |
21 | fun Data.toDomain() =
22 | DataDto(
23 | id = id,
24 | type = type,
25 | attributes = attributes?.toDomain(),
26 | )
27 |
28 | fun Attributes.toDomain() =
29 | AttributesDto(
30 | createdAt = createdAt,
31 | synopsis = synopsis,
32 | canonicalTitle = canonicalTitle,
33 | abbreviatedTitles = abbreviatedTitles,
34 | averageRating = averageRating,
35 | tba = tba,
36 | posterImage = posterImage?.toDomain(),
37 | coverImage = coverImage?.toDomain(),
38 | episodeCount = episodeCount ?: 0,
39 | episodeLength = episodeLength ?: 0,
40 | youtubeVideoId = youtubeVideoId,
41 | showType = showType,
42 | )
43 |
44 | fun PosterImage.toDomain() =
45 | PosterImageDto(
46 | small = small,
47 | medium = medium,
48 | )
49 |
50 | fun CoverImage.toDomain() =
51 | CoverImageDto(
52 | small = small,
53 | tiny = tiny,
54 | original = original,
55 | )
56 |
--------------------------------------------------------------------------------
/iosApp/iosApp/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | CADisableMinimumFrameDurationOnPhone
24 |
25 | UIApplicationSceneManifest
26 |
27 | UIApplicationSupportsMultipleScenes
28 |
29 |
30 | UILaunchScreen
31 |
32 | UIRequiredDeviceCapabilities
33 |
34 | armv7
35 |
36 | UISupportedInterfaceOrientations
37 |
38 | UIInterfaceOrientationPortrait
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UISupportedInterfaceOrientations~ipad
43 |
44 | UIInterfaceOrientationPortrait
45 | UIInterfaceOrientationPortraitUpsideDown
46 | UIInterfaceOrientationLandscapeLeft
47 | UIInterfaceOrientationLandscapeRight
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/androidApp/src/androidMain/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/presentation/components/HomeScreenLoadingComponent.kt:
--------------------------------------------------------------------------------
1 | package presentation.components
2 |
3 | import androidx.compose.foundation.layout.Arrangement
4 | import androidx.compose.foundation.layout.Box
5 | import androidx.compose.foundation.layout.Column
6 | import androidx.compose.foundation.layout.Row
7 | import androidx.compose.foundation.layout.Spacer
8 | import androidx.compose.foundation.layout.height
9 | import androidx.compose.foundation.layout.padding
10 | import androidx.compose.foundation.layout.width
11 | import androidx.compose.foundation.shape.RoundedCornerShape
12 | import androidx.compose.runtime.Composable
13 | import androidx.compose.ui.Modifier
14 | import androidx.compose.ui.draw.clip
15 | import androidx.compose.ui.unit.dp
16 | import presentation.utlis.shimmer
17 |
18 | @Composable
19 | fun HomeScreenLoadingComponent() {
20 | Column(modifier = Modifier.padding(14.dp)) {
21 | Box(modifier = Modifier.height(20.dp).width(150.dp).clip(RoundedCornerShape(6.dp)).shimmer())
22 |
23 | Spacer(modifier = Modifier.height(20.dp))
24 |
25 | Row(horizontalArrangement = Arrangement.spacedBy(10.dp)) {
26 | Box(
27 | modifier = Modifier.height(250.dp).width(150.dp).clip(RoundedCornerShape(6.dp))
28 | .shimmer(),
29 | )
30 |
31 | Box(
32 | modifier = Modifier.height(250.dp).width(150.dp).clip(RoundedCornerShape(6.dp))
33 | .shimmer(),
34 | )
35 |
36 | Box(
37 | modifier = Modifier.height(250.dp).width(150.dp).clip(RoundedCornerShape(6.dp))
38 | .shimmer(),
39 | )
40 |
41 | Box(
42 | modifier = Modifier.height(250.dp).width(150.dp).clip(RoundedCornerShape(6.dp))
43 | .shimmer(),
44 | )
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/presentation/utlis/Shimmer.kt:
--------------------------------------------------------------------------------
1 | package presentation.utlis
2 |
3 | import androidx.compose.animation.core.animateFloat
4 | import androidx.compose.animation.core.infiniteRepeatable
5 | import androidx.compose.animation.core.rememberInfiniteTransition
6 | import androidx.compose.animation.core.tween
7 | import androidx.compose.foundation.background
8 | import androidx.compose.runtime.Composable
9 | import androidx.compose.runtime.getValue
10 | import androidx.compose.runtime.mutableStateOf
11 | import androidx.compose.runtime.remember
12 | import androidx.compose.runtime.setValue
13 | import androidx.compose.ui.Modifier
14 | import androidx.compose.ui.composed
15 | import androidx.compose.ui.geometry.Offset
16 | import androidx.compose.ui.graphics.Brush
17 | import androidx.compose.ui.graphics.Color
18 | import androidx.compose.ui.layout.onGloballyPositioned
19 | import androidx.compose.ui.unit.IntSize
20 |
21 | @Composable
22 | fun Modifier.shimmer(): Modifier = composed {
23 | var size by remember {
24 | mutableStateOf(IntSize.Zero)
25 | }
26 |
27 | val transition = rememberInfiniteTransition()
28 |
29 | val startOffsetX by transition.animateFloat(
30 | initialValue = -2 * size.width.toFloat(),
31 | targetValue = 2 * size.width.toFloat(),
32 | animationSpec = infiniteRepeatable(
33 | tween(
34 | durationMillis = 1000,
35 | ),
36 | ),
37 | )
38 |
39 | background(
40 | brush = Brush.linearGradient(
41 | colors = listOf(
42 | Color(0XFFBDBDBD),
43 | Color(0XFF757575),
44 | Color(0XFFBDBDBD),
45 | ),
46 | start = Offset(x = startOffsetX, y = 0f),
47 | end = Offset(x = startOffsetX + size.width.toFloat(), y = size.height.toFloat()),
48 | ),
49 | ).onGloballyPositioned {
50 | size = it.size
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/di/CommonModule.kt:
--------------------------------------------------------------------------------
1 | package di
2 |
3 | import data.remote.KitsuApi
4 | import data.remote.trendinganime.AnimeRepository
5 | import data.remote.trendinganime.AnimeRepositoryImpl
6 | import io.ktor.client.HttpClient
7 | import io.ktor.client.plugins.HttpTimeout
8 | import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
9 | import io.ktor.client.plugins.defaultRequest
10 | import io.ktor.client.plugins.logging.LogLevel
11 | import io.ktor.client.plugins.logging.Logging
12 | import io.ktor.client.request.headers
13 | import io.ktor.http.HttpHeaders
14 | import io.ktor.http.URLProtocol
15 | import io.ktor.serialization.kotlinx.json.json
16 | import kotlinx.serialization.json.Json
17 | import org.koin.dsl.module
18 | import presentation.home.HomeScreenViewModel
19 | import utils.Constants.BASE_URL
20 | import utils.Constants.HEADER_TYPE
21 |
22 | fun commonModule() = module {
23 | single {
24 | HttpClient(get()) {
25 | defaultRequest {
26 | url {
27 | host = BASE_URL
28 | protocol = URLProtocol.HTTPS
29 | }
30 | headers {
31 | append(HttpHeaders.ContentType, HEADER_TYPE)
32 | append(HttpHeaders.Accept, HEADER_TYPE)
33 | }
34 | }
35 |
36 | install(Logging) {
37 | level = LogLevel.HEADERS
38 | }
39 |
40 | install(ContentNegotiation) {
41 | json(
42 | Json {
43 | ignoreUnknownKeys = true
44 | prettyPrint = true
45 | },
46 | )
47 | }
48 |
49 | install(HttpTimeout) {
50 | requestTimeoutMillis = 20000L
51 | connectTimeoutMillis = 20000L
52 | socketTimeoutMillis = 20000L
53 | }
54 | }
55 | }
56 |
57 | single {
58 | KitsuApi(httpClient = get())
59 | }
60 |
61 | single { AnimeRepositoryImpl(kitsuApi = get()) }
62 |
63 | single {
64 | HomeScreenViewModel(animeRepository = get())
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/data/remote/model/Attributes.kt:
--------------------------------------------------------------------------------
1 | package data.remote.model
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class Attributes(
8 | @SerialName("abbreviatedTitles")
9 | val abbreviatedTitles: List?,
10 | @SerialName("ageRating")
11 | val ageRating: String?,
12 | @SerialName("ageRatingGuide")
13 | val ageRatingGuide: String?,
14 | @SerialName("averageRating")
15 | val averageRating: String?,
16 | @SerialName("canonicalTitle")
17 | val canonicalTitle: String?,
18 | @SerialName("coverImage")
19 | val coverImage: CoverImage?,
20 | @SerialName("coverImageTopOffset")
21 | val coverImageTopOffset: Int?,
22 | @SerialName("createdAt")
23 | val createdAt: String?,
24 | @SerialName("description")
25 | val description: String?,
26 | @SerialName("endDate")
27 | val endDate: String?,
28 | @SerialName("episodeCount")
29 | val episodeCount: Int?,
30 | @SerialName("episodeLength")
31 | val episodeLength: Int?,
32 | @SerialName("favoritesCount")
33 | val favoritesCount: Int?,
34 | @SerialName("nextRelease")
35 | val nextRelease: String?,
36 | @SerialName("nsfw")
37 | val nsfw: Boolean?,
38 | @SerialName("popularityRank")
39 | val popularityRank: Int?,
40 | @SerialName("posterImage")
41 | val posterImage: PosterImage?,
42 | @SerialName("ratingFrequencies")
43 | val ratingFrequencies: RatingFrequencies?,
44 | @SerialName("ratingRank")
45 | val ratingRank: Int?,
46 | @SerialName("showType")
47 | val showType: String?,
48 | @SerialName("slug")
49 | val slug: String?,
50 | @SerialName("startDate")
51 | val startDate: String?,
52 | @SerialName("status")
53 | val status: String?,
54 | @SerialName("subtype")
55 | val subtype: String?,
56 | @SerialName("synopsis")
57 | val synopsis: String?,
58 | @SerialName("tba")
59 | val tba: String?,
60 | @SerialName("titles")
61 | val titles: Titles?,
62 | @SerialName("totalLength")
63 | val totalLength: Int?,
64 | @SerialName("updatedAt")
65 | val updatedAt: String?,
66 | @SerialName("userCount")
67 | val userCount: Int?,
68 | @SerialName("youtubeVideoId")
69 | val youtubeVideoId: String?,
70 | )
71 |
--------------------------------------------------------------------------------
/shared/src/iosMain/kotlin/utils/ImageLoader.kt:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import androidx.compose.foundation.Image
4 | import androidx.compose.foundation.layout.Box
5 | import androidx.compose.foundation.layout.BoxScope
6 | import androidx.compose.runtime.Composable
7 | import androidx.compose.runtime.getValue
8 | import androidx.compose.runtime.produceState
9 | import androidx.compose.ui.Alignment
10 | import androidx.compose.ui.Modifier
11 | import androidx.compose.ui.graphics.ColorFilter
12 | import androidx.compose.ui.graphics.FilterQuality
13 | import androidx.compose.ui.graphics.ImageBitmap
14 | import androidx.compose.ui.layout.ContentScale
15 |
16 | @Composable
17 | actual fun AsyncImage(
18 | imageUrl: String,
19 | loadingPlaceHolder: @Composable BoxScope.() -> Unit,
20 | errorPlaceHolder: @Composable BoxScope.() -> Unit,
21 | contentDescription: String?,
22 | modifier: Modifier,
23 | alignment: Alignment,
24 | contentScale: ContentScale,
25 | alpha: Float,
26 | coloFilter: ColorFilter?,
27 | filterQuality: FilterQuality,
28 | ) {
29 | val imageState by rememberImageState(imageUrl)
30 | Box(modifier = modifier) {
31 | when (val state = imageState) {
32 | ImageState.Error -> errorPlaceHolder()
33 | ImageState.Loading -> loadingPlaceHolder()
34 | is ImageState.Success -> {
35 | Image(
36 | bitmap = state.bitmap,
37 | contentDescription = contentDescription,
38 | modifier = modifier,
39 | alignment = alignment,
40 | contentScale = contentScale,
41 | alpha = alpha,
42 | colorFilter = coloFilter,
43 | filterQuality = filterQuality,
44 | )
45 | }
46 | }
47 | }
48 | }
49 |
50 | @Composable
51 | fun rememberImageState(
52 | imageUrl: String,
53 | ) = produceState(initialValue = ImageState.Loading) {
54 | runCatching {
55 | ImageUtils.getImageBitmapByUrl(imageUrl)
56 | }.onSuccess {
57 | value = ImageState.Success(it)
58 | }.onFailure {
59 | value = ImageState.Error
60 | }
61 | }
62 |
63 | sealed class ImageState {
64 | object Loading : ImageState()
65 | object Error : ImageState()
66 | data class Success(val bitmap: ImageBitmap) : ImageState()
67 | }
68 |
--------------------------------------------------------------------------------
/wear-os/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.android.application")
3 | id("org.jetbrains.kotlin.android")
4 | }
5 |
6 | android {
7 | namespace = "com.breens.wear_os"
8 | compileSdk = 33
9 |
10 | defaultConfig {
11 | applicationId = "com.breens.wear_os"
12 | minSdk = 25
13 | targetSdk = 33
14 | versionCode = 1
15 | versionName = "1.0"
16 | vectorDrawables {
17 | useSupportLibrary = true
18 | }
19 |
20 | }
21 |
22 | buildTypes {
23 | release {
24 | isMinifyEnabled = false
25 | proguardFiles(
26 | getDefaultProguardFile("proguard-android-optimize.txt"),
27 | "proguard-rules.pro"
28 | )
29 | }
30 | }
31 | compileOptions {
32 | sourceCompatibility = JavaVersion.VERSION_1_8
33 | targetCompatibility = JavaVersion.VERSION_1_8
34 | }
35 | kotlinOptions {
36 | jvmTarget = "1.8"
37 | }
38 | buildFeatures {
39 | compose = true
40 | }
41 | composeOptions {
42 | kotlinCompilerExtensionVersion = "1.4.5"
43 | }
44 | packagingOptions {
45 | resources {
46 | excludes += "/META-INF/{AL2.0,LGPL2.1}"
47 | }
48 | }
49 | }
50 |
51 | dependencies {
52 |
53 | implementation("androidx.core:core-ktx:1.9.0")
54 | implementation("com.google.android.gms:play-services-wearable:18.0.0")
55 | implementation("androidx.percentlayout:percentlayout:1.0.0")
56 | implementation("androidx.legacy:legacy-support-v4:1.0.0")
57 | implementation("androidx.recyclerview:recyclerview:1.3.0")
58 | implementation(platform("androidx.compose:compose-bom:2023.03.00"))
59 | implementation("androidx.compose.ui:ui")
60 | implementation("androidx.compose.ui:ui-tooling-preview")
61 | implementation("androidx.wear.compose:compose-material:1.0.0")
62 | implementation("androidx.wear.compose:compose-foundation:1.0.0")
63 | implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.5.1")
64 | implementation("androidx.activity:activity-compose:1.5.1")
65 | androidTestImplementation(platform("androidx.compose:compose-bom:2023.03.00"))
66 | androidTestImplementation("androidx.compose.ui:ui-test-junit4")
67 | debugImplementation("androidx.compose.ui:ui-tooling")
68 | debugImplementation("androidx.compose.ui:ui-test-manifest")
69 | }
--------------------------------------------------------------------------------
/wear-os/src/main/java/com/breens/wear_os/presentation/MainActivity.kt:
--------------------------------------------------------------------------------
1 | /* While this template provides a good starting point for using Wear Compose, you can always
2 | * take a look at https://github.com/android/wear-os-samples/tree/main/ComposeStarter and
3 | * https://github.com/android/wear-os-samples/tree/main/ComposeAdvanced to find the most up to date
4 | * changes to the libraries and their usages.
5 | */
6 |
7 | package com.breens.wear_os.presentation
8 |
9 | import android.os.Bundle
10 | import androidx.activity.ComponentActivity
11 | import androidx.activity.compose.setContent
12 | import androidx.compose.foundation.background
13 | import androidx.compose.foundation.layout.Arrangement
14 | import androidx.compose.foundation.layout.Column
15 | import androidx.compose.foundation.layout.fillMaxSize
16 | import androidx.compose.foundation.layout.fillMaxWidth
17 | import androidx.compose.runtime.Composable
18 | import androidx.compose.ui.Modifier
19 | import androidx.compose.ui.res.stringResource
20 | import androidx.compose.ui.text.style.TextAlign
21 | import androidx.compose.ui.tooling.preview.Devices
22 | import androidx.compose.ui.tooling.preview.Preview
23 | import androidx.wear.compose.material.MaterialTheme
24 | import androidx.wear.compose.material.Text
25 | import com.breens.wear_os.R
26 | import com.breens.wear_os.presentation.theme.AnimeFollowTheme
27 |
28 | class MainActivity : ComponentActivity() {
29 | override fun onCreate(savedInstanceState: Bundle?) {
30 | super.onCreate(savedInstanceState)
31 | setContent {
32 | WearApp("Android")
33 | }
34 | }
35 | }
36 |
37 | @Composable
38 | fun WearApp(greetingName: String) {
39 | AnimeFollowTheme {
40 | /* If you have enough items in your list, use [ScalingLazyColumn] which is an optimized
41 | * version of LazyColumn for wear devices with some added features. For more information,
42 | * see d.android.com/wear/compose.
43 | */
44 | Column(
45 | modifier = Modifier
46 | .fillMaxSize()
47 | .background(MaterialTheme.colors.background),
48 | verticalArrangement = Arrangement.Center
49 | ) {
50 | Greeting(greetingName = greetingName)
51 | }
52 | }
53 | }
54 |
55 | @Composable
56 | fun Greeting(greetingName: String) {
57 | Text(
58 | modifier = Modifier.fillMaxWidth(),
59 | textAlign = TextAlign.Center,
60 | color = MaterialTheme.colors.primary,
61 | text = stringResource(R.string.hello_world, greetingName)
62 | )
63 | }
64 |
65 | @Preview(device = Devices.WEAR_OS_SMALL_ROUND, showSystemUi = true)
66 | @Composable
67 | fun DefaultPreview() {
68 | WearApp("Preview Android")
69 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AnimeFollowKMM
2 | An app that allows you to discover anime built with Kotlin Multiplatform Mobile which allows sharing of both UI and logic between the Android and IOS platforms 📱
3 |
4 | ## Demo
5 | ### iOS
6 |
7 |
8 | #### Android
9 |
10 |
11 | ## Tech Stack.
12 | - [Voyager](https://github.com/adrielcafe/voyager) - A multiplatform navigation library built for, and seamlessly integrated with, Jetpack Compose.
13 | - [Koin](https://insert-koin.io/) - Koin is a lightweight and pragmatic dependency injection framework for Android development, allowing for easy management and injection of dependencies into Android applications.
14 | - [Ktor](https://ktor.io/) - Ktor for Android is a lightweight and efficient framework that enables developers to build high-performance server-side applications and web services on the Android platform using Kotlin.
15 | - [Kitsu API](https://kitsu.docs.apiary.io/#introduction) - Kitsu is a modern anime discovery platform that helps you track the anime you're watching, discover new anime and socialize with other fans.
16 |
17 | ## Benefits of KMM
18 | - Code sharing: With KMM, you can write shared code that can be used across multiple platforms, such as Android and iOS.
19 | - Single-language development: KMM enables you to use Kotlin as the primary programming language for both the shared code and platform-specific code.
20 | - Consistent business logic: KMM allows you to implement and share business logic, network calls, data models, and other core functionalities across platforms.
21 | - Faster time to market: By sharing code and leveraging KMM's cross-platform capabilities, developers can significantly reduce development time and effort.
22 | - Reduced maintenance overhead
23 | - Native platform integration: KMM allows seamless integration with platform-specific APIs, libraries, and UI components.
24 |
25 | ## Resourceful Projects 🌟
26 | - [GoTCharactersKMM](https://github.com/JoelKanyi/GoTCharactersKMM.git) - by [Joel Kanyi](https://github.com/JoelKanyi)
27 | - [Foodium-KMM](https://github.com/PatilShreyas/Foodium-KMM.git) - by [Shreyas Patil](https://github.com/PatilShreyas)
28 | - [Compose Multiplatform iOS+Android Application project template](https://github.com/JetBrains/compose-multiplatform-ios-android-template)
29 |
30 | ## Goals
31 | - [ ] Add ability to add to a favorite list
32 | - [ ] Show characters of a particular anime
33 | - [ ] Improve UI/UX
34 | - [ ] Filter anime by categories
35 | - [ ] Publish to PlayStore
36 | - [ ] Publish to AppStore
37 | - [ ] Handle different states gracefully and in a scalable approach
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/presentation/components/AnimeComponent.kt:
--------------------------------------------------------------------------------
1 | package presentation.components
2 |
3 | import androidx.compose.foundation.clickable
4 | import androidx.compose.foundation.layout.Arrangement
5 | import androidx.compose.foundation.layout.Column
6 | import androidx.compose.foundation.layout.height
7 | import androidx.compose.foundation.layout.width
8 | import androidx.compose.foundation.shape.RoundedCornerShape
9 | import androidx.compose.material.Card
10 | import androidx.compose.material.Text
11 | import androidx.compose.runtime.Composable
12 | import androidx.compose.ui.Alignment
13 | import androidx.compose.ui.Modifier
14 | import androidx.compose.ui.graphics.Color
15 | import androidx.compose.ui.graphics.DefaultAlpha
16 | import androidx.compose.ui.graphics.drawscope.DrawScope
17 | import androidx.compose.ui.layout.ContentScale
18 | import androidx.compose.ui.text.font.FontWeight
19 | import androidx.compose.ui.text.style.TextAlign
20 | import androidx.compose.ui.unit.dp
21 | import domain.dto.DataDto
22 | import utils.AsyncImage
23 |
24 | @Composable
25 | fun AnimeComponent(
26 | dataDto: DataDto,
27 | navigateToAnimeDetailsScreen: (DataDto) -> Unit,
28 | ) {
29 | Column(verticalArrangement = Arrangement.Center) {
30 | if (dataDto.attributes?.posterImage?.medium != null) {
31 | Card(
32 | modifier = Modifier
33 | .height(250.dp)
34 | .width(150.dp)
35 | .clickable {
36 | navigateToAnimeDetailsScreen(dataDto)
37 | },
38 | shape = RoundedCornerShape(15.dp),
39 | ) {
40 | AsyncImage(
41 | imageUrl = dataDto.attributes.posterImage.medium ?: "",
42 | contentDescription = "Anime",
43 | contentScale = ContentScale.Crop,
44 | loadingPlaceHolder = {},
45 | modifier = Modifier,
46 | errorPlaceHolder = {},
47 | alignment = Alignment.Center,
48 | alpha = DefaultAlpha,
49 | coloFilter = null,
50 | filterQuality = DrawScope.DefaultFilterQuality,
51 | )
52 | }
53 | } else {
54 | Card(
55 | modifier = Modifier.height(200.dp).width(120.dp),
56 | shape = RoundedCornerShape(15.dp),
57 | backgroundColor = Color.LightGray,
58 | ) {
59 | }
60 | }
61 |
62 | Text(
63 | text = dataDto.attributes?.canonicalTitle ?: "",
64 | textAlign = TextAlign.Start,
65 | softWrap = true,
66 | modifier = Modifier.width(100.dp),
67 | fontWeight = FontWeight.Medium,
68 | )
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%"=="" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%"=="" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if %ERRORLEVEL% equ 0 goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if %ERRORLEVEL% equ 0 goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | set EXIT_CODE=%ERRORLEVEL%
84 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
85 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
86 | exit /b %EXIT_CODE%
87 |
88 | :mainEnd
89 | if "%OS%"=="Windows_NT" endlocal
90 |
91 | :omega
92 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/presentation/home/HomeScreenViewModel.kt:
--------------------------------------------------------------------------------
1 | package presentation.home
2 |
3 | import cafe.adriel.voyager.core.model.StateScreenModel
4 | import cafe.adriel.voyager.core.model.coroutineScope
5 | import data.remote.trendinganime.AnimeRepository
6 | import domain.dto.DataDto
7 | import kotlinx.coroutines.flow.update
8 | import kotlinx.coroutines.launch
9 | import utils.NetworkResult
10 |
11 | class HomeScreenViewModel(
12 | private val animeRepository: AnimeRepository,
13 | ) : StateScreenModel(HomeScreenUiState()) {
14 |
15 | init {
16 | getTrendingAnime()
17 |
18 | getUpcomingAnime()
19 | }
20 |
21 | private fun getTrendingAnime() {
22 | coroutineScope.launch {
23 | mutableState.update {
24 | it.copy(
25 | loadingTrendingAnime = true,
26 | )
27 | }
28 |
29 | animeRepository.getTrendingAnime().collect { result ->
30 | when (result) {
31 | is NetworkResult.Error -> {
32 | mutableState.update {
33 | it.copy(
34 | loadingTrendingAnime = false,
35 | errorMessageTrendingAnime = result.errorMessage
36 | ?: "Something wrong happened!",
37 | )
38 | }
39 | }
40 |
41 | is NetworkResult.Success -> {
42 | mutableState.update {
43 | it.copy(
44 | loadingTrendingAnime = false,
45 | trendingAnime = result.data ?: emptyList(),
46 | )
47 | }
48 | }
49 | }
50 | }
51 | }
52 | }
53 |
54 | private fun getUpcomingAnime() {
55 | coroutineScope.launch {
56 | mutableState.update {
57 | it.copy(
58 | loadingUpcomingAnime = true,
59 | )
60 | }
61 |
62 | animeRepository.getUpcomingAnime().collect { result ->
63 | when (result) {
64 | is NetworkResult.Error -> {
65 | mutableState.update {
66 | it.copy(
67 | loadingUpcomingAnime = false,
68 | errorMessageUpcomingAnime = result.errorMessage
69 | ?: "Something wrong happened!",
70 | )
71 | }
72 | }
73 |
74 | is NetworkResult.Success -> {
75 | mutableState.update {
76 | it.copy(
77 | loadingUpcomingAnime = false,
78 | upcomingAnime = result.data ?: emptyList(),
79 | )
80 | }
81 | }
82 | }
83 | }
84 | }
85 | }
86 | }
87 |
88 | data class HomeScreenUiState(
89 | val loadingTrendingAnime: Boolean = false,
90 | val trendingAnime: List = emptyList(),
91 | val errorMessageTrendingAnime: String? = null,
92 | val loadingUpcomingAnime: Boolean = false,
93 | val upcomingAnime: List = emptyList(),
94 | val errorMessageUpcomingAnime: String? = null,
95 | )
96 |
--------------------------------------------------------------------------------
/shared/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | kotlin("multiplatform")
3 | kotlin("native.cocoapods")
4 | id("com.android.library")
5 | id("org.jetbrains.compose")
6 | kotlin("plugin.serialization") version "1.8.10"
7 | }
8 |
9 | kotlin {
10 | android()
11 |
12 | iosX64()
13 | iosArm64()
14 | iosSimulatorArm64()
15 |
16 | cocoapods {
17 | version = "1.0.0"
18 | summary = "Some description for the Shared Module"
19 | homepage = "Link to the Shared Module homepage"
20 | ios.deploymentTarget = "14.1"
21 | podfile = project.file("../iosApp/Podfile")
22 | framework {
23 | baseName = "shared"
24 | isStatic = true
25 | }
26 | extraSpecAttributes["resources"] =
27 | "['src/commonMain/resources/**', 'src/iosMain/resources/**']"
28 | }
29 |
30 | sourceSets {
31 | val commonMain by getting {
32 | dependencies {
33 | implementation(compose.runtime)
34 | implementation(compose.foundation)
35 | implementation(compose.material)
36 | @OptIn(org.jetbrains.compose.ExperimentalComposeLibrary::class)
37 | implementation(compose.components.resources)
38 |
39 | // Koin
40 | api("io.insert-koin:koin-core:3.2.2")
41 |
42 | // Ktor
43 | implementation("io.ktor:ktor-client-core:2.2.1")
44 | implementation("io.ktor:ktor-client-content-negotiation:2.2.1")
45 | implementation("io.ktor:ktor-serialization-kotlinx-json:2.2.1")
46 | implementation("io.ktor:ktor-client-json:2.2.1")
47 | implementation("io.ktor:ktor-client-logging:2.2.1")
48 | implementation("io.ktor:ktor-client-serialization:2.2.1")
49 |
50 | // Voyager
51 | val voyagerVersion = "1.0.0-rc04"
52 | implementation("cafe.adriel.voyager:voyager-navigator:$voyagerVersion")
53 | }
54 | }
55 | val androidMain by getting {
56 | dependencies {
57 | api("androidx.activity:activity-compose:1.6.1")
58 | api("androidx.appcompat:appcompat:1.6.1")
59 | api("androidx.core:core-ktx:1.9.0")
60 |
61 | api("io.ktor:ktor-client-android:2.2.1")
62 | implementation("io.coil-kt:coil-compose:2.3.0")
63 | }
64 | }
65 | val iosX64Main by getting
66 | val iosArm64Main by getting
67 | val iosSimulatorArm64Main by getting
68 | val iosMain by creating {
69 | dependsOn(commonMain)
70 | iosX64Main.dependsOn(this)
71 | iosArm64Main.dependsOn(this)
72 | iosSimulatorArm64Main.dependsOn(this)
73 | dependencies {
74 | implementation("io.ktor:ktor-client-darwin:2.2.1")
75 | }
76 | }
77 | }
78 | }
79 |
80 | android {
81 | compileSdk = (findProperty("android.compileSdk") as String).toInt()
82 | namespace = "com.breensmbaka.common"
83 |
84 | sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
85 | sourceSets["main"].res.srcDirs("src/androidMain/res")
86 | sourceSets["main"].resources.srcDirs("src/commonMain/resources")
87 |
88 | defaultConfig {
89 | minSdk = (findProperty("android.minSdk") as String).toInt()
90 | targetSdk = (findProperty("android.targetSdk") as String).toInt()
91 | }
92 | compileOptions {
93 | sourceCompatibility = JavaVersion.VERSION_11
94 | targetCompatibility = JavaVersion.VERSION_11
95 | }
96 | kotlin {
97 | jvmToolchain(11)
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/shared/src/commonMain/resources/compose-multiplatform.xml:
--------------------------------------------------------------------------------
1 |
6 |
10 |
14 |
18 |
24 |
30 |
36 |
37 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/presentation/home/HomeScreen.kt:
--------------------------------------------------------------------------------
1 | package presentation.home
2 |
3 | import androidx.compose.foundation.layout.Arrangement
4 | import androidx.compose.foundation.layout.Box
5 | import androidx.compose.foundation.layout.Column
6 | import androidx.compose.foundation.layout.PaddingValues
7 | import androidx.compose.foundation.layout.Spacer
8 | import androidx.compose.foundation.layout.fillMaxSize
9 | import androidx.compose.foundation.layout.height
10 | import androidx.compose.foundation.lazy.LazyColumn
11 | import androidx.compose.foundation.lazy.LazyRow
12 | import androidx.compose.foundation.lazy.items
13 | import androidx.compose.material.Text
14 | import androidx.compose.runtime.Composable
15 | import androidx.compose.runtime.collectAsState
16 | import androidx.compose.ui.Alignment
17 | import androidx.compose.ui.Modifier
18 | import androidx.compose.ui.text.font.FontWeight
19 | import androidx.compose.ui.unit.dp
20 | import androidx.compose.ui.unit.sp
21 | import cafe.adriel.voyager.core.screen.Screen
22 | import cafe.adriel.voyager.navigator.LocalNavigator
23 | import cafe.adriel.voyager.navigator.currentOrThrow
24 | import org.koin.core.component.KoinComponent
25 | import org.koin.core.component.inject
26 | import presentation.components.AnimeComponent
27 | import presentation.components.HomeScreenLoadingComponent
28 | import presentation.details.AnimeDetailsScreen
29 |
30 | internal class HomeScreen : Screen, KoinComponent {
31 |
32 | @Composable
33 | override fun Content() {
34 | val navigator = LocalNavigator.currentOrThrow
35 | val homeScreenViewModel: HomeScreenViewModel by inject()
36 | val homeScreenUiState = homeScreenViewModel.state.collectAsState()
37 |
38 | Box(modifier = Modifier.fillMaxSize()) {
39 | LazyColumn(
40 | verticalArrangement = Arrangement.spacedBy(14.dp),
41 | contentPadding = PaddingValues(14.dp),
42 | ) {
43 | item {
44 | if (homeScreenUiState.value.loadingTrendingAnime) {
45 | HomeScreenLoadingComponent()
46 | }
47 |
48 | if (!homeScreenUiState.value.loadingTrendingAnime) {
49 | Column {
50 | Text(
51 | "🔥 Trending Anime",
52 | fontSize = 18.sp,
53 | fontWeight = FontWeight.Bold,
54 | )
55 |
56 | Spacer(modifier = Modifier.height(8.dp))
57 |
58 | LazyRow(horizontalArrangement = Arrangement.spacedBy(10.dp)) {
59 | items(homeScreenUiState.value.trendingAnime) { trendingAnime ->
60 | AnimeComponent(
61 | dataDto = trendingAnime,
62 | navigateToAnimeDetailsScreen = {
63 | navigator.push(AnimeDetailsScreen(dataDto = it))
64 | },
65 | )
66 | }
67 | }
68 | }
69 | }
70 | }
71 |
72 | item {
73 | if (homeScreenUiState.value.loadingUpcomingAnime) {
74 | HomeScreenLoadingComponent()
75 | }
76 |
77 | if (!homeScreenUiState.value.loadingUpcomingAnime) {
78 | Column {
79 | Text(
80 | "📆 Upcoming Anime",
81 | fontSize = 18.sp,
82 | fontWeight = FontWeight.Bold,
83 | )
84 |
85 | Spacer(modifier = Modifier.height(8.dp))
86 |
87 | LazyRow(horizontalArrangement = Arrangement.spacedBy(10.dp)) {
88 | items(homeScreenUiState.value.upcomingAnime) { trendingAnime ->
89 | AnimeComponent(
90 | dataDto = trendingAnime,
91 | navigateToAnimeDetailsScreen = {
92 | navigator.push(AnimeDetailsScreen(dataDto = it))
93 | },
94 | )
95 | }
96 | }
97 | }
98 | }
99 | }
100 | }
101 | if (!homeScreenUiState.value.loadingUpcomingAnime && homeScreenUiState.value.errorMessageUpcomingAnime != null) {
102 | Text(
103 | text = homeScreenUiState.value.errorMessageTrendingAnime ?: "---",
104 | modifier = Modifier.align(
105 | Alignment.Center,
106 | ),
107 | )
108 | }
109 | }
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/androidApp/src/androidMain/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/presentation/details/AnimeDetailsScreen.kt:
--------------------------------------------------------------------------------
1 | package presentation.details
2 |
3 | import androidx.compose.animation.animateContentSize
4 | import androidx.compose.animation.core.tween
5 | import androidx.compose.foundation.clickable
6 | import androidx.compose.foundation.interaction.MutableInteractionSource
7 | import androidx.compose.foundation.layout.Arrangement
8 | import androidx.compose.foundation.layout.Column
9 | import androidx.compose.foundation.layout.Row
10 | import androidx.compose.foundation.layout.Spacer
11 | import androidx.compose.foundation.layout.height
12 | import androidx.compose.foundation.layout.padding
13 | import androidx.compose.foundation.lazy.LazyColumn
14 | import androidx.compose.material.Icon
15 | import androidx.compose.material.IconButton
16 | import androidx.compose.material.Scaffold
17 | import androidx.compose.material.Text
18 | import androidx.compose.material.TopAppBar
19 | import androidx.compose.material.icons.Icons
20 | import androidx.compose.material.icons.rounded.ArrowBack
21 | import androidx.compose.runtime.Composable
22 | import androidx.compose.runtime.getValue
23 | import androidx.compose.runtime.mutableStateOf
24 | import androidx.compose.runtime.remember
25 | import androidx.compose.runtime.setValue
26 | import androidx.compose.ui.Alignment
27 | import androidx.compose.ui.Modifier
28 | import androidx.compose.ui.graphics.Color
29 | import androidx.compose.ui.graphics.DefaultAlpha
30 | import androidx.compose.ui.graphics.drawscope.DrawScope
31 | import androidx.compose.ui.layout.ContentScale
32 | import androidx.compose.ui.text.font.FontWeight
33 | import androidx.compose.ui.text.style.TextDecoration
34 | import androidx.compose.ui.text.style.TextOverflow
35 | import androidx.compose.ui.unit.dp
36 | import androidx.compose.ui.unit.sp
37 | import cafe.adriel.voyager.core.screen.Screen
38 | import cafe.adriel.voyager.navigator.LocalNavigator
39 | import cafe.adriel.voyager.navigator.currentOrThrow
40 | import domain.dto.DataDto
41 | import org.koin.core.component.KoinComponent
42 | import presentation.components.DetailImageLoadingComponent
43 | import utils.AsyncImage
44 |
45 | internal class AnimeDetailsScreen(
46 | private val dataDto: DataDto,
47 | ) : Screen, KoinComponent {
48 |
49 | @Composable
50 | override fun Content() {
51 | val navigator = LocalNavigator.currentOrThrow
52 |
53 | Scaffold(
54 | topBar = {
55 | TopAppBar(
56 | backgroundColor = Color.Black,
57 | content = {
58 | Row(
59 | verticalAlignment = Alignment.CenterVertically,
60 | horizontalArrangement = Arrangement.Start,
61 | ) {
62 | IconButton(
63 | onClick = { navigator.pop() },
64 | ) {
65 | Icon(
66 | imageVector = Icons.Rounded.ArrowBack,
67 | contentDescription = "Back",
68 | tint = Color.White,
69 | )
70 | }
71 | Text(
72 | text = "${dataDto.attributes?.canonicalTitle}",
73 | color = Color.White,
74 | fontSize = 18.sp,
75 | softWrap = true,
76 | )
77 | }
78 | },
79 | )
80 | },
81 | ) {
82 | LazyColumn {
83 | item {
84 | AsyncImage(
85 | imageUrl = dataDto.attributes?.coverImage?.original ?: "",
86 | contentDescription = "Anime",
87 | contentScale = ContentScale.Crop,
88 | loadingPlaceHolder = {
89 | DetailImageLoadingComponent()
90 | },
91 | modifier = Modifier.fillParentMaxWidth().height(250.dp),
92 | errorPlaceHolder = {},
93 | alignment = Alignment.Center,
94 | alpha = DefaultAlpha,
95 | coloFilter = null,
96 | filterQuality = DrawScope.DefaultFilterQuality,
97 | )
98 |
99 | Spacer(modifier = Modifier.height(12.dp))
100 | }
101 |
102 | item {
103 | Text(
104 | "Synopsis",
105 | fontSize = 18.sp,
106 | fontWeight = FontWeight.ExtraBold,
107 | modifier = Modifier.padding(start = 14.dp, bottom = 12.dp),
108 | textDecoration = TextDecoration.Underline,
109 | )
110 | }
111 |
112 | item {
113 | var showMoreInfo by remember { mutableStateOf(false) }
114 | val interactionSource = remember { MutableInteractionSource() }
115 | val synopsis = dataDto.attributes?.synopsis ?: "No synopsis found"
116 |
117 | Column(
118 | modifier = Modifier
119 | .animateContentSize(animationSpec = tween(100))
120 | .clickable(
121 | interactionSource = interactionSource,
122 | indication = null,
123 | ) { showMoreInfo = !showMoreInfo },
124 | ) {
125 | if (showMoreInfo) {
126 | Text(
127 | text = synopsis,
128 | fontSize = 16.sp,
129 | modifier = Modifier.padding(14.dp),
130 | )
131 | } else {
132 | Text(
133 | text = synopsis,
134 | fontSize = 16.sp,
135 | maxLines = 3,
136 | overflow = TextOverflow.Ellipsis,
137 | modifier = Modifier.padding(14.dp),
138 | )
139 | }
140 | }
141 | }
142 | }
143 | }
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | #
21 | # Gradle start up script for POSIX generated by Gradle.
22 | #
23 | # Important for running:
24 | #
25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
26 | # noncompliant, but you have some other compliant shell such as ksh or
27 | # bash, then to run this script, type that shell name before the whole
28 | # command line, like:
29 | #
30 | # ksh Gradle
31 | #
32 | # Busybox and similar reduced shells will NOT work, because this script
33 | # requires all of these POSIX shell features:
34 | # * functions;
35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
37 | # * compound commands having a testable exit status, especially «case»;
38 | # * various built-in commands including «command», «set», and «ulimit».
39 | #
40 | # Important for patching:
41 | #
42 | # (2) This script targets any POSIX shell, so it avoids extensions provided
43 | # by Bash, Ksh, etc; in particular arrays are avoided.
44 | #
45 | # The "traditional" practice of packing multiple parameters into a
46 | # space-separated string is a well documented source of bugs and security
47 | # problems, so this is (mostly) avoided, by progressively accumulating
48 | # options in "$@", and eventually passing that to Java.
49 | #
50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
52 | # see the in-line comments for details.
53 | #
54 | # There are tweaks for specific operating systems such as AIX, CygWin,
55 | # Darwin, MinGW, and NonStop.
56 | #
57 | # (3) This script is generated from the Groovy template
58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
59 | # within the Gradle project.
60 | #
61 | # You can find Gradle at https://github.com/gradle/gradle/.
62 | #
63 | ##############################################################################
64 |
65 | # Attempt to set APP_HOME
66 |
67 | # Resolve links: $0 may be a link
68 | app_path=$0
69 |
70 | # Need this for daisy-chained symlinks.
71 | while
72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
73 | [ -h "$app_path" ]
74 | do
75 | ls=$( ls -ld "$app_path" )
76 | link=${ls#*' -> '}
77 | case $link in #(
78 | /*) app_path=$link ;; #(
79 | *) app_path=$APP_HOME$link ;;
80 | esac
81 | done
82 |
83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
84 |
85 | APP_NAME="Gradle"
86 | APP_BASE_NAME=${0##*/}
87 |
88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
90 |
91 | # Use the maximum available, or set MAX_FD != -1 to use that value.
92 | MAX_FD=maximum
93 |
94 | warn () {
95 | echo "$*"
96 | } >&2
97 |
98 | die () {
99 | echo
100 | echo "$*"
101 | echo
102 | exit 1
103 | } >&2
104 |
105 | # OS specific support (must be 'true' or 'false').
106 | cygwin=false
107 | msys=false
108 | darwin=false
109 | nonstop=false
110 | case "$( uname )" in #(
111 | CYGWIN* ) cygwin=true ;; #(
112 | Darwin* ) darwin=true ;; #(
113 | MSYS* | MINGW* ) msys=true ;; #(
114 | NONSTOP* ) nonstop=true ;;
115 | esac
116 |
117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
118 |
119 |
120 | # Determine the Java command to use to start the JVM.
121 | if [ -n "$JAVA_HOME" ] ; then
122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
123 | # IBM's JDK on AIX uses strange locations for the executables
124 | JAVACMD=$JAVA_HOME/jre/sh/java
125 | else
126 | JAVACMD=$JAVA_HOME/bin/java
127 | fi
128 | if [ ! -x "$JAVACMD" ] ; then
129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
130 |
131 | Please set the JAVA_HOME variable in your environment to match the
132 | location of your Java installation."
133 | fi
134 | else
135 | JAVACMD=java
136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
137 |
138 | Please set the JAVA_HOME variable in your environment to match the
139 | location of your Java installation."
140 | fi
141 |
142 | # Increase the maximum file descriptors if we can.
143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
144 | case $MAX_FD in #(
145 | max*)
146 | MAX_FD=$( ulimit -H -n ) ||
147 | warn "Could not query maximum file descriptor limit"
148 | esac
149 | case $MAX_FD in #(
150 | '' | soft) :;; #(
151 | *)
152 | ulimit -n "$MAX_FD" ||
153 | warn "Could not set maximum file descriptor limit to $MAX_FD"
154 | esac
155 | fi
156 |
157 | # Collect all arguments for the java command, stacking in reverse order:
158 | # * args from the command line
159 | # * the main class name
160 | # * -classpath
161 | # * -D...appname settings
162 | # * --module-path (only if needed)
163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
164 |
165 | # For Cygwin or MSYS, switch paths to Windows format before running java
166 | if "$cygwin" || "$msys" ; then
167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
169 |
170 | JAVACMD=$( cygpath --unix "$JAVACMD" )
171 |
172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
173 | for arg do
174 | if
175 | case $arg in #(
176 | -*) false ;; # don't mess with options #(
177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
178 | [ -e "$t" ] ;; #(
179 | *) false ;;
180 | esac
181 | then
182 | arg=$( cygpath --path --ignore --mixed "$arg" )
183 | fi
184 | # Roll the args list around exactly as many times as the number of
185 | # args, so each arg winds up back in the position where it started, but
186 | # possibly modified.
187 | #
188 | # NB: a `for` loop captures its iteration list before it begins, so
189 | # changing the positional parameters here affects neither the number of
190 | # iterations, nor the values presented in `arg`.
191 | shift # remove old arg
192 | set -- "$@" "$arg" # push replacement arg
193 | done
194 | fi
195 |
196 | # Collect all arguments for the java command;
197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
198 | # shell script including quotes and variable substitutions, so put them in
199 | # double quotes to make sure that they get re-expanded; and
200 | # * put everything else in single quotes, so that it's not re-expanded.
201 |
202 | set -- \
203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
204 | -classpath "$CLASSPATH" \
205 | org.gradle.wrapper.GradleWrapperMain \
206 | "$@"
207 |
208 | # Stop when "xargs" is not available.
209 | if ! command -v xargs >/dev/null 2>&1
210 | then
211 | die "xargs is not available"
212 | fi
213 |
214 | # Use "xargs" to parse quoted args.
215 | #
216 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
217 | #
218 | # In Bash we could simply go:
219 | #
220 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
221 | # set -- "${ARGS[@]}" "$@"
222 | #
223 | # but POSIX shell has neither arrays nor command substitution, so instead we
224 | # post-process each arg (as a line of input to sed) to backslash-escape any
225 | # character that might be a shell metacharacter, then use eval to reverse
226 | # that process (while maintaining the separation between arguments), and wrap
227 | # the whole thing up as a single "set" statement.
228 | #
229 | # This will of course break if any of these variables contains a newline or
230 | # an unmatched quote.
231 | #
232 |
233 | eval "set -- $(
234 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
235 | xargs -n1 |
236 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
237 | tr '\n' ' '
238 | )" '"$@"'
239 |
240 | exec "$JAVACMD" "$@"
241 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright 2020-2021 JetBrains s.r.o. and and respective authors and developers.
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/iosApp/iosApp.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 51;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557BA273AAA24004C7B11 /* Assets.xcassets */; };
11 | 058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */; };
12 | 2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2152FB032600AC8F00CF470E /* iOSApp.swift */; };
13 | 7555FF83242A565900829871 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7555FF82242A565900829871 /* ContentView.swift */; };
14 | CFDB58B53BB94DE262B13C24 /* Pods_iosApp.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6B1049432C0C2B312090ABF6 /* Pods_iosApp.framework */; };
15 | /* End PBXBuildFile section */
16 |
17 | /* Begin PBXFileReference section */
18 | 058557BA273AAA24004C7B11 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
19 | 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
20 | 2152FB032600AC8F00CF470E /* iOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSApp.swift; sourceTree = ""; };
21 | 4FF3202A603A284706412EDC /* Pods-iosApp.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-iosApp.debug.xcconfig"; path = "Target Support Files/Pods-iosApp/Pods-iosApp.debug.xcconfig"; sourceTree = ""; };
22 | 6B1049432C0C2B312090ABF6 /* Pods_iosApp.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_iosApp.framework; sourceTree = BUILT_PRODUCTS_DIR; };
23 | 7555FF7B242A565900829871 /* MyApplication.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MyApplication.app; sourceTree = BUILT_PRODUCTS_DIR; };
24 | 7555FF82242A565900829871 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; };
25 | 7555FF8C242A565B00829871 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
26 | AB3632DC29227652001CCB65 /* Config.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = ""; };
27 | FF8CA3F5360CEAB49D74065F /* Pods-iosApp.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-iosApp.release.xcconfig"; path = "Target Support Files/Pods-iosApp/Pods-iosApp.release.xcconfig"; sourceTree = ""; };
28 | /* End PBXFileReference section */
29 |
30 | /* Begin PBXFrameworksBuildPhase section */
31 | F85CB1118929364A9C6EFABC /* Frameworks */ = {
32 | isa = PBXFrameworksBuildPhase;
33 | buildActionMask = 2147483647;
34 | files = (
35 | CFDB58B53BB94DE262B13C24 /* Pods_iosApp.framework in Frameworks */,
36 | );
37 | runOnlyForDeploymentPostprocessing = 0;
38 | };
39 | /* End PBXFrameworksBuildPhase section */
40 |
41 | /* Begin PBXGroup section */
42 | 058557D7273AAEEB004C7B11 /* Preview Content */ = {
43 | isa = PBXGroup;
44 | children = (
45 | 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */,
46 | );
47 | path = "Preview Content";
48 | sourceTree = "";
49 | };
50 | 42799AB246E5F90AF97AA0EF /* Frameworks */ = {
51 | isa = PBXGroup;
52 | children = (
53 | 6B1049432C0C2B312090ABF6 /* Pods_iosApp.framework */,
54 | );
55 | name = Frameworks;
56 | sourceTree = "";
57 | };
58 | 7555FF72242A565900829871 = {
59 | isa = PBXGroup;
60 | children = (
61 | AB1DB47929225F7C00F7AF9C /* Configuration */,
62 | 7555FF7D242A565900829871 /* iosApp */,
63 | 7555FF7C242A565900829871 /* Products */,
64 | FEFF387C0A8D172AA4D59CAE /* Pods */,
65 | 42799AB246E5F90AF97AA0EF /* Frameworks */,
66 | );
67 | sourceTree = "";
68 | };
69 | 7555FF7C242A565900829871 /* Products */ = {
70 | isa = PBXGroup;
71 | children = (
72 | 7555FF7B242A565900829871 /* MyApplication.app */,
73 | );
74 | name = Products;
75 | sourceTree = "";
76 | };
77 | 7555FF7D242A565900829871 /* iosApp */ = {
78 | isa = PBXGroup;
79 | children = (
80 | 058557BA273AAA24004C7B11 /* Assets.xcassets */,
81 | 7555FF82242A565900829871 /* ContentView.swift */,
82 | 7555FF8C242A565B00829871 /* Info.plist */,
83 | 2152FB032600AC8F00CF470E /* iOSApp.swift */,
84 | 058557D7273AAEEB004C7B11 /* Preview Content */,
85 | );
86 | path = iosApp;
87 | sourceTree = "";
88 | };
89 | AB1DB47929225F7C00F7AF9C /* Configuration */ = {
90 | isa = PBXGroup;
91 | children = (
92 | AB3632DC29227652001CCB65 /* Config.xcconfig */,
93 | );
94 | path = Configuration;
95 | sourceTree = "";
96 | };
97 | FEFF387C0A8D172AA4D59CAE /* Pods */ = {
98 | isa = PBXGroup;
99 | children = (
100 | 4FF3202A603A284706412EDC /* Pods-iosApp.debug.xcconfig */,
101 | FF8CA3F5360CEAB49D74065F /* Pods-iosApp.release.xcconfig */,
102 | );
103 | path = Pods;
104 | sourceTree = "";
105 | };
106 | /* End PBXGroup section */
107 |
108 | /* Begin PBXNativeTarget section */
109 | 7555FF7A242A565900829871 /* iosApp */ = {
110 | isa = PBXNativeTarget;
111 | buildConfigurationList = 7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "iosApp" */;
112 | buildPhases = (
113 | 98D614C51D2DA07C614CC46E /* [CP] Check Pods Manifest.lock */,
114 | 7555FF77242A565900829871 /* Sources */,
115 | 7555FF79242A565900829871 /* Resources */,
116 | F85CB1118929364A9C6EFABC /* Frameworks */,
117 | 2134C13603D0B299603D9F49 /* [CP] Copy Pods Resources */,
118 | );
119 | buildRules = (
120 | );
121 | dependencies = (
122 | );
123 | name = iosApp;
124 | productName = iosApp;
125 | productReference = 7555FF7B242A565900829871 /* MyApplication.app */;
126 | productType = "com.apple.product-type.application";
127 | };
128 | /* End PBXNativeTarget section */
129 |
130 | /* Begin PBXProject section */
131 | 7555FF73242A565900829871 /* Project object */ = {
132 | isa = PBXProject;
133 | attributes = {
134 | LastSwiftUpdateCheck = 1130;
135 | LastUpgradeCheck = 1130;
136 | ORGANIZATIONNAME = orgName;
137 | TargetAttributes = {
138 | 7555FF7A242A565900829871 = {
139 | CreatedOnToolsVersion = 11.3.1;
140 | };
141 | };
142 | };
143 | buildConfigurationList = 7555FF76242A565900829871 /* Build configuration list for PBXProject "iosApp" */;
144 | compatibilityVersion = "Xcode 9.3";
145 | developmentRegion = en;
146 | hasScannedForEncodings = 0;
147 | knownRegions = (
148 | en,
149 | Base,
150 | );
151 | mainGroup = 7555FF72242A565900829871;
152 | productRefGroup = 7555FF7C242A565900829871 /* Products */;
153 | projectDirPath = "";
154 | projectRoot = "";
155 | targets = (
156 | 7555FF7A242A565900829871 /* iosApp */,
157 | );
158 | };
159 | /* End PBXProject section */
160 |
161 | /* Begin PBXResourcesBuildPhase section */
162 | 7555FF79242A565900829871 /* Resources */ = {
163 | isa = PBXResourcesBuildPhase;
164 | buildActionMask = 2147483647;
165 | files = (
166 | 058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */,
167 | 058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */,
168 | );
169 | runOnlyForDeploymentPostprocessing = 0;
170 | };
171 | /* End PBXResourcesBuildPhase section */
172 |
173 | /* Begin PBXShellScriptBuildPhase section */
174 | 2134C13603D0B299603D9F49 /* [CP] Copy Pods Resources */ = {
175 | isa = PBXShellScriptBuildPhase;
176 | buildActionMask = 2147483647;
177 | files = (
178 | );
179 | inputFileListPaths = (
180 | "${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-resources-${CONFIGURATION}-input-files.xcfilelist",
181 | );
182 | name = "[CP] Copy Pods Resources";
183 | outputFileListPaths = (
184 | "${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-resources-${CONFIGURATION}-output-files.xcfilelist",
185 | );
186 | runOnlyForDeploymentPostprocessing = 0;
187 | shellPath = /bin/sh;
188 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-resources.sh\"\n";
189 | showEnvVarsInLog = 0;
190 | };
191 | 98D614C51D2DA07C614CC46E /* [CP] Check Pods Manifest.lock */ = {
192 | isa = PBXShellScriptBuildPhase;
193 | buildActionMask = 2147483647;
194 | files = (
195 | );
196 | inputFileListPaths = (
197 | );
198 | inputPaths = (
199 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
200 | "${PODS_ROOT}/Manifest.lock",
201 | );
202 | name = "[CP] Check Pods Manifest.lock";
203 | outputFileListPaths = (
204 | );
205 | outputPaths = (
206 | "$(DERIVED_FILE_DIR)/Pods-iosApp-checkManifestLockResult.txt",
207 | );
208 | runOnlyForDeploymentPostprocessing = 0;
209 | shellPath = /bin/sh;
210 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
211 | showEnvVarsInLog = 0;
212 | };
213 | /* End PBXShellScriptBuildPhase section */
214 |
215 | /* Begin PBXSourcesBuildPhase section */
216 | 7555FF77242A565900829871 /* Sources */ = {
217 | isa = PBXSourcesBuildPhase;
218 | buildActionMask = 2147483647;
219 | files = (
220 | 2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */,
221 | 7555FF83242A565900829871 /* ContentView.swift in Sources */,
222 | );
223 | runOnlyForDeploymentPostprocessing = 0;
224 | };
225 | /* End PBXSourcesBuildPhase section */
226 |
227 | /* Begin XCBuildConfiguration section */
228 | 7555FFA3242A565B00829871 /* Debug */ = {
229 | isa = XCBuildConfiguration;
230 | baseConfigurationReference = AB3632DC29227652001CCB65 /* Config.xcconfig */;
231 | buildSettings = {
232 | ALWAYS_SEARCH_USER_PATHS = NO;
233 | CLANG_ANALYZER_NONNULL = YES;
234 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
235 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
236 | CLANG_CXX_LIBRARY = "libc++";
237 | CLANG_ENABLE_MODULES = YES;
238 | CLANG_ENABLE_OBJC_ARC = YES;
239 | CLANG_ENABLE_OBJC_WEAK = YES;
240 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
241 | CLANG_WARN_BOOL_CONVERSION = YES;
242 | CLANG_WARN_COMMA = YES;
243 | CLANG_WARN_CONSTANT_CONVERSION = YES;
244 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
245 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
246 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
247 | CLANG_WARN_EMPTY_BODY = YES;
248 | CLANG_WARN_ENUM_CONVERSION = YES;
249 | CLANG_WARN_INFINITE_RECURSION = YES;
250 | CLANG_WARN_INT_CONVERSION = YES;
251 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
252 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
253 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
254 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
255 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
256 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
257 | CLANG_WARN_STRICT_PROTOTYPES = YES;
258 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
259 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
260 | CLANG_WARN_UNREACHABLE_CODE = YES;
261 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
262 | COPY_PHASE_STRIP = NO;
263 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
264 | ENABLE_STRICT_OBJC_MSGSEND = YES;
265 | ENABLE_TESTABILITY = YES;
266 | GCC_C_LANGUAGE_STANDARD = gnu11;
267 | GCC_DYNAMIC_NO_PIC = NO;
268 | GCC_NO_COMMON_BLOCKS = YES;
269 | GCC_OPTIMIZATION_LEVEL = 0;
270 | GCC_PREPROCESSOR_DEFINITIONS = (
271 | "DEBUG=1",
272 | "$(inherited)",
273 | );
274 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
275 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
276 | GCC_WARN_UNDECLARED_SELECTOR = YES;
277 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
278 | GCC_WARN_UNUSED_FUNCTION = YES;
279 | GCC_WARN_UNUSED_VARIABLE = YES;
280 | IPHONEOS_DEPLOYMENT_TARGET = 14.1;
281 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
282 | MTL_FAST_MATH = YES;
283 | ONLY_ACTIVE_ARCH = YES;
284 | SDKROOT = iphoneos;
285 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
286 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
287 | };
288 | name = Debug;
289 | };
290 | 7555FFA4242A565B00829871 /* Release */ = {
291 | isa = XCBuildConfiguration;
292 | baseConfigurationReference = AB3632DC29227652001CCB65 /* Config.xcconfig */;
293 | buildSettings = {
294 | ALWAYS_SEARCH_USER_PATHS = NO;
295 | CLANG_ANALYZER_NONNULL = YES;
296 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
297 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
298 | CLANG_CXX_LIBRARY = "libc++";
299 | CLANG_ENABLE_MODULES = YES;
300 | CLANG_ENABLE_OBJC_ARC = YES;
301 | CLANG_ENABLE_OBJC_WEAK = YES;
302 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
303 | CLANG_WARN_BOOL_CONVERSION = YES;
304 | CLANG_WARN_COMMA = YES;
305 | CLANG_WARN_CONSTANT_CONVERSION = YES;
306 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
307 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
308 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
309 | CLANG_WARN_EMPTY_BODY = YES;
310 | CLANG_WARN_ENUM_CONVERSION = YES;
311 | CLANG_WARN_INFINITE_RECURSION = YES;
312 | CLANG_WARN_INT_CONVERSION = YES;
313 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
314 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
315 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
316 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
317 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
318 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
319 | CLANG_WARN_STRICT_PROTOTYPES = YES;
320 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
321 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
322 | CLANG_WARN_UNREACHABLE_CODE = YES;
323 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
324 | COPY_PHASE_STRIP = NO;
325 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
326 | ENABLE_NS_ASSERTIONS = NO;
327 | ENABLE_STRICT_OBJC_MSGSEND = YES;
328 | GCC_C_LANGUAGE_STANDARD = gnu11;
329 | GCC_NO_COMMON_BLOCKS = YES;
330 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
331 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
332 | GCC_WARN_UNDECLARED_SELECTOR = YES;
333 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
334 | GCC_WARN_UNUSED_FUNCTION = YES;
335 | GCC_WARN_UNUSED_VARIABLE = YES;
336 | IPHONEOS_DEPLOYMENT_TARGET = 14.1;
337 | MTL_ENABLE_DEBUG_INFO = NO;
338 | MTL_FAST_MATH = YES;
339 | SDKROOT = iphoneos;
340 | SWIFT_COMPILATION_MODE = wholemodule;
341 | SWIFT_OPTIMIZATION_LEVEL = "-O";
342 | VALIDATE_PRODUCT = YES;
343 | };
344 | name = Release;
345 | };
346 | 7555FFA6242A565B00829871 /* Debug */ = {
347 | isa = XCBuildConfiguration;
348 | baseConfigurationReference = 4FF3202A603A284706412EDC /* Pods-iosApp.debug.xcconfig */;
349 | buildSettings = {
350 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
351 | CODE_SIGN_IDENTITY = "Apple Development";
352 | CODE_SIGN_STYLE = Automatic;
353 | DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\"";
354 | DEVELOPMENT_TEAM = "${TEAM_ID}";
355 | ENABLE_PREVIEWS = YES;
356 | INFOPLIST_FILE = iosApp/Info.plist;
357 | IPHONEOS_DEPLOYMENT_TARGET = 14.1;
358 | LD_RUNPATH_SEARCH_PATHS = (
359 | "$(inherited)",
360 | "@executable_path/Frameworks",
361 | );
362 | PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}${TEAM_ID}";
363 | PRODUCT_NAME = "${APP_NAME}";
364 | PROVISIONING_PROFILE_SPECIFIER = "";
365 | SWIFT_VERSION = 5.0;
366 | TARGETED_DEVICE_FAMILY = "1,2";
367 | };
368 | name = Debug;
369 | };
370 | 7555FFA7242A565B00829871 /* Release */ = {
371 | isa = XCBuildConfiguration;
372 | baseConfigurationReference = FF8CA3F5360CEAB49D74065F /* Pods-iosApp.release.xcconfig */;
373 | buildSettings = {
374 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
375 | CODE_SIGN_IDENTITY = "Apple Development";
376 | CODE_SIGN_STYLE = Automatic;
377 | DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\"";
378 | DEVELOPMENT_TEAM = "${TEAM_ID}";
379 | ENABLE_PREVIEWS = YES;
380 | INFOPLIST_FILE = iosApp/Info.plist;
381 | IPHONEOS_DEPLOYMENT_TARGET = 14.1;
382 | LD_RUNPATH_SEARCH_PATHS = (
383 | "$(inherited)",
384 | "@executable_path/Frameworks",
385 | );
386 | PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}${TEAM_ID}";
387 | PRODUCT_NAME = "${APP_NAME}";
388 | PROVISIONING_PROFILE_SPECIFIER = "";
389 | SWIFT_VERSION = 5.0;
390 | TARGETED_DEVICE_FAMILY = "1,2";
391 | };
392 | name = Release;
393 | };
394 | /* End XCBuildConfiguration section */
395 |
396 | /* Begin XCConfigurationList section */
397 | 7555FF76242A565900829871 /* Build configuration list for PBXProject "iosApp" */ = {
398 | isa = XCConfigurationList;
399 | buildConfigurations = (
400 | 7555FFA3242A565B00829871 /* Debug */,
401 | 7555FFA4242A565B00829871 /* Release */,
402 | );
403 | defaultConfigurationIsVisible = 0;
404 | defaultConfigurationName = Release;
405 | };
406 | 7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "iosApp" */ = {
407 | isa = XCConfigurationList;
408 | buildConfigurations = (
409 | 7555FFA6242A565B00829871 /* Debug */,
410 | 7555FFA7242A565B00829871 /* Release */,
411 | );
412 | defaultConfigurationIsVisible = 0;
413 | defaultConfigurationName = Release;
414 | };
415 | /* End XCConfigurationList section */
416 | };
417 | rootObject = 7555FF73242A565900829871 /* Project object */;
418 | }
419 |
--------------------------------------------------------------------------------