├── app
├── .gitignore
└── src
│ ├── main
│ ├── ic_launcher-web.png
│ ├── res
│ │ ├── mipmap-hdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_round.png
│ │ │ ├── ic_launcher_foreground.png
│ │ │ └── ic_launcher_monochrome.png
│ │ ├── mipmap-mdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_round.png
│ │ │ ├── ic_launcher_foreground.png
│ │ │ └── ic_launcher_monochrome.png
│ │ ├── mipmap-xhdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_round.png
│ │ │ ├── ic_launcher_foreground.png
│ │ │ └── ic_launcher_monochrome.png
│ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_round.png
│ │ │ ├── ic_launcher_foreground.png
│ │ │ └── ic_launcher_monochrome.png
│ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_round.png
│ │ │ ├── ic_launcher_foreground.png
│ │ │ └── ic_launcher_monochrome.png
│ │ ├── values
│ │ │ └── ic_launcher_background.xml
│ │ └── mipmap-anydpi-v26
│ │ │ ├── ic_launcher.xml
│ │ │ └── ic_launcher_round.xml
│ └── java
│ │ └── com
│ │ └── chesire
│ │ └── nekome
│ │ ├── ui
│ │ └── UIState.kt
│ │ ├── injection
│ │ ├── LogoutExecutorModule.kt
│ │ ├── WorkerModule.kt
│ │ ├── AppModule.kt
│ │ ├── DatabaseModule.kt
│ │ └── SeriesModule.kt
│ │ ├── services
│ │ ├── WidgetDataWorker.kt
│ │ └── DataRefreshNotifier.kt
│ │ ├── binders
│ │ └── UserProviderBinder.kt
│ │ └── LogoutHandler.kt
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── chesire
│ │ └── nekome
│ │ ├── helpers
│ │ ├── IntExtensions.kt
│ │ ├── AuthProviderExtensions.kt
│ │ ├── creation
│ │ │ ├── UserDomain.kt
│ │ │ └── SearchDomain.kt
│ │ ├── UserDaoExtensions.kt
│ │ └── PreferencesExtensions.kt
│ │ ├── injection
│ │ ├── MockAuthModule.kt
│ │ ├── MockUserModule.kt
│ │ ├── MockLibraryModule.kt
│ │ ├── MockSearchModule.kt
│ │ └── MemoryDatabaseModule.kt
│ │ └── TestRunner.kt
│ └── test
│ └── java
│ └── com
│ └── chesire
│ └── nekome
│ └── LogoutHandlerTests.kt
├── core
├── compose
│ ├── .gitignore
│ ├── consumer-rules.pro
│ ├── src
│ │ └── main
│ │ │ └── java
│ │ │ └── com
│ │ │ └── chesire
│ │ │ └── nekome
│ │ │ └── core
│ │ │ └── compose
│ │ │ ├── theme
│ │ │ ├── Type.kt
│ │ │ └── Shape.kt
│ │ │ └── LazyListStateExtensions.kt
│ └── build.gradle.kts
├── preferences
│ ├── .gitignore
│ ├── consumer-rules.pro
│ ├── src
│ │ ├── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── res
│ │ │ │ └── values
│ │ │ │ │ └── keys.xml
│ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── chesire
│ │ │ │ └── nekome
│ │ │ │ └── core
│ │ │ │ └── preferences
│ │ │ │ ├── flags
│ │ │ │ ├── ImageQuality.kt
│ │ │ │ ├── SortOption.kt
│ │ │ │ ├── TitleLanguage.kt
│ │ │ │ └── HomeScreenOptions.kt
│ │ │ │ └── ext
│ │ │ │ └── SharedPreferenceExtensions.kt
│ │ └── test
│ │ │ └── java
│ │ │ └── com
│ │ │ └── chesire
│ │ │ └── nekome
│ │ │ └── core
│ │ │ └── preferences
│ │ │ ├── ApplicationPreferencesTests.kt
│ │ │ ├── SeriesPreferencesTests.kt
│ │ │ └── flags
│ │ │ ├── ImageQualityTests.kt
│ │ │ └── TitleLanguageTests.kt
│ └── build.gradle.kts
└── resources
│ ├── .gitignore
│ ├── consumer-rules.pro
│ ├── src
│ └── main
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ └── com
│ │ └── chesire
│ │ └── nekome
│ │ └── resources
│ │ └── Alias.kt
│ └── build.gradle.kts
├── testing
├── .gitignore
├── consumer-rules.pro
└── build.gradle.kts
├── features
├── login
│ ├── .gitignore
│ ├── src
│ │ ├── main
│ │ │ ├── java
│ │ │ │ └── com
│ │ │ │ │ └── chesire
│ │ │ │ │ └── nekome
│ │ │ │ │ └── app
│ │ │ │ │ └── login
│ │ │ │ │ ├── syncing
│ │ │ │ │ ├── ui
│ │ │ │ │ │ └── UIState.kt
│ │ │ │ │ └── core
│ │ │ │ │ │ ├── SyncSeriesUseCase.kt
│ │ │ │ │ │ └── RetrieveAvatarUseCase.kt
│ │ │ │ │ └── credentials
│ │ │ │ │ ├── core
│ │ │ │ │ ├── ClearCredentialsUseCase.kt
│ │ │ │ │ └── PopulateUserDetailsUseCase.kt
│ │ │ │ │ └── ui
│ │ │ │ │ ├── ViewAction.kt
│ │ │ │ │ └── UIState.kt
│ │ │ ├── AndroidManifest.xml
│ │ │ └── res
│ │ │ │ └── drawable
│ │ │ │ ├── ic_lock.xml
│ │ │ │ └── ic_account_circle.xml
│ │ └── test
│ │ │ └── java
│ │ │ └── com
│ │ │ └── chesire
│ │ │ └── nekome
│ │ │ └── app
│ │ │ └── login
│ │ │ ├── credentials
│ │ │ └── core
│ │ │ │ └── ClearCredentialsUseCaseTest.kt
│ │ │ └── syncing
│ │ │ └── core
│ │ │ └── SyncSeriesUseCaseTest.kt
│ └── consumer-rules.pro
├── search
│ ├── .gitignore
│ ├── src
│ │ ├── main
│ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── chesire
│ │ │ │ └── nekome
│ │ │ │ └── app
│ │ │ │ └── search
│ │ │ │ └── search
│ │ │ │ ├── core
│ │ │ │ ├── model
│ │ │ │ │ └── SearchGroup.kt
│ │ │ │ ├── RememberSearchGroupUseCase.kt
│ │ │ │ ├── RetrieveUserSeriesIdsUseCase.kt
│ │ │ │ └── SearchInitializeUseCase.kt
│ │ │ │ ├── ui
│ │ │ │ └── ViewAction.kt
│ │ │ │ └── data
│ │ │ │ └── SearchPreferences.kt
│ │ └── test
│ │ │ └── java
│ │ │ └── com
│ │ │ └── chesire
│ │ │ └── nekome
│ │ │ └── app
│ │ │ └── search
│ │ │ └── search
│ │ │ └── core
│ │ │ └── RememberSearchGroupUseCaseTest.kt
│ └── consumer-rules.pro
├── series
│ ├── .gitignore
│ ├── src
│ │ ├── main
│ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── chesire
│ │ │ │ └── nekome
│ │ │ │ └── app
│ │ │ │ └── series
│ │ │ │ ├── collection
│ │ │ │ ├── core
│ │ │ │ │ ├── CurrentSortUseCase.kt
│ │ │ │ │ ├── CollectSeriesUseCase.kt
│ │ │ │ │ ├── UpdateSortUseCase.kt
│ │ │ │ │ ├── UpdateFiltersUseCase.kt
│ │ │ │ │ ├── CurrentFiltersUseCase.kt
│ │ │ │ │ ├── ShouldRateSeriesUseCase.kt
│ │ │ │ │ ├── RefreshSeriesUseCase.kt
│ │ │ │ │ └── IncrementSeriesUseCase.kt
│ │ │ │ └── ui
│ │ │ │ │ └── ViewAction.kt
│ │ │ │ └── item
│ │ │ │ ├── core
│ │ │ │ ├── RetrieveItemUseCase.kt
│ │ │ │ ├── GetImageUseCase.kt
│ │ │ │ ├── DeleteItemUseCase.kt
│ │ │ │ ├── BuildTitleUseCase.kt
│ │ │ │ └── UpdateItemUseCase.kt
│ │ │ │ └── ui
│ │ │ │ └── ViewAction.kt
│ │ └── test
│ │ │ └── java
│ │ │ └── com
│ │ │ └── chesire
│ │ │ └── nekome
│ │ │ └── app
│ │ │ └── series
│ │ │ ├── collection
│ │ │ └── core
│ │ │ │ ├── UpdateSortUseCaseTest.kt
│ │ │ │ ├── CurrentSortUseCaseTest.kt
│ │ │ │ └── CollectSeriesUseCaseTest.kt
│ │ │ └── item
│ │ │ └── core
│ │ │ └── RetrieveItemUseCaseTest.kt
│ └── consumer-rules.pro
├── serieswidget
│ ├── .gitignore
│ ├── consumer-rules.pro
│ └── src
│ │ └── main
│ │ ├── java
│ │ └── com
│ │ │ └── chesire
│ │ │ └── nekome
│ │ │ └── feature
│ │ │ └── serieswidget
│ │ │ ├── ui
│ │ │ ├── ViewAction.kt
│ │ │ ├── UIState.kt
│ │ │ └── DomainMapper.kt
│ │ │ ├── GlanceDataReceiver.kt
│ │ │ ├── SeriesWidgetEntryPoint.kt
│ │ │ └── core
│ │ │ └── UpdateSeriesUseCase.kt
│ │ ├── res
│ │ └── xml
│ │ │ └── series_widget_info.xml
│ │ └── AndroidManifest.xml
└── settings
│ ├── .gitignore
│ ├── src
│ ├── main
│ │ └── java
│ │ │ └── com
│ │ │ └── chesire
│ │ │ └── nekome
│ │ │ └── app
│ │ │ └── settings
│ │ │ └── config
│ │ │ ├── core
│ │ │ ├── RetrieveUserUseCase.kt
│ │ │ ├── UpdateRateSeriesUseCase.kt
│ │ │ ├── UpdateThemeUseCase.kt
│ │ │ ├── UpdateImageQualityUseCase.kt
│ │ │ ├── UpdateTitleLanguageUseCase.kt
│ │ │ ├── UpdateDefaultHomeScreenUseCase.kt
│ │ │ └── UpdateDefaultSeriesStateUseCase.kt
│ │ │ └── LogoutExecutor.kt
│ └── test
│ │ └── java
│ │ └── com
│ │ └── chesire
│ │ └── nekome
│ │ └── app
│ │ └── settings
│ │ └── config
│ │ └── core
│ │ ├── UpdateRateSeriesUseCaseTest.kt
│ │ ├── UpdateThemeUseCaseTest.kt
│ │ ├── UpdateImageQualityUseCaseTest.kt
│ │ ├── UpdateTitleLanguageUseCaseTest.kt
│ │ ├── UpdateDefaultSeriesStateUseCaseTest.kt
│ │ ├── UpdateDefaultHomeScreenUseCaseTest.kt
│ │ └── RetrieveUserUseCaseTest.kt
│ └── consumer-rules.pro
├── libraries
├── core
│ ├── .gitignore
│ ├── src
│ │ ├── main
│ │ │ ├── res
│ │ │ │ ├── drawable-hdpi
│ │ │ │ │ └── blackcat.webp
│ │ │ │ ├── drawable-mdpi
│ │ │ │ │ └── blackcat.webp
│ │ │ │ ├── drawable-xhdpi
│ │ │ │ │ └── blackcat.webp
│ │ │ │ ├── drawable-xxhdpi
│ │ │ │ │ └── blackcat.webp
│ │ │ │ ├── drawable-xxxhdpi
│ │ │ │ │ └── blackcat.webp
│ │ │ │ ├── anim
│ │ │ │ │ ├── fragment_fade_enter.xml
│ │ │ │ │ └── fragment_fade_exit.xml
│ │ │ │ ├── values-v21
│ │ │ │ │ └── styles.xml
│ │ │ │ ├── values
│ │ │ │ │ ├── colors.xml
│ │ │ │ │ ├── dimens.xml
│ │ │ │ │ └── styles.xml
│ │ │ │ ├── values-night
│ │ │ │ │ └── colors.xml
│ │ │ │ └── drawable
│ │ │ │ │ ├── splash_screen.xml
│ │ │ │ │ └── ic_blackcat.xml
│ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── chesire
│ │ │ │ └── nekome
│ │ │ │ └── core
│ │ │ │ ├── flags
│ │ │ │ ├── Service.kt
│ │ │ │ ├── RatingSystem.kt
│ │ │ │ ├── SeriesStatus.kt
│ │ │ │ ├── Subtype.kt
│ │ │ │ └── SeriesType.kt
│ │ │ │ ├── models
│ │ │ │ └── ErrorDomain.kt
│ │ │ │ └── AuthCaster.kt
│ │ └── test
│ │ │ └── java
│ │ │ └── com
│ │ │ └── chesire
│ │ │ └── nekome
│ │ │ └── core
│ │ │ └── flags
│ │ │ └── SeriesTypeTests.kt
│ ├── consumer-rules.pro
│ └── build.gradle.kts
├── kitsu
│ ├── .gitignore
│ ├── activity
│ │ ├── consumer-rules.pro
│ │ ├── .gitignore
│ │ ├── src
│ │ │ └── main
│ │ │ │ ├── AndroidManifest.xml
│ │ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── chesire
│ │ │ │ └── nekome
│ │ │ │ └── kitsu
│ │ │ │ └── activity
│ │ │ │ ├── dto
│ │ │ │ ├── ChangedData.kt
│ │ │ │ └── RetrieveActivityDto.kt
│ │ │ │ ├── KitsuActivityService.kt
│ │ │ │ └── RetrieveActivityDtoMapper.kt
│ │ └── build.gradle.kts
│ ├── auth
│ │ ├── .gitignore
│ │ ├── consumer-rules.pro
│ │ ├── src
│ │ │ └── main
│ │ │ │ ├── AndroidManifest.xml
│ │ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── chesire
│ │ │ │ └── nekome
│ │ │ │ └── kitsu
│ │ │ │ └── auth
│ │ │ │ ├── dto
│ │ │ │ ├── AuthResponseDto.kt
│ │ │ │ ├── RefreshTokenRequestDto.kt
│ │ │ │ └── LoginRequestDto.kt
│ │ │ │ └── KitsuAuthService.kt
│ │ └── build.gradle.kts
│ ├── library
│ │ ├── consumer-rules.pro
│ │ ├── .gitignore
│ │ ├── src
│ │ │ └── main
│ │ │ │ ├── AndroidManifest.xml
│ │ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── chesire
│ │ │ │ └── nekome
│ │ │ │ └── kitsu
│ │ │ │ └── library
│ │ │ │ └── dto
│ │ │ │ ├── AddResponseDto.kt
│ │ │ │ └── RetrieveResponseDto.kt
│ │ └── build.gradle.kts
│ ├── search
│ │ ├── consumer-rules.pro
│ │ ├── .gitignore
│ │ ├── src
│ │ │ ├── main
│ │ │ │ ├── AndroidManifest.xml
│ │ │ │ └── java
│ │ │ │ │ └── com
│ │ │ │ │ └── chesire
│ │ │ │ │ └── nekome
│ │ │ │ │ └── kitsu
│ │ │ │ │ └── search
│ │ │ │ │ ├── dto
│ │ │ │ │ ├── SearchResponseDto.kt
│ │ │ │ │ └── SearchItemDto.kt
│ │ │ │ │ └── SearchItemDtoMapper.kt
│ │ │ └── test
│ │ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── chesire
│ │ │ │ └── nekome
│ │ │ │ └── kitsu
│ │ │ │ └── search
│ │ │ │ └── SearchCreation.kt
│ │ └── build.gradle.kts
│ ├── trending
│ │ ├── consumer-rules.pro
│ │ ├── .gitignore
│ │ ├── src
│ │ │ ├── main
│ │ │ │ ├── AndroidManifest.xml
│ │ │ │ └── java
│ │ │ │ │ └── com
│ │ │ │ │ └── chesire
│ │ │ │ │ └── nekome
│ │ │ │ │ └── kitsu
│ │ │ │ │ └── trending
│ │ │ │ │ ├── dto
│ │ │ │ │ ├── TrendingResponseDto.kt
│ │ │ │ │ └── TrendingItemDto.kt
│ │ │ │ │ ├── KitsuTrendingService.kt
│ │ │ │ │ └── TrendingItemDtoMapper.kt
│ │ │ └── test
│ │ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── chesire
│ │ │ │ └── nekome
│ │ │ │ └── kitsu
│ │ │ │ └── trending
│ │ │ │ └── TrendingCreation.kt
│ │ └── build.gradle.kts
│ ├── user
│ │ ├── .gitignore
│ │ ├── consumer-rules.pro
│ │ ├── src
│ │ │ └── main
│ │ │ │ ├── AndroidManifest.xml
│ │ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── chesire
│ │ │ │ └── nekome
│ │ │ │ └── kitsu
│ │ │ │ └── user
│ │ │ │ ├── dto
│ │ │ │ ├── UserResponseDto.kt
│ │ │ │ └── UserItemDto.kt
│ │ │ │ ├── KitsuUserService.kt
│ │ │ │ └── UserItemDtoMapper.kt
│ │ └── build.gradle.kts
│ ├── src
│ │ └── main
│ │ │ └── java
│ │ │ └── com
│ │ │ └── chesire
│ │ │ └── nekome
│ │ │ └── kitsu
│ │ │ ├── KitsuConstants.kt
│ │ │ ├── api
│ │ │ └── intermediaries
│ │ │ │ └── Links.kt
│ │ │ ├── ResponseParsing.kt
│ │ │ └── adapters
│ │ │ └── SeriesTypeAdapter.kt
│ ├── consumer-rules.pro
│ └── build.gradle.kts
├── database
│ ├── .gitignore
│ ├── src
│ │ ├── main
│ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── chesire
│ │ │ │ └── nekome
│ │ │ │ └── database
│ │ │ │ ├── entity
│ │ │ │ ├── UserEntity.kt
│ │ │ │ └── SeriesEntity.kt
│ │ │ │ └── converters
│ │ │ │ ├── SubtypeConverter.kt
│ │ │ │ ├── ServiceConverter.kt
│ │ │ │ ├── SeriesTypeConverter.kt
│ │ │ │ ├── SeriesStatusConverter.kt
│ │ │ │ ├── UserSeriesStatusConverter.kt
│ │ │ │ ├── ImageModelConverter.kt
│ │ │ │ └── MapConverter.kt
│ │ └── test
│ │ │ └── java
│ │ │ └── com
│ │ │ └── chesire
│ │ │ └── nekome
│ │ │ └── database
│ │ │ └── converters
│ │ │ ├── ServiceConverterTests.kt
│ │ │ ├── SubtypeConverterTests.kt
│ │ │ ├── SeriesTypeConverterTests.kt
│ │ │ ├── SeriesStatusConverterTests.kt
│ │ │ ├── UserSeriesStatusConverterTests.kt
│ │ │ ├── ImageModelConverterTests.kt
│ │ │ └── MapConverterTests.kt
│ └── consumer-rules.pro
└── datasource
│ ├── activity
│ ├── consumer-rules.pro
│ ├── .gitignore
│ ├── src
│ │ └── main
│ │ │ └── java
│ │ │ └── com
│ │ │ └── chesire
│ │ │ └── nekome
│ │ │ └── datasource
│ │ │ └── activity
│ │ │ ├── ActivityDomain.kt
│ │ │ ├── Event.kt
│ │ │ ├── remote
│ │ │ └── ActivityApi.kt
│ │ │ └── local
│ │ │ └── ActivityLocalDataStorage.kt
│ └── build.gradle.kts
│ ├── auth
│ ├── .gitignore
│ ├── consumer-rules.pro
│ ├── src
│ │ └── main
│ │ │ ├── AndroidManifest.xml
│ │ │ └── java
│ │ │ └── com
│ │ │ └── chesire
│ │ │ └── nekome
│ │ │ └── datasource
│ │ │ └── auth
│ │ │ ├── AuthException.kt
│ │ │ ├── remote
│ │ │ ├── AuthFailure.kt
│ │ │ ├── AuthApi.kt
│ │ │ └── AuthInjectionInterceptor.kt
│ │ │ └── local
│ │ │ └── AuthProvider.kt
│ └── build.gradle.kts
│ ├── search
│ ├── consumer-rules.pro
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ │ └── main
│ │ └── java
│ │ └── com
│ │ └── chesire
│ │ └── nekome
│ │ └── datasource
│ │ └── search
│ │ ├── SearchDomain.kt
│ │ └── remote
│ │ └── SearchApi.kt
│ ├── series
│ ├── consumer-rules.pro
│ ├── .gitignore
│ ├── src
│ │ └── main
│ │ │ └── java
│ │ │ └── com
│ │ │ └── chesire
│ │ │ └── nekome
│ │ │ └── datasource
│ │ │ └── series
│ │ │ ├── UserProvider.kt
│ │ │ └── SeriesDomain.kt
│ └── build.gradle.kts
│ ├── trending
│ ├── consumer-rules.pro
│ ├── .gitignore
│ ├── src
│ │ └── main
│ │ │ └── java
│ │ │ └── com
│ │ │ └── chesire
│ │ │ └── nekome
│ │ │ └── datasource
│ │ │ └── trending
│ │ │ ├── TrendingDomain.kt
│ │ │ └── remote
│ │ │ └── TrendingApi.kt
│ └── build.gradle.kts
│ └── user
│ ├── .gitignore
│ ├── consumer-rules.pro
│ ├── src
│ └── main
│ │ └── java
│ │ └── com
│ │ └── chesire
│ │ └── nekome
│ │ └── datasource
│ │ └── user
│ │ ├── UserDomain.kt
│ │ ├── User.kt
│ │ ├── remote
│ │ └── UserApi.kt
│ │ └── UserMapper.kt
│ └── build.gradle.kts
├── fastlane
├── metadata
│ └── android
│ │ ├── en-GB
│ │ ├── video.txt
│ │ ├── title.txt
│ │ ├── changelogs
│ │ │ ├── 24081508.txt
│ │ │ ├── 151.txt
│ │ │ ├── 23071421.txt
│ │ │ ├── 24010720.txt
│ │ │ ├── 23042320.txt
│ │ │ ├── 23101020.txt
│ │ │ ├── 23052116.txt
│ │ │ ├── 23062419.txt
│ │ │ ├── 21091122.txt
│ │ │ ├── 23050122.txt
│ │ │ ├── 21091120.txt
│ │ │ ├── 23100818.txt
│ │ │ ├── 24012317.txt
│ │ │ ├── 24010710.txt
│ │ │ ├── 23062323.txt
│ │ │ └── 201.txt
│ │ ├── short_description.txt
│ │ ├── images
│ │ │ ├── icon.png
│ │ │ ├── featureGraphic.png
│ │ │ └── phoneScreenshots
│ │ │ │ ├── 1_en-GB.png
│ │ │ │ ├── 2_en-GB.png
│ │ │ │ ├── 3_en-GB.png
│ │ │ │ ├── 4_en-GB.png
│ │ │ │ ├── 5_en-GB.png
│ │ │ │ └── 6_en-GB.png
│ │ └── full_description.txt
│ │ └── en-US
│ │ ├── video.txt
│ │ ├── title.txt
│ │ ├── changelogs
│ │ ├── 24081508.txt
│ │ ├── 151.txt
│ │ ├── 23071421.txt
│ │ ├── 24010720.txt
│ │ ├── 23042320.txt
│ │ ├── 23101020.txt
│ │ ├── 23052116.txt
│ │ ├── 23062419.txt
│ │ ├── 21091122.txt
│ │ ├── 23050122.txt
│ │ ├── 21091120.txt
│ │ ├── 23100818.txt
│ │ ├── 24012317.txt
│ │ ├── 24010710.txt
│ │ ├── 23062323.txt
│ │ └── 201.txt
│ │ ├── short_description.txt
│ │ ├── images
│ │ ├── icon.png
│ │ ├── featureGraphic.png
│ │ └── phoneScreenshots
│ │ │ ├── 1_en-US.png
│ │ │ ├── 2_en-US.png
│ │ │ ├── 3_en-US.png
│ │ │ ├── 4_en-US.png
│ │ │ ├── 5_en-US.png
│ │ │ └── 6_en-US.png
│ │ └── full_description.txt
├── Appfile
├── Fastfile
└── README.md
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── Gemfile
├── .idea
├── codeStyles
│ └── codeStyleConfig.xml
└── kotlinc.xml
├── codecov.yml
├── .github
├── renovate.json
└── workflows
│ └── master.yml
├── privacypolicy.md
├── gradle.properties
├── Dangerfile
└── settings.gradle.kts
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/core/compose/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/core/compose/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/core/preferences/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/core/resources/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/core/resources/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/testing/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/core/preferences/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/features/login/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/features/search/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/features/series/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/features/serieswidget/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/libraries/core/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/libraries/kitsu/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-GB/video.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/video.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/features/serieswidget/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/features/settings/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/libraries/database/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/libraries/kitsu/activity/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/libraries/kitsu/auth/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/libraries/kitsu/auth/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/libraries/kitsu/library/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/libraries/kitsu/search/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/libraries/kitsu/trending/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/libraries/kitsu/user/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/libraries/kitsu/user/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-GB/title.txt:
--------------------------------------------------------------------------------
1 | Nekome
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/title.txt:
--------------------------------------------------------------------------------
1 | Nekome
--------------------------------------------------------------------------------
/libraries/datasource/activity/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/libraries/datasource/auth/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/libraries/datasource/auth/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/libraries/datasource/search/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/libraries/datasource/series/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/libraries/datasource/trending/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/libraries/datasource/user/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/libraries/datasource/user/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/libraries/kitsu/activity/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/libraries/kitsu/library/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/libraries/kitsu/search/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/libraries/kitsu/trending/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/libraries/datasource/activity/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/libraries/datasource/search/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/libraries/datasource/series/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/libraries/datasource/trending/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-GB/changelogs/24081508.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/24081508.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-GB/changelogs/151.txt:
--------------------------------------------------------------------------------
1 | Initial release
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/151.txt:
--------------------------------------------------------------------------------
1 | Initial release
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-GB/changelogs/23071421.txt:
--------------------------------------------------------------------------------
1 | * Update the series detail view
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-GB/changelogs/24010720.txt:
--------------------------------------------------------------------------------
1 | * Use updated logo for the Nekome
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/23071421.txt:
--------------------------------------------------------------------------------
1 | * Update the series detail view
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/24010720.txt:
--------------------------------------------------------------------------------
1 | * Use updated logo for the Nekome
2 |
--------------------------------------------------------------------------------
/core/resources/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-GB/changelogs/23042320.txt:
--------------------------------------------------------------------------------
1 | * Big rewrite to change how the UI looks.
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-GB/changelogs/23101020.txt:
--------------------------------------------------------------------------------
1 | * Fix issue with R8 causing api failures
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/23042320.txt:
--------------------------------------------------------------------------------
1 | Big rewrite to change how the UI looks.
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/23101020.txt:
--------------------------------------------------------------------------------
1 | * Fix issue with R8 causing api failures
2 |
--------------------------------------------------------------------------------
/core/preferences/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-GB/changelogs/23052116.txt:
--------------------------------------------------------------------------------
1 | * Fix issue with api calls to Kitsu not working
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-GB/changelogs/23062419.txt:
--------------------------------------------------------------------------------
1 | * Add option to set the title display language
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/23052116.txt:
--------------------------------------------------------------------------------
1 | * Fix issue with api calls to Kitsu not working
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/23062419.txt:
--------------------------------------------------------------------------------
1 | * Add option to set the title display language
2 |
--------------------------------------------------------------------------------
/app/src/main/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chesire/Nekome/HEAD/app/src/main/ic_launcher-web.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-GB/changelogs/21091122.txt:
--------------------------------------------------------------------------------
1 | * Fix issue with background services not starting up correctly
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-GB/changelogs/23050122.txt:
--------------------------------------------------------------------------------
1 | * Fix potential crash when loading the settings screen.
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/21091122.txt:
--------------------------------------------------------------------------------
1 | * Fix issue with background services not starting up correctly
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/23050122.txt:
--------------------------------------------------------------------------------
1 | * Fix potential crash when loading the settings screen.
2 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chesire/Nekome/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-GB/changelogs/21091120.txt:
--------------------------------------------------------------------------------
1 | * Fix crash that would sometimes occur due to invalid persisted data
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-GB/short_description.txt:
--------------------------------------------------------------------------------
1 | Keep track of your anime and manga, through the use of the Kitsu API.
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/21091120.txt:
--------------------------------------------------------------------------------
1 | * Fix crash that would sometimes occur due to invalid persisted data
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/short_description.txt:
--------------------------------------------------------------------------------
1 | Keep track of your anime and manga, through the use of the Kitsu API.
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chesire/Nekome/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chesire/Nekome/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chesire/Nekome/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chesire/Nekome/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-GB/changelogs/23100818.txt:
--------------------------------------------------------------------------------
1 | * Add a widget that allows user to update their anime series
2 | * UI improvements
3 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-GB/changelogs/24012317.txt:
--------------------------------------------------------------------------------
1 | * Fix crash when navigating to series detail view, if series has a / in the name
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/23100818.txt:
--------------------------------------------------------------------------------
1 | * Add a widget that allows user to update their anime series
2 | * UI improvements
3 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/24012317.txt:
--------------------------------------------------------------------------------
1 | * Fix crash when navigating to series detail view, if series has a / in the name
2 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chesire/Nekome/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-GB/changelogs/24010710.txt:
--------------------------------------------------------------------------------
1 | * Add monochrome icon
2 | * Move search from bottom navigation to the series list as a FAB
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-GB/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chesire/Nekome/HEAD/fastlane/metadata/android/en-GB/images/icon.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/24010710.txt:
--------------------------------------------------------------------------------
1 | * Add monochrome icon
2 | * Move search from bottom navigation to the series list as a FAB
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chesire/Nekome/HEAD/fastlane/metadata/android/en-US/images/icon.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chesire/Nekome/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chesire/Nekome/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chesire/Nekome/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chesire/Nekome/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chesire/Nekome/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chesire/Nekome/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chesire/Nekome/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chesire/Nekome/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chesire/Nekome/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chesire/Nekome/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chesire/Nekome/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png
--------------------------------------------------------------------------------
/libraries/core/src/main/res/drawable-hdpi/blackcat.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chesire/Nekome/HEAD/libraries/core/src/main/res/drawable-hdpi/blackcat.webp
--------------------------------------------------------------------------------
/libraries/core/src/main/res/drawable-mdpi/blackcat.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chesire/Nekome/HEAD/libraries/core/src/main/res/drawable-mdpi/blackcat.webp
--------------------------------------------------------------------------------
/libraries/core/src/main/res/drawable-xhdpi/blackcat.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chesire/Nekome/HEAD/libraries/core/src/main/res/drawable-xhdpi/blackcat.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chesire/Nekome/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chesire/Nekome/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chesire/Nekome/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chesire/Nekome/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-GB/images/featureGraphic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chesire/Nekome/HEAD/fastlane/metadata/android/en-GB/images/featureGraphic.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/featureGraphic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chesire/Nekome/HEAD/fastlane/metadata/android/en-US/images/featureGraphic.png
--------------------------------------------------------------------------------
/libraries/core/src/main/res/drawable-xxhdpi/blackcat.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chesire/Nekome/HEAD/libraries/core/src/main/res/drawable-xxhdpi/blackcat.webp
--------------------------------------------------------------------------------
/libraries/core/src/main/res/drawable-xxxhdpi/blackcat.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chesire/Nekome/HEAD/libraries/core/src/main/res/drawable-xxxhdpi/blackcat.webp
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-GB/changelogs/23062323.txt:
--------------------------------------------------------------------------------
1 | * Add option to change displayed image quality
2 | * Fix potential crash occurring while app is in background
3 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/23062323.txt:
--------------------------------------------------------------------------------
1 | * Add option to change displayed image quality
2 | * Fix potential crash occurring while app is in background
3 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 |
3 | gem "fastlane", "> 0"
4 | gem "danger", "> 0"
5 | gem "danger-android_lint", "> 0"
6 | gem "danger-checkstyle_format", "> 0"
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #74909E
4 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-GB/images/phoneScreenshots/1_en-GB.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chesire/Nekome/HEAD/fastlane/metadata/android/en-GB/images/phoneScreenshots/1_en-GB.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-GB/images/phoneScreenshots/2_en-GB.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chesire/Nekome/HEAD/fastlane/metadata/android/en-GB/images/phoneScreenshots/2_en-GB.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-GB/images/phoneScreenshots/3_en-GB.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chesire/Nekome/HEAD/fastlane/metadata/android/en-GB/images/phoneScreenshots/3_en-GB.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-GB/images/phoneScreenshots/4_en-GB.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chesire/Nekome/HEAD/fastlane/metadata/android/en-GB/images/phoneScreenshots/4_en-GB.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-GB/images/phoneScreenshots/5_en-GB.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chesire/Nekome/HEAD/fastlane/metadata/android/en-GB/images/phoneScreenshots/5_en-GB.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-GB/images/phoneScreenshots/6_en-GB.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chesire/Nekome/HEAD/fastlane/metadata/android/en-GB/images/phoneScreenshots/6_en-GB.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/1_en-US.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chesire/Nekome/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/1_en-US.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/2_en-US.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chesire/Nekome/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/2_en-US.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/3_en-US.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chesire/Nekome/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/3_en-US.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/4_en-US.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chesire/Nekome/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/4_en-US.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/5_en-US.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chesire/Nekome/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/5_en-US.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/6_en-US.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chesire/Nekome/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/6_en-US.png
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/libraries/kitsu/auth/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/libraries/kitsu/library/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/libraries/kitsu/search/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/libraries/kitsu/src/main/java/com/chesire/nekome/kitsu/KitsuConstants.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.kitsu
2 |
3 | /**
4 | * Base URL for all Kitsu requests.
5 | */
6 | const val KITSU_URL = "https://kitsu.app/"
7 |
--------------------------------------------------------------------------------
/libraries/kitsu/user/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/features/search/src/main/java/com/chesire/nekome/app/search/search/core/model/SearchGroup.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.app.search.search.core.model
2 |
3 | enum class SearchGroup {
4 | Anime,
5 | Manga
6 | }
7 |
--------------------------------------------------------------------------------
/libraries/kitsu/activity/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/libraries/kitsu/trending/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/fastlane/Appfile:
--------------------------------------------------------------------------------
1 | json_key_file("fastlane/googleservice.json") # Path to the json secret file - Follow https://docs.fastlane.tools/actions/supply/#setup to get one
2 | package_name("com.chesire.nekome") # e.g. com.krausefx.app
3 |
--------------------------------------------------------------------------------
/features/login/src/main/java/com/chesire/nekome/app/login/syncing/ui/UIState.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.app.login.syncing.ui
2 |
3 | data class UIState(
4 | val avatar: String,
5 | val finishedSyncing: Boolean?
6 | )
7 |
--------------------------------------------------------------------------------
/libraries/datasource/auth/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/core/resources/src/main/java/com/chesire/nekome/resources/Alias.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.resources
2 |
3 | typealias Resources = com.chesire.nekome.core.resources.R
4 | typealias StringResource = com.chesire.nekome.core.resources.R.string
5 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-GB/changelogs/201.txt:
--------------------------------------------------------------------------------
1 | * Add a link to the open source repo on GitHub from the settings.
2 | * Update the results screen to show a bit more information about each series.
3 | * Update the login and syncing screens UI.
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/201.txt:
--------------------------------------------------------------------------------
1 | * Add a link to the open source repo on GitHub from the settings.
2 | * Update the results screen to show a bit more information about each series.
3 | * Update the login and syncing screens UI.
--------------------------------------------------------------------------------
/libraries/core/src/main/java/com/chesire/nekome/core/flags/Service.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.core.flags
2 |
3 | /**
4 | * All possible supported services.
5 | */
6 | enum class Service {
7 | Kitsu,
8 | Unknown
9 | }
10 |
--------------------------------------------------------------------------------
/features/login/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/ViewAction.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.feature.serieswidget.ui
2 |
3 | sealed interface ViewAction {
4 |
5 | data class UpdateSeries(val id: Int) : ViewAction
6 | }
7 |
--------------------------------------------------------------------------------
/core/preferences/src/test/java/com/chesire/nekome/core/preferences/ApplicationPreferencesTests.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.core.preferences
2 |
3 | class ApplicationPreferencesTests {
4 | // TODO: Add unit tests for the application preferences
5 | }
6 |
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | ignore:
2 | - "app/src/main/java/com/chesire/nekome/App.kt"
3 | - "app/src/main/java/com/chesire/nekome/injection/*"
4 | - "app/src/main/java/com/chesire/nekome/injection/**/*"
5 | - "testing/src/main/java/**/*"
6 | - "**/*Fragment*"
7 | - "**/build/**"
--------------------------------------------------------------------------------
/core/compose/src/main/java/com/chesire/nekome/core/compose/theme/Type.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.core.compose.theme
2 |
3 | import androidx.compose.material3.Typography
4 |
5 | // Set of Material typography styles to start with
6 | internal val NekomeTypography = Typography()
7 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-all.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/libraries/core/src/main/java/com/chesire/nekome/core/flags/RatingSystem.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.core.flags
2 |
3 | /**
4 | * Wrapper around the possible configurations for the ratings.
5 | */
6 | enum class RatingSystem {
7 | Unknown,
8 | Advanced,
9 | Regular,
10 | Simple
11 | }
12 |
--------------------------------------------------------------------------------
/.idea/kotlinc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/libraries/core/src/main/java/com/chesire/nekome/core/flags/SeriesStatus.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.core.flags
2 |
3 | /**
4 | * All possible states of a series.
5 | */
6 | enum class SeriesStatus {
7 | Unknown,
8 | Current,
9 | Finished,
10 | TBA,
11 | Unreleased,
12 | Upcoming
13 | }
14 |
--------------------------------------------------------------------------------
/libraries/core/src/main/res/anim/fragment_fade_enter.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/libraries/core/src/main/res/anim/fragment_fade_exit.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/libraries/core/src/main/res/values-v21/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
--------------------------------------------------------------------------------
/libraries/core/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #607D8B
4 | #455A64
5 | #42A5F5
6 |
7 | #AAAAAA
8 |
9 |
--------------------------------------------------------------------------------
/libraries/core/src/main/res/values-night/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #333F48
4 | #252E34
5 | #2196F3
6 |
7 | #AAAAAA
8 |
9 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/chesire/nekome/helpers/IntExtensions.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.helpers
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 |
5 | /**
6 | * Gets a string based on what the value of [this] is.
7 | */
8 | fun Int.getResource() = InstrumentationRegistry.getInstrumentation().targetContext.getString(this)
9 |
--------------------------------------------------------------------------------
/libraries/datasource/activity/src/main/java/com/chesire/nekome/datasource/activity/ActivityDomain.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.datasource.activity
2 |
3 | /**
4 | * Provides a domain for a single piece of activity that has occurred.
5 | */
6 | data class ActivityDomain(
7 | val id: Int,
8 | val timestamp: String,
9 | val events: List
10 | )
11 |
--------------------------------------------------------------------------------
/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/UIState.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.feature.serieswidget.ui
2 |
3 | data class UIState(
4 | val series: List = emptyList()
5 | )
6 |
7 | data class Series(
8 | val userId: Int,
9 | val title: String,
10 | val progress: String,
11 | val isUpdating: Boolean
12 | )
13 |
--------------------------------------------------------------------------------
/libraries/core/src/main/res/drawable/splash_screen.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/libraries/datasource/auth/src/main/java/com/chesire/nekome/datasource/auth/AuthException.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.datasource.auth
2 |
3 | import java.io.IOException
4 |
5 | /**
6 | * Exception thrown when the auth token is unable to be refreshed.
7 | */
8 | class AuthException : IOException()
9 | // IOException must be extended or the Retrofit interceptors will crash
10 |
--------------------------------------------------------------------------------
/core/preferences/src/test/java/com/chesire/nekome/core/preferences/SeriesPreferencesTests.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.core.preferences
2 |
3 | class SeriesPreferencesTests {
4 |
5 | private val defaultFilter =
6 | """
7 | {"0":true,"1":false,"2":false,"3":false,"4":false}
8 | """.trimIndent()
9 |
10 | // TODO: Add unit tests for the series preferences
11 | }
12 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.github/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "config:base"
4 | ],
5 | "assignees": [
6 | "Chesire"
7 | ],
8 | "branchPrefix": "deps/",
9 | "semanticCommits": "enabled",
10 | "semanticCommitType": "chore",
11 | "semanticCommitScope": "deps",
12 | "labels": [
13 | "dependencies"
14 | ],
15 | "bundler": {
16 | "rangeStrategy": "update-lockfile"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/core/resources/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.library)
3 | alias(libs.plugins.kotlin.android)
4 | }
5 |
6 | android {
7 | namespace = "com.chesire.nekome.core.resources"
8 | compileSdk = libs.versions.sdk.get().toInt()
9 |
10 | defaultConfig {
11 | minSdk = 21
12 |
13 | consumerProguardFiles("consumer-rules.pro")
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/features/login/src/main/java/com/chesire/nekome/app/login/credentials/core/ClearCredentialsUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.app.login.credentials.core
2 |
3 | import com.chesire.nekome.datasource.auth.AccessTokenRepository
4 | import javax.inject.Inject
5 |
6 | class ClearCredentialsUseCase @Inject constructor(private val auth: AccessTokenRepository) {
7 |
8 | operator fun invoke() = auth.clear()
9 | }
10 |
--------------------------------------------------------------------------------
/libraries/kitsu/user/src/main/java/com/chesire/nekome/kitsu/user/dto/UserResponseDto.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.kitsu.user.dto
2 |
3 | import com.squareup.moshi.Json
4 | import com.squareup.moshi.JsonClass
5 |
6 | /**
7 | * DTO for responses from the Kitsu user endpoint.
8 | */
9 | @JsonClass(generateAdapter = true)
10 | data class UserResponseDto(
11 | @Json(name = "data")
12 | val data: List
13 | )
14 |
--------------------------------------------------------------------------------
/privacypolicy.md:
--------------------------------------------------------------------------------
1 | # Privacy Policy
2 |
3 | Nekome takes your privacy seriously. To better protect your privacy we provide this privacy policy notice explaining the way your personal information is collected and used.
4 |
5 | ## Collection of Information
6 |
7 | Nekome collects the email address and password of the user for their Kitsu account to facilitate logging in and retrieve/updating their lists.
8 | No other data is obtained or stored.
9 |
--------------------------------------------------------------------------------
/libraries/kitsu/search/src/main/java/com/chesire/nekome/kitsu/search/dto/SearchResponseDto.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.kitsu.search.dto
2 |
3 | import com.squareup.moshi.Json
4 | import com.squareup.moshi.JsonClass
5 |
6 | /**
7 | * DTO for responses from the Kitsu search endpoint.
8 | */
9 | @JsonClass(generateAdapter = true)
10 | data class SearchResponseDto(
11 | @Json(name = "data")
12 | val data: List
13 | )
14 |
--------------------------------------------------------------------------------
/core/compose/src/main/java/com/chesire/nekome/core/compose/theme/Shape.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.core.compose.theme
2 |
3 | import androidx.compose.foundation.shape.RoundedCornerShape
4 | import androidx.compose.material3.Shapes
5 | import androidx.compose.ui.unit.dp
6 |
7 | internal val NekomeShapes = Shapes(
8 | small = RoundedCornerShape(4.dp),
9 | medium = RoundedCornerShape(4.dp),
10 | large = RoundedCornerShape(0.dp)
11 | )
12 |
--------------------------------------------------------------------------------
/libraries/kitsu/trending/src/main/java/com/chesire/nekome/kitsu/trending/dto/TrendingResponseDto.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.kitsu.trending.dto
2 |
3 | import com.squareup.moshi.Json
4 | import com.squareup.moshi.JsonClass
5 |
6 | /**
7 | * DTO for responses from the Kitsu trending endpoint.
8 | */
9 | @JsonClass(generateAdapter = true)
10 | data class TrendingResponseDto(
11 | @Json(name = "data")
12 | val data: List
13 | )
14 |
--------------------------------------------------------------------------------
/core/preferences/src/main/res/values/keys.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | keyDefaultSeriesState
4 | keyDefaultHomeScreen
5 | keyTheme
6 | keyRateOnCompletion
7 |
8 |
--------------------------------------------------------------------------------
/features/login/src/main/java/com/chesire/nekome/app/login/credentials/ui/ViewAction.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.app.login.credentials.ui
2 |
3 | sealed interface ViewAction {
4 | data class UsernameChanged(val newUsername: String) : ViewAction
5 | data class PasswordChanged(val newPassword: String) : ViewAction
6 | object LoginPressed : ViewAction
7 | object ErrorSnackbarObserved : ViewAction
8 | object NavigationObserved : ViewAction
9 | }
10 |
--------------------------------------------------------------------------------
/libraries/core/src/main/java/com/chesire/nekome/core/flags/Subtype.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.core.flags
2 |
3 | /**
4 | * List of all possible series sub types.
5 | */
6 | enum class Subtype {
7 | Unknown,
8 |
9 | // Anime
10 | ONA,
11 | OVA,
12 | TV,
13 | Movie,
14 | Music,
15 | Special,
16 |
17 | // Manga
18 | Doujin,
19 | Manga,
20 | Manhua,
21 | Manhwa,
22 | Novel,
23 | OEL,
24 | Oneshot
25 | }
26 |
--------------------------------------------------------------------------------
/libraries/datasource/trending/src/main/java/com/chesire/nekome/datasource/trending/TrendingDomain.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.datasource.trending
2 |
3 | import com.chesire.nekome.core.flags.SeriesType
4 | import com.chesire.nekome.core.models.ImageModel
5 |
6 | /**
7 | * Domain class for a trending item.
8 | */
9 | data class TrendingDomain(
10 | val id: Int,
11 | val type: SeriesType,
12 | val canonicalTitle: String,
13 | val posterImage: ImageModel
14 | )
15 |
--------------------------------------------------------------------------------
/libraries/datasource/auth/src/main/java/com/chesire/nekome/datasource/auth/remote/AuthFailure.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.datasource.auth.remote
2 |
3 | data class AuthDomain(
4 | val accessToken: String,
5 | val refreshToken: String
6 | )
7 |
8 | sealed class AuthFailure {
9 | object CouldNotReachServer : AuthFailure()
10 | object InvalidCredentials : AuthFailure()
11 | object CouldNotRefresh : AuthFailure()
12 | object BadRequest : AuthFailure()
13 | }
14 |
--------------------------------------------------------------------------------
/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/GlanceDataReceiver.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.feature.serieswidget
2 |
3 | import androidx.glance.appwidget.GlanceAppWidget
4 | import androidx.glance.appwidget.GlanceAppWidgetReceiver
5 | import com.chesire.nekome.feature.serieswidget.ui.SeriesWidget
6 |
7 | class GlanceDataReceiver : GlanceAppWidgetReceiver() {
8 | override val glanceAppWidget: GlanceAppWidget
9 | get() = SeriesWidget()
10 | }
11 |
--------------------------------------------------------------------------------
/libraries/kitsu/activity/src/main/java/com/chesire/nekome/kitsu/activity/dto/ChangedData.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.kitsu.activity.dto
2 |
3 | /**
4 | * Container for the [ChangedData] events from the API.
5 | */
6 | data class ChangedDataContainer(
7 | val changedData: List
8 | )
9 |
10 | /**
11 | * Data for an event of "activity" for a user.
12 | */
13 | data class ChangedData(
14 | val type: String,
15 | val from: String,
16 | val to: String
17 | )
18 |
--------------------------------------------------------------------------------
/libraries/datasource/user/src/main/java/com/chesire/nekome/datasource/user/UserDomain.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.datasource.user
2 |
3 | import com.chesire.nekome.core.flags.Service
4 | import com.chesire.nekome.core.models.ImageModel
5 |
6 | /**
7 | * Domain class for user related information.
8 | */
9 | data class UserDomain(
10 | val userId: Int,
11 | val name: String,
12 | val avatar: ImageModel,
13 | val coverImage: ImageModel,
14 | val service: Service
15 | )
16 |
--------------------------------------------------------------------------------
/libraries/core/src/main/java/com/chesire/nekome/core/flags/SeriesType.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.core.flags
2 |
3 | /**
4 | * All possible types of a series.
5 | */
6 | enum class SeriesType(val id: Int) {
7 | Unknown(-1),
8 | Anime(0),
9 | Manga(1);
10 |
11 | companion object {
12 | /**
13 | * Gets the series typed based on its id.
14 | */
15 | fun forId(typeId: Int): SeriesType = values().find { it.id == typeId } ?: Unknown
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/libraries/datasource/activity/src/main/java/com/chesire/nekome/datasource/activity/Event.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.datasource.activity
2 |
3 | /**
4 | * Represents a single activity event.
5 | * [from] represents the value before the activity event.
6 | * [to] represents the value after the activity event.
7 | * [eventType] represents the type of event that has occurred.
8 | */
9 | data class Event(
10 | val from: String,
11 | val to: String,
12 | val eventType: String
13 | )
14 |
--------------------------------------------------------------------------------
/libraries/datasource/user/src/main/java/com/chesire/nekome/datasource/user/User.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.datasource.user
2 |
3 | /**
4 | * Possible results for accessing the user object from the [UserRepository].
5 | */
6 | sealed class User {
7 |
8 | /**
9 | * User has been found with a valid model.
10 | */
11 | data class Found(val domain: UserDomain) : User()
12 |
13 | /**
14 | * No user has been found.
15 | */
16 | object NotFound : User()
17 | }
18 |
--------------------------------------------------------------------------------
/libraries/kitsu/library/src/main/java/com/chesire/nekome/kitsu/library/dto/AddResponseDto.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.kitsu.library.dto
2 |
3 | import com.squareup.moshi.Json
4 | import com.squareup.moshi.JsonClass
5 |
6 | /**
7 | * DTO from the Kitsu library add/update endpoint.
8 | */
9 | @JsonClass(generateAdapter = true)
10 | data class AddResponseDto(
11 | @Json(name = "data")
12 | val data: DataDto,
13 | @Json(name = "included")
14 | val included: List
15 | )
16 |
--------------------------------------------------------------------------------
/libraries/kitsu/auth/src/main/java/com/chesire/nekome/kitsu/auth/dto/AuthResponseDto.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.kitsu.auth.dto
2 |
3 | import com.squareup.moshi.Json
4 | import com.squareup.moshi.JsonClass
5 |
6 | /**
7 | * DTO for responses from the Kitsu auth endpoints.
8 | */
9 | @JsonClass(generateAdapter = true)
10 | data class AuthResponseDto(
11 | @Json(name = "access_token")
12 | val accessToken: String,
13 | @Json(name = "refresh_token")
14 | val refreshToken: String
15 | )
16 |
--------------------------------------------------------------------------------
/features/settings/src/main/java/com/chesire/nekome/app/settings/config/core/RetrieveUserUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.app.settings.config.core
2 |
3 | import com.chesire.nekome.datasource.user.User
4 | import com.chesire.nekome.datasource.user.UserRepository
5 | import javax.inject.Inject
6 | import kotlinx.coroutines.flow.Flow
7 |
8 | class RetrieveUserUseCase @Inject constructor(private val userRepository: UserRepository) {
9 |
10 | operator fun invoke(): Flow = userRepository.user
11 | }
12 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-GB/full_description.txt:
--------------------------------------------------------------------------------
1 | An ad free and open source application that uses Kitsu to keep your current anime and manga series progress up to date.
2 |
3 | Application features:
4 | * Add new anime/manga series to track
5 | * Update current series episode/chapter number
6 | * Change series status
7 |
8 | This app does NOT allow you to read or watch anything, this merely allows you to track your current progress.
9 |
10 | Note: A free account with Kitsu is required to use this application.
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/full_description.txt:
--------------------------------------------------------------------------------
1 | An ad free and open source application that uses Kitsu to keep your current anime and manga series progress up to date.
2 |
3 | Application features:
4 | * Add new anime/manga series to track
5 | * Update current series episode/chapter number
6 | * Change series status
7 |
8 | This app does NOT allow you to read or watch anything, this merely allows you to track your current progress.
9 |
10 | Note: A free account with Kitsu is required to use this application.
--------------------------------------------------------------------------------
/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/SeriesWidgetEntryPoint.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.feature.serieswidget
2 |
3 | import com.chesire.nekome.feature.serieswidget.ui.SeriesWidgetViewModel
4 | import dagger.hilt.EntryPoint
5 | import dagger.hilt.InstallIn
6 | import dagger.hilt.components.SingletonComponent
7 |
8 | @EntryPoint
9 | @InstallIn(SingletonComponent::class)
10 | interface SeriesWidgetEntryPoint {
11 |
12 | fun seriesWidgetViewModel(): SeriesWidgetViewModel
13 | }
14 |
--------------------------------------------------------------------------------
/features/serieswidget/src/main/res/xml/series_widget_info.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/chesire/nekome/helpers/AuthProviderExtensions.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.helpers
2 |
3 | import com.chesire.nekome.datasource.auth.local.AuthProvider
4 |
5 | /**
6 | * Tells the [AuthProvider] that a user is logged in, used to skip login.
7 | */
8 | fun AuthProvider.login() {
9 | accessToken = "fakeAccessToken"
10 | }
11 |
12 | /**
13 | * Tells the [AuthProvider] that a user is logged out, used to enter login.
14 | */
15 | fun AuthProvider.logout() {
16 | accessToken = ""
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chesire/nekome/ui/UIState.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.ui
2 |
3 | data class UIState(
4 | val isInitialized: Boolean,
5 | val userLoggedIn: Boolean,
6 | val defaultHomeScreen: String,
7 | val kickUserToLogin: Unit?
8 | ) {
9 | companion object {
10 | val empty = UIState(
11 | isInitialized = false,
12 | userLoggedIn = false,
13 | defaultHomeScreen = Screen.Anime.route,
14 | kickUserToLogin = null
15 | )
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/features/series/src/main/java/com/chesire/nekome/app/series/collection/core/CurrentSortUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.app.series.collection.core
2 |
3 | import com.chesire.nekome.core.preferences.SeriesPreferences
4 | import com.chesire.nekome.core.preferences.flags.SortOption
5 | import javax.inject.Inject
6 | import kotlinx.coroutines.flow.first
7 |
8 | class CurrentSortUseCase @Inject constructor(private val pref: SeriesPreferences) {
9 |
10 | suspend operator fun invoke(): SortOption = pref.sort.first()
11 | }
12 |
--------------------------------------------------------------------------------
/libraries/kitsu/auth/src/main/java/com/chesire/nekome/kitsu/auth/dto/RefreshTokenRequestDto.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.kitsu.auth.dto
2 |
3 | import com.squareup.moshi.Json
4 | import com.squareup.moshi.JsonClass
5 |
6 | /**
7 | * DTO to use when requesting to refresh the auth token.
8 | */
9 | @JsonClass(generateAdapter = true)
10 | data class RefreshTokenRequestDto(
11 | @Json(name = "refresh_token")
12 | val refreshToken: String,
13 | @Json(name = "grant_type")
14 | val grantType: String = "refresh_token"
15 | )
16 |
--------------------------------------------------------------------------------
/features/series/src/main/java/com/chesire/nekome/app/series/collection/core/CollectSeriesUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.app.series.collection.core
2 |
3 | import com.chesire.nekome.datasource.series.SeriesDomain
4 | import com.chesire.nekome.datasource.series.SeriesRepository
5 | import javax.inject.Inject
6 | import kotlinx.coroutines.flow.Flow
7 |
8 | class CollectSeriesUseCase @Inject constructor(private val seriesRepo: SeriesRepository) {
9 |
10 | operator fun invoke(): Flow> = seriesRepo.getSeries()
11 | }
12 |
--------------------------------------------------------------------------------
/libraries/core/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 | 8dp
7 | 16dp
8 | 32dp
9 |
10 | 8dp
11 |
12 | 8dp
13 |
14 |
--------------------------------------------------------------------------------
/libraries/datasource/search/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.library)
3 | alias(libs.plugins.kotlin.android)
4 | }
5 |
6 | android {
7 | namespace = "com.chesire.nekome.datasource.search"
8 | compileSdk = libs.versions.sdk.get().toInt()
9 |
10 | defaultConfig {
11 | minSdk = 21
12 |
13 | consumerProguardFiles("consumer-rules.pro")
14 | }
15 | }
16 |
17 | dependencies {
18 | implementation(project(":libraries:core"))
19 |
20 | implementation(libs.kotlin.result)
21 | }
22 |
--------------------------------------------------------------------------------
/libraries/datasource/trending/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.library)
3 | alias(libs.plugins.kotlin.android)
4 | }
5 |
6 | android {
7 | namespace = "com.chesire.nekome.datasource.trending"
8 | compileSdk = libs.versions.sdk.get().toInt()
9 |
10 | defaultConfig {
11 | minSdk = 21
12 |
13 | consumerProguardFiles("consumer-rules.pro")
14 | }
15 | }
16 |
17 | dependencies {
18 | implementation(project(":libraries:core"))
19 |
20 | implementation(libs.kotlin.result)
21 | }
22 |
--------------------------------------------------------------------------------
/features/login/src/main/res/drawable/ic_lock.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/libraries/kitsu/src/main/java/com/chesire/nekome/kitsu/api/intermediaries/Links.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.kitsu.api.intermediaries
2 |
3 | import com.squareup.moshi.Json
4 | import com.squareup.moshi.JsonClass
5 |
6 | /**
7 | * Class used as intermediary when parsing out response json.
8 | */
9 | @JsonClass(generateAdapter = true)
10 | data class Links(
11 | @Json(name = "first")
12 | val first: String = "",
13 | @Json(name = "next")
14 | val next: String = "",
15 | @Json(name = "last")
16 | val last: String = ""
17 | )
18 |
--------------------------------------------------------------------------------
/features/login/src/main/res/drawable/ic_account_circle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/chesire/nekome/helpers/creation/UserDomain.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.helpers.creation
2 |
3 | import com.chesire.nekome.core.flags.Service
4 | import com.chesire.nekome.core.models.ImageModel
5 | import com.chesire.nekome.datasource.user.UserDomain
6 |
7 | /**
8 | * Creates a new [UserDomain].
9 | */
10 | fun createUserDomain() =
11 | UserDomain(
12 | userId = 1,
13 | name = "name",
14 | avatar = ImageModel.empty,
15 | coverImage = ImageModel.empty,
16 | service = Service.Kitsu
17 | )
18 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chesire/nekome/injection/LogoutExecutorModule.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.injection
2 |
3 | import com.chesire.nekome.LogoutHandler
4 | import com.chesire.nekome.app.settings.config.LogoutExecutor
5 | import dagger.Binds
6 | import dagger.Module
7 | import dagger.hilt.InstallIn
8 | import dagger.hilt.android.components.ViewModelComponent
9 |
10 | @Module
11 | @InstallIn(ViewModelComponent::class)
12 | abstract class LogoutExecutorModule {
13 |
14 | @Binds
15 | abstract fun bindExecutor(concrete: LogoutHandler): LogoutExecutor
16 | }
17 |
--------------------------------------------------------------------------------
/features/search/src/main/java/com/chesire/nekome/app/search/search/ui/ViewAction.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.app.search.search.ui
2 |
3 | import com.chesire.nekome.app.search.search.core.model.SearchGroup
4 |
5 | sealed interface ViewAction {
6 | data class SearchGroupChanged(val newGroup: SearchGroup) : ViewAction
7 | data class SearchTextUpdated(val newSearchText: String) : ViewAction
8 | data object ExecuteSearch : ViewAction
9 | data class TrackSeries(val model: ResultModel) : ViewAction
10 | data object ErrorSnackbarObserved : ViewAction
11 | }
12 |
--------------------------------------------------------------------------------
/libraries/kitsu/auth/src/main/java/com/chesire/nekome/kitsu/auth/dto/LoginRequestDto.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.kitsu.auth.dto
2 |
3 | import com.squareup.moshi.Json
4 | import com.squareup.moshi.JsonClass
5 |
6 | /**
7 | * DTO to use when attempting to authorize as a user.
8 | */
9 | @JsonClass(generateAdapter = true)
10 | data class LoginRequestDto(
11 | @Json(name = "username")
12 | val username: String,
13 | @Json(name = "password")
14 | val password: String,
15 | @Json(name = "grant_type")
16 | val grantType: String = "password"
17 | )
18 |
--------------------------------------------------------------------------------
/features/settings/src/main/java/com/chesire/nekome/app/settings/config/LogoutExecutor.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.app.settings.config
2 |
3 | /**
4 | * Interface to populate with a concrete to handle performing log out operations.
5 | */
6 | interface LogoutExecutor {
7 |
8 | /**
9 | * Execute log out, clearing anything left over and resetting the application state.
10 | *
11 | * This needs to be called using a suspend function so clearing any data such as from databases
12 | * can be done safely.
13 | */
14 | suspend fun executeLogout()
15 | }
16 |
--------------------------------------------------------------------------------
/libraries/datasource/user/src/main/java/com/chesire/nekome/datasource/user/remote/UserApi.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.datasource.user.remote
2 |
3 | import com.chesire.nekome.core.models.ErrorDomain
4 | import com.chesire.nekome.datasource.user.UserDomain
5 | import com.github.michaelbull.result.Result
6 |
7 | /**
8 | * Methods relating to getting information about a user from the api.
9 | */
10 | interface UserApi {
11 |
12 | /**
13 | * Executes request to get the user details.
14 | */
15 | suspend fun getUserDetails(): Result
16 | }
17 |
--------------------------------------------------------------------------------
/libraries/datasource/activity/src/main/java/com/chesire/nekome/datasource/activity/remote/ActivityApi.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.datasource.activity.remote
2 |
3 | import com.chesire.nekome.core.models.ErrorDomain
4 | import com.chesire.nekome.datasource.activity.ActivityDomain
5 | import com.github.michaelbull.result.Result
6 |
7 | /**
8 | * Methods relating to retrieving the users activity.
9 | */
10 | interface ActivityApi {
11 |
12 | /**
13 | * Retrieve the users latest activity.
14 | */
15 | suspend fun retrieveActivity(): Result, ErrorDomain>
16 | }
17 |
--------------------------------------------------------------------------------
/features/search/src/main/java/com/chesire/nekome/app/search/search/core/RememberSearchGroupUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.app.search.search.core
2 |
3 | import com.chesire.nekome.app.search.search.core.model.SearchGroup
4 | import com.chesire.nekome.app.search.search.data.SearchPreferences
5 | import javax.inject.Inject
6 |
7 | class RememberSearchGroupUseCase @Inject constructor(
8 | private val searchPreferences: SearchPreferences
9 | ) {
10 |
11 | operator fun invoke(newSearchGroup: SearchGroup) {
12 | searchPreferences.lastSearchGroup = newSearchGroup.name
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/features/settings/src/main/java/com/chesire/nekome/app/settings/config/core/UpdateRateSeriesUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.app.settings.config.core
2 |
3 | import com.chesire.nekome.core.preferences.SeriesPreferences
4 | import javax.inject.Inject
5 | import timber.log.Timber
6 |
7 | class UpdateRateSeriesUseCase @Inject constructor(private val pref: SeriesPreferences) {
8 |
9 | suspend operator fun invoke(newRateSeriesValue: Boolean) {
10 | Timber.i("Updating rate series to [$newRateSeriesValue]")
11 | pref.updateRateSeriesOnCompletion(newRateSeriesValue)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | android.useAndroidX=true
10 | org.gradle.configuration-cache=true
11 | org.gradle.jvmargs=-Xmx1536m
12 | org.gradle.parallel=true
13 |
--------------------------------------------------------------------------------
/features/settings/src/main/java/com/chesire/nekome/app/settings/config/core/UpdateThemeUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.app.settings.config.core
2 |
3 | import com.chesire.nekome.core.preferences.ApplicationPreferences
4 | import com.chesire.nekome.core.preferences.flags.Theme
5 | import javax.inject.Inject
6 | import timber.log.Timber
7 |
8 | class UpdateThemeUseCase @Inject constructor(private val pref: ApplicationPreferences) {
9 |
10 | suspend operator fun invoke(newTheme: Theme) {
11 | Timber.i("Updating theme to [$newTheme]")
12 | pref.updateTheme(newTheme)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/libraries/kitsu/trending/src/test/java/com/chesire/nekome/kitsu/trending/TrendingCreation.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.kitsu.trending
2 |
3 | import com.chesire.nekome.core.flags.SeriesType
4 | import com.chesire.nekome.core.models.ImageModel
5 | import com.chesire.nekome.kitsu.trending.dto.TrendingItemDto
6 |
7 | /**
8 | * Create a [TrendingItemDto] for tests.
9 | */
10 | fun createTrendingItemDto(type: SeriesType) =
11 | TrendingItemDto(
12 | 0,
13 | type,
14 | TrendingItemDto.Attributes(
15 | "canonicalTitle",
16 | ImageModel.empty
17 | )
18 | )
19 |
--------------------------------------------------------------------------------
/features/series/src/main/java/com/chesire/nekome/app/series/collection/core/UpdateSortUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.app.series.collection.core
2 |
3 | import com.chesire.nekome.core.preferences.SeriesPreferences
4 | import com.chesire.nekome.core.preferences.flags.SortOption
5 | import javax.inject.Inject
6 | import timber.log.Timber
7 |
8 | class UpdateSortUseCase @Inject constructor(private val pref: SeriesPreferences) {
9 |
10 | suspend operator fun invoke(newSortOption: SortOption) {
11 | Timber.d("Updating sorting to [$newSortOption]")
12 | pref.updateSort(newSortOption)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/chesire/nekome/injection/MockAuthModule.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.injection
2 |
3 | import com.chesire.nekome.datasource.auth.remote.AuthApi
4 | import dagger.Module
5 | import dagger.Provides
6 | import dagger.Reusable
7 | import dagger.hilt.components.SingletonComponent
8 | import dagger.hilt.testing.TestInstallIn
9 | import io.mockk.mockk
10 |
11 | @Module
12 | @TestInstallIn(
13 | components = [SingletonComponent::class],
14 | replaces = [AuthModule::class]
15 | )
16 | class MockAuthModule {
17 |
18 | @Provides
19 | @Reusable
20 | fun provideApi() = mockk()
21 | }
22 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/chesire/nekome/injection/MockUserModule.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.injection
2 |
3 | import com.chesire.nekome.datasource.user.remote.UserApi
4 | import dagger.Module
5 | import dagger.Provides
6 | import dagger.Reusable
7 | import dagger.hilt.components.SingletonComponent
8 | import dagger.hilt.testing.TestInstallIn
9 | import io.mockk.mockk
10 |
11 | @Module
12 | @TestInstallIn(
13 | components = [SingletonComponent::class],
14 | replaces = [UserModule::class]
15 | )
16 | class MockUserModule {
17 |
18 | @Provides
19 | @Reusable
20 | fun provideApi() = mockk()
21 | }
22 |
--------------------------------------------------------------------------------
/libraries/kitsu/library/src/main/java/com/chesire/nekome/kitsu/library/dto/RetrieveResponseDto.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.kitsu.library.dto
2 |
3 | import com.chesire.nekome.kitsu.api.intermediaries.Links
4 | import com.squareup.moshi.Json
5 | import com.squareup.moshi.JsonClass
6 |
7 | /**
8 | * DTO from the Kitsu library retrieve endpoint.
9 | */
10 | @JsonClass(generateAdapter = true)
11 | data class RetrieveResponseDto(
12 | @Json(name = "data")
13 | val data: List,
14 | @Json(name = "included")
15 | val included: List?,
16 | @Json(name = "links")
17 | val links: Links
18 | )
19 |
--------------------------------------------------------------------------------
/libraries/datasource/search/src/main/java/com/chesire/nekome/datasource/search/SearchDomain.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.datasource.search
2 |
3 | import com.chesire.nekome.core.flags.SeriesType
4 | import com.chesire.nekome.core.flags.Subtype
5 | import com.chesire.nekome.core.models.ImageModel
6 |
7 | /**
8 | * Domain related to a singular searched item.
9 | */
10 | data class SearchDomain(
11 | val id: Int,
12 | val type: SeriesType,
13 | val synopsis: String,
14 | val canonicalTitle: String,
15 | val otherTitles: Map,
16 | val subtype: Subtype,
17 | val posterImage: ImageModel
18 | )
19 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chesire/nekome/services/WidgetDataWorker.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.services
2 |
3 | import android.content.Context
4 | import androidx.glance.appwidget.updateAll
5 | import androidx.work.CoroutineWorker
6 | import androidx.work.WorkerParameters
7 | import com.chesire.nekome.feature.serieswidget.ui.SeriesWidget
8 |
9 | class WidgetDataWorker(
10 | private val context: Context,
11 | params: WorkerParameters
12 | ) : CoroutineWorker(context, params) {
13 |
14 | override suspend fun doWork(): Result {
15 | SeriesWidget().updateAll(context)
16 | return Result.success()
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/chesire/nekome/injection/MockLibraryModule.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.injection
2 |
3 | import com.chesire.nekome.datasource.series.remote.SeriesApi
4 | import dagger.Module
5 | import dagger.Provides
6 | import dagger.Reusable
7 | import dagger.hilt.components.SingletonComponent
8 | import dagger.hilt.testing.TestInstallIn
9 | import io.mockk.mockk
10 |
11 | @Module
12 | @TestInstallIn(
13 | components = [SingletonComponent::class],
14 | replaces = [LibraryModule::class]
15 | )
16 | class MockLibraryModule {
17 |
18 | @Provides
19 | @Reusable
20 | fun provideApi() = mockk()
21 | }
22 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/chesire/nekome/injection/MockSearchModule.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.injection
2 |
3 | import com.chesire.nekome.datasource.search.remote.SearchApi
4 | import dagger.Module
5 | import dagger.Provides
6 | import dagger.Reusable
7 | import dagger.hilt.components.SingletonComponent
8 | import dagger.hilt.testing.TestInstallIn
9 | import io.mockk.mockk
10 |
11 | @Module
12 | @TestInstallIn(
13 | components = [SingletonComponent::class],
14 | replaces = [SearchModule::class]
15 | )
16 | class MockSearchModule {
17 |
18 | @Provides
19 | @Reusable
20 | fun provideApi() = mockk()
21 | }
22 |
--------------------------------------------------------------------------------
/libraries/kitsu/activity/src/main/java/com/chesire/nekome/kitsu/activity/KitsuActivityService.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.kitsu.activity
2 |
3 | import com.chesire.nekome.kitsu.activity.dto.RetrieveActivityDto
4 | import retrofit2.Response
5 | import retrofit2.http.GET
6 |
7 | /**
8 | * Constructed with Retrofit to interface with the Kitsu API for queries related to user activity.
9 | */
10 | interface KitsuActivityService {
11 |
12 | /**
13 | * Retrieves the recent library events for a user.
14 | */
15 | @GET("api/edge/library-events")
16 | suspend fun retrieveLibraryEvents(): Response
17 | }
18 |
--------------------------------------------------------------------------------
/features/series/src/main/java/com/chesire/nekome/app/series/item/core/RetrieveItemUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.app.series.item.core
2 |
3 | import com.chesire.nekome.datasource.series.SeriesDomain
4 | import com.chesire.nekome.datasource.series.SeriesRepository
5 | import javax.inject.Inject
6 | import kotlinx.coroutines.Dispatchers
7 | import kotlinx.coroutines.withContext
8 |
9 | class RetrieveItemUseCase @Inject constructor(private val seriesRepo: SeriesRepository) {
10 |
11 | suspend operator fun invoke(userSeriesId: Int): SeriesDomain = withContext(Dispatchers.IO) {
12 | seriesRepo.getSeries(userSeriesId)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/libraries/database/src/main/java/com/chesire/nekome/database/entity/UserEntity.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.database.entity
2 |
3 | import androidx.room.Entity
4 | import androidx.room.PrimaryKey
5 | import com.chesire.nekome.core.flags.Service
6 | import com.chesire.nekome.core.models.ImageModel
7 | import com.squareup.moshi.JsonClass
8 |
9 | /**
10 | * Data for a singular user entity.
11 | */
12 | @Entity
13 | @JsonClass(generateAdapter = true)
14 | data class UserEntity(
15 | val userId: Int,
16 | val name: String,
17 | val avatar: ImageModel,
18 | val coverImage: ImageModel,
19 | @PrimaryKey
20 | val service: Service
21 | )
22 |
--------------------------------------------------------------------------------
/features/settings/src/main/java/com/chesire/nekome/app/settings/config/core/UpdateImageQualityUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.app.settings.config.core
2 |
3 | import com.chesire.nekome.core.preferences.SeriesPreferences
4 | import com.chesire.nekome.core.preferences.flags.ImageQuality
5 | import javax.inject.Inject
6 | import timber.log.Timber
7 |
8 | class UpdateImageQualityUseCase @Inject constructor(private val pref: SeriesPreferences) {
9 |
10 | suspend operator fun invoke(newImageQuality: ImageQuality) {
11 | Timber.i("Updating image quality to [$newImageQuality]")
12 | pref.updateImageQuality(newImageQuality)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/features/login/src/main/java/com/chesire/nekome/app/login/credentials/core/PopulateUserDetailsUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.app.login.credentials.core
2 |
3 | import com.chesire.nekome.datasource.user.UserRepository
4 | import com.github.michaelbull.result.Result
5 | import javax.inject.Inject
6 | import kotlinx.coroutines.Dispatchers
7 | import kotlinx.coroutines.withContext
8 |
9 | class PopulateUserDetailsUseCase @Inject constructor(private val user: UserRepository) {
10 |
11 | suspend operator fun invoke(): Result {
12 | return withContext(Dispatchers.IO) {
13 | user.refreshUser()
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/features/settings/src/main/java/com/chesire/nekome/app/settings/config/core/UpdateTitleLanguageUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.app.settings.config.core
2 |
3 | import com.chesire.nekome.core.preferences.SeriesPreferences
4 | import com.chesire.nekome.core.preferences.flags.TitleLanguage
5 | import javax.inject.Inject
6 | import timber.log.Timber
7 |
8 | class UpdateTitleLanguageUseCase @Inject constructor(private val pref: SeriesPreferences) {
9 |
10 | suspend operator fun invoke(newTitleLanguage: TitleLanguage) {
11 | Timber.i("Updating title language to [$newTitleLanguage]")
12 | pref.updateTitleLanguage(newTitleLanguage)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/libraries/core/src/test/java/com/chesire/nekome/core/flags/SeriesTypeTests.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.core.flags
2 |
3 | import org.junit.Assert.assertEquals
4 | import org.junit.Test
5 |
6 | class SeriesTypeTests {
7 | @Test
8 | fun `forId '-1' returns SeriesType#Unknown`() {
9 | assertEquals(SeriesType.Unknown, SeriesType.forId(-1))
10 | }
11 |
12 | @Test
13 | fun `forId '0' returns SeriesType#Anime`() {
14 | assertEquals(SeriesType.Anime, SeriesType.forId(0))
15 | }
16 |
17 | @Test
18 | fun `forId '1' returns SeriesType#Manga`() {
19 | assertEquals(SeriesType.Manga, SeriesType.forId(1))
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/chesire/nekome/helpers/UserDaoExtensions.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.helpers
2 |
3 | import com.chesire.nekome.core.models.ImageModel
4 | import com.chesire.nekome.database.dao.UserDao
5 | import com.chesire.nekome.testing.createUserEntity
6 | import kotlinx.coroutines.runBlocking
7 |
8 | /**
9 | * Creates and pushes a new user into the [UserDao].
10 | */
11 | fun UserDao.createTestUser(
12 | userId: Int = 0,
13 | name: String = "Nekome",
14 | avatar: ImageModel = ImageModel.empty,
15 | coverImage: ImageModel = ImageModel.empty
16 | ) = runBlocking {
17 | insert(createUserEntity(userId, name, avatar, coverImage))
18 | }
19 |
--------------------------------------------------------------------------------
/features/serieswidget/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
10 |
11 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/chesire/nekome/TestRunner.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome
2 |
3 | import android.app.Application
4 | import android.content.Context
5 | import androidx.test.runner.AndroidJUnitRunner
6 | import dagger.hilt.android.testing.HiltTestApplication
7 |
8 | /**
9 | * Overridden runner that forces the application object to be [HiltTestApplication].
10 | */
11 | @Suppress("unused")
12 | class TestRunner : AndroidJUnitRunner() {
13 | override fun newApplication(
14 | cl: ClassLoader?,
15 | className: String?,
16 | context: Context?
17 | ): Application = super.newApplication(cl, HiltTestApplication::class.java.name, context)
18 | }
19 |
--------------------------------------------------------------------------------
/libraries/datasource/auth/src/main/java/com/chesire/nekome/datasource/auth/remote/AuthApi.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.datasource.auth.remote
2 |
3 | import com.github.michaelbull.result.Result
4 |
5 | /**
6 | * Methods relating to authorizing as a user.
7 | */
8 | interface AuthApi {
9 |
10 | /**
11 | * Logs into the service using a [username] and [password], returning the success state.
12 | */
13 | suspend fun login(username: String, password: String): Result
14 |
15 | /**
16 | * Refreshes any current auth credentials.
17 | */
18 | suspend fun refresh(refreshToken: String): Result
19 | }
20 |
--------------------------------------------------------------------------------
/features/search/src/main/java/com/chesire/nekome/app/search/search/core/RetrieveUserSeriesIdsUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.app.search.search.core
2 |
3 | import com.chesire.nekome.datasource.series.SeriesRepository
4 | import javax.inject.Inject
5 | import kotlinx.coroutines.flow.Flow
6 | import kotlinx.coroutines.flow.mapLatest
7 |
8 | class RetrieveUserSeriesIdsUseCase @Inject constructor(private val seriesRepo: SeriesRepository) {
9 |
10 | operator fun invoke(): Flow> {
11 | return seriesRepo
12 | .getSeries()
13 | .mapLatest { seriesDomains ->
14 | seriesDomains.map { it.id }
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/features/settings/src/main/java/com/chesire/nekome/app/settings/config/core/UpdateDefaultHomeScreenUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.app.settings.config.core
2 |
3 | import com.chesire.nekome.core.preferences.ApplicationPreferences
4 | import com.chesire.nekome.core.preferences.flags.HomeScreenOptions
5 | import javax.inject.Inject
6 | import timber.log.Timber
7 |
8 | class UpdateDefaultHomeScreenUseCase @Inject constructor(private val pref: ApplicationPreferences) {
9 |
10 | suspend operator fun invoke(newHomeScreen: HomeScreenOptions) {
11 | Timber.i("Updating default home screen to [$newHomeScreen]")
12 | pref.updateDefaultHomeScreen(newHomeScreen)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/features/settings/src/main/java/com/chesire/nekome/app/settings/config/core/UpdateDefaultSeriesStateUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.app.settings.config.core
2 |
3 | import com.chesire.nekome.core.flags.UserSeriesStatus
4 | import com.chesire.nekome.core.preferences.ApplicationPreferences
5 | import javax.inject.Inject
6 | import timber.log.Timber
7 |
8 | class UpdateDefaultSeriesStateUseCase @Inject constructor(private val pref: ApplicationPreferences) {
9 |
10 | suspend operator fun invoke(newDefaultState: UserSeriesStatus) {
11 | Timber.i("Updating default series status to [$newDefaultState]")
12 | pref.updateDefaultSeriesState(newDefaultState)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/libraries/datasource/activity/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.library)
3 | alias(libs.plugins.kotlin.android)
4 | }
5 |
6 | android {
7 | namespace = "com.chesire.nekome.datasource.activity"
8 | compileSdk = libs.versions.sdk.get().toInt()
9 |
10 | defaultConfig {
11 | minSdk = 21
12 |
13 | consumerProguardFiles("consumer-rules.pro")
14 | }
15 | }
16 |
17 | dependencies {
18 | implementation(project(":libraries:core"))
19 |
20 | implementation(libs.google.hilt.android)
21 | implementation(libs.kotlin.coroutines.android)
22 | implementation(libs.kotlin.coroutines.core)
23 | implementation(libs.kotlin.result)
24 | }
25 |
--------------------------------------------------------------------------------
/features/series/src/main/java/com/chesire/nekome/app/series/item/ui/ViewAction.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.app.series.item.ui
2 |
3 | import com.chesire.nekome.core.flags.UserSeriesStatus
4 |
5 | sealed interface ViewAction {
6 | data class OnDeleteResult(val result: Boolean) : ViewAction
7 | data class ProgressChanged(val newProgress: String) : ViewAction
8 | data class RatingChanged(val newRating: Float) : ViewAction
9 | data class SeriesStatusChanged(val newSeriesStatus: UserSeriesStatus) : ViewAction
10 | object ConfirmPressed : ViewAction
11 | object DeletePressed : ViewAction
12 | object SnackbarObserved : ViewAction
13 | object FinishScreenObserved : ViewAction
14 | }
15 |
--------------------------------------------------------------------------------
/libraries/database/src/main/java/com/chesire/nekome/database/converters/SubtypeConverter.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.database.converters
2 |
3 | import androidx.room.TypeConverter
4 | import com.chesire.nekome.core.flags.Subtype
5 |
6 | /**
7 | * Converter for [Subtype] -> [String].
8 | *
9 | * For saving an [Subtype] into the database.
10 | */
11 | class SubtypeConverter {
12 | /**
13 | * Converts a [Subtype] into a [String].
14 | */
15 | @TypeConverter
16 | fun fromSubtype(type: Subtype): String = type.name
17 |
18 | /**
19 | * Converts a [String] into a [Subtype].
20 | */
21 | @TypeConverter
22 | fun toSubtype(type: String): Subtype = Subtype.valueOf(type)
23 | }
24 |
--------------------------------------------------------------------------------
/libraries/kitsu/user/src/main/java/com/chesire/nekome/kitsu/user/KitsuUserService.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.kitsu.user
2 |
3 | import com.chesire.nekome.kitsu.user.dto.UserResponseDto
4 | import retrofit2.Response
5 | import retrofit2.http.GET
6 |
7 | /**
8 | * Constructed using Retrofit to interface with the Kitsu API for queries related to a user.
9 | */
10 | interface KitsuUserService {
11 |
12 | /**
13 | * Gets the details about a user.
14 | * This requires an auth token set in the header to get the correct user.
15 | */
16 | @GET("api/edge/users?filter[self]=true&fields[users]=id,name,avatar,coverImage")
17 | suspend fun getUserDetailsAsync(): Response
18 | }
19 |
--------------------------------------------------------------------------------
/libraries/database/src/main/java/com/chesire/nekome/database/converters/ServiceConverter.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.database.converters
2 |
3 | import androidx.room.TypeConverter
4 | import com.chesire.nekome.core.flags.Service
5 |
6 | /**
7 | * Converter for [Service] -> [String].
8 | *
9 | * For saving an [Service] into the database.
10 | */
11 | class ServiceConverter {
12 | /**
13 | * Converts a [Service] into a [String].
14 | */
15 | @TypeConverter
16 | fun fromService(service: Service): String = service.name
17 |
18 | /**
19 | * Converts a [String] into a [Service].
20 | */
21 | @TypeConverter
22 | fun toService(service: String): Service = Service.valueOf(service)
23 | }
24 |
--------------------------------------------------------------------------------
/fastlane/Fastfile:
--------------------------------------------------------------------------------
1 | # This file contains the fastlane.tools configuration
2 | # You can find the documentation at https://docs.fastlane.tools
3 | #
4 | # For a list of all available actions, check out
5 | #
6 | # https://docs.fastlane.tools/actions
7 | #
8 |
9 | # Uncomment the line if you want fastlane to automatically update itself
10 | # update_fastlane
11 |
12 | default_platform(:android)
13 |
14 | platform :android do
15 | desc "Deploy a new Alpha Build to the Play Store"
16 | lane :alpha do
17 | gradle(
18 | task: 'clean assemble',
19 | build_type: 'release'
20 | )
21 | upload_to_play_store(
22 | track: 'alpha',
23 | #mapping: '' - Will add this later
24 | )
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/libraries/kitsu/search/src/test/java/com/chesire/nekome/kitsu/search/SearchCreation.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.kitsu.search
2 |
3 | import com.chesire.nekome.core.flags.SeriesType
4 | import com.chesire.nekome.core.flags.Subtype
5 | import com.chesire.nekome.core.models.ImageModel
6 | import com.chesire.nekome.kitsu.search.dto.SearchItemDto
7 |
8 | /**
9 | * Create a [SearchItemDto] for tests.
10 | */
11 | fun createSearchItemDto(type: SeriesType) =
12 | SearchItemDto(
13 | 0,
14 | type,
15 | SearchItemDto.Attributes(
16 | "synopsis",
17 | mapOf("en" to "en"),
18 | "canonicalTitle",
19 | Subtype.Unknown,
20 | ImageModel.empty
21 | )
22 | )
23 |
--------------------------------------------------------------------------------
/features/series/src/main/java/com/chesire/nekome/app/series/collection/core/UpdateFiltersUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.app.series.collection.core
2 |
3 | import com.chesire.nekome.core.flags.UserSeriesStatus
4 | import com.chesire.nekome.core.preferences.SeriesPreferences
5 | import javax.inject.Inject
6 | import timber.log.Timber
7 |
8 | class UpdateFiltersUseCase @Inject constructor(private val pref: SeriesPreferences) {
9 |
10 | suspend operator fun invoke(newFilter: Map) {
11 | Timber.d("Updating filters to [$newFilter]")
12 | pref.updateFilter(
13 | newFilter
14 | .map { it.key.index to it.value }
15 | .toMap()
16 | )
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/libraries/database/src/main/java/com/chesire/nekome/database/converters/SeriesTypeConverter.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.database.converters
2 |
3 | import androidx.room.TypeConverter
4 | import com.chesire.nekome.core.flags.SeriesType
5 |
6 | /**
7 | * Converter for [SeriesType] -> [String].
8 | *
9 | * For saving an [SeriesType] into the database.
10 | */
11 | class SeriesTypeConverter {
12 | /**
13 | * Converts a [SeriesType] into a [String].
14 | */
15 | @TypeConverter
16 | fun fromSeriesType(type: SeriesType): String = type.name
17 |
18 | /**
19 | * Converts a [String] into a [SeriesType].
20 | */
21 | @TypeConverter
22 | fun toSeriesType(type: String): SeriesType = SeriesType.valueOf(type)
23 | }
24 |
--------------------------------------------------------------------------------
/libraries/datasource/search/src/main/java/com/chesire/nekome/datasource/search/remote/SearchApi.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.datasource.search.remote
2 |
3 | import com.chesire.nekome.core.models.ErrorDomain
4 | import com.chesire.nekome.datasource.search.SearchDomain
5 | import com.github.michaelbull.result.Result
6 |
7 | /**
8 | * Methods relating to searching for series.
9 | */
10 | interface SearchApi {
11 |
12 | /**
13 | * Search for the anime series [title].
14 | */
15 | suspend fun searchForAnime(title: String): Result, ErrorDomain>
16 |
17 | /**
18 | * Search for the manga series [title].
19 | */
20 | suspend fun searchForManga(title: String): Result, ErrorDomain>
21 | }
22 |
--------------------------------------------------------------------------------
/libraries/core/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chesire/nekome/injection/WorkerModule.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.injection
2 |
3 | import android.content.Context
4 | import androidx.work.WorkManager
5 | import dagger.Module
6 | import dagger.Provides
7 | import dagger.hilt.InstallIn
8 | import dagger.hilt.android.qualifiers.ApplicationContext
9 | import dagger.hilt.components.SingletonComponent
10 |
11 | /**
12 | * Dagger [Module] to provide the systems [WorkManager].
13 | */
14 | @Module
15 | @InstallIn(SingletonComponent::class)
16 | object WorkerModule {
17 |
18 | /**
19 | * Provides a [WorkManager] instance to the dependency graph.
20 | */
21 | @Provides
22 | fun providesWorkManager(@ApplicationContext context: Context) = WorkManager.getInstance(context)
23 | }
24 |
--------------------------------------------------------------------------------
/features/series/src/main/java/com/chesire/nekome/app/series/collection/core/CurrentFiltersUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.app.series.collection.core
2 |
3 | import com.chesire.nekome.core.flags.UserSeriesStatus
4 | import com.chesire.nekome.core.preferences.SeriesPreferences
5 | import javax.inject.Inject
6 | import kotlinx.coroutines.flow.first
7 |
8 | class CurrentFiltersUseCase @Inject constructor(private val pref: SeriesPreferences) {
9 |
10 | suspend operator fun invoke(): Map =
11 | pref
12 | .filter
13 | .first()
14 | .map { UserSeriesStatus.getFromIndex(it.key.toString()) to it.value }
15 | .filterNot { it.first == UserSeriesStatus.Unknown }
16 | .toMap()
17 | }
18 |
--------------------------------------------------------------------------------
/libraries/datasource/trending/src/main/java/com/chesire/nekome/datasource/trending/remote/TrendingApi.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.datasource.trending.remote
2 |
3 | import com.chesire.nekome.core.models.ErrorDomain
4 | import com.chesire.nekome.datasource.trending.TrendingDomain
5 | import com.github.michaelbull.result.Result
6 |
7 | /**
8 | * Methods relating to getting information about trending topics.
9 | */
10 | interface TrendingApi {
11 |
12 | /**
13 | * Retrieves the current trending anime.
14 | */
15 | suspend fun getTrendingAnime(): Result, ErrorDomain>
16 |
17 | /**
18 | * Retrieves the current trending manga.
19 | */
20 | suspend fun getTrendingManga(): Result, ErrorDomain>
21 | }
22 |
--------------------------------------------------------------------------------
/libraries/database/src/main/java/com/chesire/nekome/database/converters/SeriesStatusConverter.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.database.converters
2 |
3 | import androidx.room.TypeConverter
4 | import com.chesire.nekome.core.flags.SeriesStatus
5 |
6 | /**
7 | * Converter for [SeriesStatus] -> [String].
8 | *
9 | * For saving an [SeriesStatus] into the database.
10 | */
11 | class SeriesStatusConverter {
12 | /**
13 | * Converts a [SeriesStatus] into a [String].
14 | */
15 | @TypeConverter
16 | fun fromSeriesStatus(status: SeriesStatus): String = status.name
17 |
18 | /**
19 | * Converts a [String] into a [SeriesStatus].
20 | */
21 | @TypeConverter
22 | fun toSeriesStatus(status: String): SeriesStatus = SeriesStatus.valueOf(status)
23 | }
24 |
--------------------------------------------------------------------------------
/core/preferences/src/main/java/com/chesire/nekome/core/preferences/flags/ImageQuality.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.core.preferences.flags
2 |
3 | import androidx.annotation.StringRes
4 | import com.chesire.nekome.resources.StringResource
5 |
6 | /**
7 | * Options available for the quality of the images.
8 | */
9 | enum class ImageQuality(val index: Int, @StringRes val stringId: Int) {
10 | Low(0, StringResource.image_quality_low),
11 | Medium(1, StringResource.image_quality_medium),
12 | High(2, StringResource.image_quality_high);
13 |
14 | companion object {
15 |
16 | /**
17 | * Get [ImageQuality] for its given [index].
18 | */
19 | fun forIndex(index: Int): ImageQuality = values().find { it.index == index } ?: Low
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/features/login/src/main/java/com/chesire/nekome/app/login/syncing/core/SyncSeriesUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.app.login.syncing.core
2 |
3 | import com.chesire.nekome.datasource.series.SeriesRepository
4 | import javax.inject.Inject
5 | import kotlinx.coroutines.Dispatchers
6 | import kotlinx.coroutines.async
7 | import kotlinx.coroutines.awaitAll
8 | import kotlinx.coroutines.withContext
9 |
10 | class SyncSeriesUseCase @Inject constructor(private val seriesRepo: SeriesRepository) {
11 |
12 | suspend operator fun invoke() = withContext(Dispatchers.IO) {
13 | val animeJob = async(Dispatchers.IO) { seriesRepo.refreshAnime() }
14 | val mangaJob = async(Dispatchers.IO) { seriesRepo.refreshManga() }
15 |
16 | awaitAll(animeJob, mangaJob)
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chesire/nekome/binders/UserProviderBinder.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.binders
2 |
3 | import com.chesire.nekome.datasource.series.UserProvider
4 | import com.chesire.nekome.datasource.user.UserRepository
5 | import javax.inject.Inject
6 |
7 | /**
8 | * Provides a concrete class to bind the [UserProvider] interface.
9 | */
10 | class UserProviderBinder @Inject constructor(
11 | private val userRepository: UserRepository
12 | ) : UserProvider {
13 |
14 | override suspend fun provideUserId(): UserProvider.UserIdResult {
15 | val id = userRepository.retrieveUserId()
16 | return if (id == null) {
17 | UserProvider.UserIdResult.Failure
18 | } else {
19 | UserProvider.UserIdResult.Success(id)
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/features/search/src/main/java/com/chesire/nekome/app/search/search/data/SearchPreferences.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.app.search.search.data
2 |
3 | import android.content.SharedPreferences
4 | import androidx.core.content.edit
5 | import javax.inject.Inject
6 |
7 | /**
8 | * Wrapper around [SharedPreferences] to store settings or items related to Search.
9 | */
10 | data class SearchPreferences @Inject constructor(private val sharedPreferences: SharedPreferences) {
11 |
12 | var lastSearchGroup: String
13 | get() = sharedPreferences.getString(LAST_SEARCH_GROUP, "") ?: ""
14 | set(value) = sharedPreferences.edit { putString(LAST_SEARCH_GROUP, value) }
15 |
16 | companion object {
17 | private const val LAST_SEARCH_GROUP = "preference.last_search_group"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/libraries/database/src/test/java/com/chesire/nekome/database/converters/ServiceConverterTests.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.database.converters
2 |
3 | import com.chesire.nekome.core.flags.Service
4 | import org.junit.Assert.assertEquals
5 | import org.junit.Test
6 |
7 | class ServiceConverterTests {
8 | @Test
9 | fun `fromService converts to enum name from Service`() {
10 | val converter = ServiceConverter()
11 | Service.values().forEach {
12 | assertEquals(it.name, converter.fromService(it))
13 | }
14 | }
15 |
16 | @Test
17 | fun `toService converts to Service from name`() {
18 | val converter = ServiceConverter()
19 | Service.values().forEach {
20 | assertEquals(it, converter.toService(it.name))
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/libraries/database/src/test/java/com/chesire/nekome/database/converters/SubtypeConverterTests.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.database.converters
2 |
3 | import com.chesire.nekome.core.flags.Subtype
4 | import org.junit.Assert.assertEquals
5 | import org.junit.Test
6 |
7 | class SubtypeConverterTests {
8 | @Test
9 | fun `fromSubtype converts to enum name from Subtype`() {
10 | val converter = SubtypeConverter()
11 | Subtype.values().forEach {
12 | assertEquals(it.name, converter.fromSubtype(it))
13 | }
14 | }
15 |
16 | @Test
17 | fun `toSubtype converts to Subtype from name`() {
18 | val converter = SubtypeConverter()
19 | Subtype.values().forEach {
20 | assertEquals(it, converter.toSubtype(it.name))
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/features/series/src/main/java/com/chesire/nekome/app/series/item/core/GetImageUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.app.series.item.core
2 |
3 | import com.chesire.nekome.core.models.ImageModel
4 | import com.chesire.nekome.core.preferences.SeriesPreferences
5 | import com.chesire.nekome.core.preferences.flags.ImageQuality
6 | import javax.inject.Inject
7 | import kotlinx.coroutines.flow.first
8 |
9 | class GetImageUseCase @Inject constructor(private val pref: SeriesPreferences) {
10 |
11 | suspend operator fun invoke(images: ImageModel): String {
12 | return when (pref.imageQuality.first()) {
13 | ImageQuality.Low -> images.smallest?.url
14 | ImageQuality.Medium -> images.middlest?.url
15 | ImageQuality.High -> images.largest?.url
16 | } ?: ""
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/DomainMapper.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.feature.serieswidget.ui
2 |
3 | import com.chesire.nekome.datasource.series.SeriesDomain
4 | import javax.inject.Inject
5 |
6 | class DomainMapper @Inject constructor() {
7 |
8 | fun toSeries(domain: SeriesDomain): Series {
9 | return Series(
10 | userId = domain.userId,
11 | title = domain.title,
12 | progress = buildProgress(domain.progress, domain.totalLength),
13 | isUpdating = false
14 | )
15 | }
16 |
17 | private fun buildProgress(progress: Int, totalLength: Int): String {
18 | val maxLengthString = if (totalLength == 0) "-" else totalLength
19 | return "$progress / $maxLengthString"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/features/login/src/main/java/com/chesire/nekome/app/login/syncing/core/RetrieveAvatarUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.app.login.syncing.core
2 |
3 | import com.chesire.nekome.datasource.user.User
4 | import com.chesire.nekome.datasource.user.UserRepository
5 | import javax.inject.Inject
6 | import kotlinx.coroutines.flow.filterIsInstance
7 | import kotlinx.coroutines.flow.firstOrNull
8 | import kotlinx.coroutines.flow.map
9 |
10 | class RetrieveAvatarUseCase @Inject constructor(private val userRepository: UserRepository) {
11 |
12 | suspend operator fun invoke(): String {
13 | return userRepository
14 | .user
15 | .filterIsInstance()
16 | .map { user -> user.domain.avatar.largest?.url }
17 | .firstOrNull()
18 | ?: ""
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/libraries/kitsu/trending/src/main/java/com/chesire/nekome/kitsu/trending/KitsuTrendingService.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.kitsu.trending
2 |
3 | import com.chesire.nekome.kitsu.trending.dto.TrendingResponseDto
4 | import retrofit2.Response
5 | import retrofit2.http.GET
6 |
7 | /**
8 | * Constructed using Retrofit to interface with the Kitsu API for queries related to trending.
9 | */
10 | interface KitsuTrendingService {
11 |
12 | /**
13 | * Gets the top trending Anime from Kitsu.
14 | */
15 | @GET("api/edge/trending/anime")
16 | suspend fun getTrendingAnimeAsync(): Response
17 |
18 | /**
19 | * Gets the top trending Manga from Kitsu.
20 | */
21 | @GET("api/edge/trending/manga")
22 | suspend fun getTrendingMangaAsync(): Response
23 | }
24 |
--------------------------------------------------------------------------------
/.github/workflows/master.yml:
--------------------------------------------------------------------------------
1 | name: Master
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 |
7 | jobs:
8 | build:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: actions/checkout@v4
12 | - name: set up JDK 17
13 | uses: actions/setup-java@v4
14 | with:
15 | distribution: 'adopt'
16 | java-version: 17
17 | - name: setup gradle cache
18 | uses: gradle/actions/setup-gradle@v3
19 | with:
20 | cache-read-only: false
21 | - name: run assemble
22 | run: ./gradlew assemble
23 | - name: run kover
24 | run: ./gradlew koverXmlReport
25 | - uses: codecov/codecov-action@v4
26 | - name: run lint
27 | run: ./gradlew :app:lint
28 | - name: run detektCheck
29 | run: ./gradlew detektCheck
30 |
--------------------------------------------------------------------------------
/features/series/src/main/java/com/chesire/nekome/app/series/item/core/DeleteItemUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.app.series.item.core
2 |
3 | import com.chesire.nekome.datasource.series.SeriesRepository
4 | import com.github.michaelbull.result.Result
5 | import com.github.michaelbull.result.mapError
6 | import javax.inject.Inject
7 | import kotlinx.coroutines.Dispatchers
8 | import kotlinx.coroutines.withContext
9 |
10 | class DeleteItemUseCase @Inject constructor(private val seriesRepo: SeriesRepository) {
11 |
12 | suspend operator fun invoke(userSeriesId: Int): Result {
13 | return withContext(Dispatchers.IO) {
14 | val series = seriesRepo.getSeries(userSeriesId)
15 |
16 | seriesRepo.deleteSeries(series)
17 | .mapError { }
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/libraries/database/src/main/java/com/chesire/nekome/database/converters/UserSeriesStatusConverter.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.database.converters
2 |
3 | import androidx.room.TypeConverter
4 | import com.chesire.nekome.core.flags.UserSeriesStatus
5 |
6 | /**
7 | * Converter for [UserSeriesStatus] -> [String].
8 | *
9 | * For saving an [UserSeriesStatus] into the database.
10 | */
11 | class UserSeriesStatusConverter {
12 | /**
13 | * Converts a [UserSeriesStatus] into a [String].
14 | */
15 | @TypeConverter
16 | fun fromUserSeriesStatus(status: UserSeriesStatus): String = status.name
17 |
18 | /**
19 | * Converts a [String] into a [UserSeriesStatus].
20 | */
21 | @TypeConverter
22 | fun toUserSeriesStatus(status: String): UserSeriesStatus = UserSeriesStatus.valueOf(status)
23 | }
24 |
--------------------------------------------------------------------------------
/libraries/datasource/series/src/main/java/com/chesire/nekome/datasource/series/UserProvider.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.datasource.series
2 |
3 | /**
4 | * Provider for accessing the information for a user.
5 | */
6 | interface UserProvider {
7 | /**
8 | * Executes an async command to acquire the users id.
9 | */
10 | suspend fun provideUserId(): UserIdResult
11 |
12 | /**
13 | * Represents different result states that [provideUserId] can provide.
14 | */
15 | sealed class UserIdResult {
16 | /**
17 | * Success state containing the user id.
18 | */
19 | data class Success(val id: Int) : UserIdResult()
20 |
21 | /**
22 | * Failure state when id could not be retrieved.
23 | */
24 | object Failure : UserIdResult()
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/fastlane/README.md:
--------------------------------------------------------------------------------
1 | fastlane documentation
2 | ================
3 | # Installation
4 |
5 | Make sure you have the latest version of the Xcode command line tools installed:
6 |
7 | ```
8 | xcode-select --install
9 | ```
10 |
11 | Install _fastlane_ using
12 | ```
13 | [sudo] gem install fastlane -NV
14 | ```
15 | or alternatively using `brew cask install fastlane`
16 |
17 | # Available Actions
18 | ## Android
19 | ### android alpha
20 | ```
21 | fastlane android alpha
22 | ```
23 | Deploy a new Alpha Build to the Play Store
24 |
25 | ----
26 |
27 | This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run.
28 | More information about fastlane can be found on [fastlane.tools](https://fastlane.tools).
29 | The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools).
30 |
--------------------------------------------------------------------------------
/libraries/database/src/test/java/com/chesire/nekome/database/converters/SeriesTypeConverterTests.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.database.converters
2 |
3 | import com.chesire.nekome.core.flags.SeriesType
4 | import org.junit.Assert.assertEquals
5 | import org.junit.Test
6 |
7 | class SeriesTypeConverterTests {
8 | @Test
9 | fun `fromSeriesType converts to enum name from SeriesType`() {
10 | val converter = SeriesTypeConverter()
11 | SeriesType.values().forEach {
12 | assertEquals(it.name, converter.fromSeriesType(it))
13 | }
14 | }
15 |
16 | @Test
17 | fun `toSeriesType converts to SeriesType from name`() {
18 | val converter = SeriesTypeConverter()
19 | SeriesType.values().forEach {
20 | assertEquals(it, converter.toSeriesType(it.name))
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/testing/consumer-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.kts.
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
22 |
--------------------------------------------------------------------------------
/libraries/kitsu/src/main/java/com/chesire/nekome/kitsu/ResponseParsing.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.kitsu
2 |
3 | import com.chesire.nekome.core.models.ErrorDomain
4 | import com.chesire.nekome.datasource.auth.AuthException
5 | import java.net.UnknownHostException
6 | import retrofit2.Response
7 |
8 | /**
9 | * Converts the current [Response] into an [ErrorDomain].
10 | */
11 | fun Response.asError(): ErrorDomain = ErrorDomain(errorBody()?.string() ?: message(), code())
12 |
13 | /**
14 | * Parses out the [Exception] providing an [ErrorDomain] for use elsewhere.
15 | */
16 | fun Exception.parse(): ErrorDomain {
17 | return when (this) {
18 | is UnknownHostException -> ErrorDomain.couldNotReach
19 | is AuthException -> ErrorDomain.couldNotRefresh
20 | else -> ErrorDomain.badRequest.copy(message = toString())
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/features/login/consumer-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.kts.
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
22 |
--------------------------------------------------------------------------------
/features/search/consumer-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.kts.
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
22 |
--------------------------------------------------------------------------------
/features/series/consumer-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.kts.
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
22 |
--------------------------------------------------------------------------------
/features/series/src/main/java/com/chesire/nekome/app/series/collection/ui/ViewAction.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.app.series.collection.ui
2 |
3 | import com.chesire.nekome.core.preferences.flags.SortOption
4 |
5 | sealed interface ViewAction {
6 | object PerformSeriesRefresh : ViewAction
7 | data class SeriesPressed(val series: Series) : ViewAction
8 | object SeriesNavigationObserved : ViewAction
9 | data class IncrementSeriesPressed(val series: Series) : ViewAction
10 | data class IncrementSeriesWithRating(val series: Series, val rating: Int?) : ViewAction
11 | object SortPressed : ViewAction
12 | data class PerformSort(val option: SortOption?) : ViewAction
13 | object FilterPressed : ViewAction
14 | data class PerformFilter(val filters: List?) : ViewAction
15 | object ErrorSnackbarObserved : ViewAction
16 | }
17 |
--------------------------------------------------------------------------------
/features/settings/consumer-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.kts.
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
22 |
--------------------------------------------------------------------------------
/libraries/database/consumer-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.kts.
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
22 |
--------------------------------------------------------------------------------
/libraries/datasource/activity/src/main/java/com/chesire/nekome/datasource/activity/local/ActivityLocalDataStorage.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.datasource.activity.local
2 |
3 | import com.chesire.nekome.datasource.activity.ActivityDomain
4 | import javax.inject.Inject
5 | import javax.inject.Singleton
6 |
7 | /**
8 | * Local data storage for the users activity.
9 | * This will cache the data in memory, as it doesn't need to have long term storage.
10 | */
11 | @Singleton // Keep this file as a singleton so its memory persistent.
12 | class ActivityLocalDataStorage @Inject constructor() {
13 | var cachedActivityItems: List = emptyList()
14 |
15 | /**
16 | * Sets a new cache of data into the local cache.
17 | */
18 | fun setNewCache(newCache: List) {
19 | cachedActivityItems = newCache
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/libraries/kitsu/consumer-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.kts.
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
22 |
--------------------------------------------------------------------------------
/libraries/datasource/auth/src/main/java/com/chesire/nekome/datasource/auth/remote/AuthInjectionInterceptor.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.datasource.auth.remote
2 |
3 | import com.chesire.nekome.datasource.auth.AccessTokenRepository
4 | import javax.inject.Inject
5 | import okhttp3.Interceptor
6 | import okhttp3.Response
7 |
8 | /**
9 | * Interceptor to push the authorization header into api requests.
10 | */
11 | class AuthInjectionInterceptor @Inject constructor(
12 | private val repo: AccessTokenRepository
13 | ) : Interceptor {
14 |
15 | override fun intercept(chain: Interceptor.Chain): Response {
16 | val authenticatedRequest = chain.request()
17 | .newBuilder()
18 | .header("Authorization", "Bearer ${repo.accessToken}")
19 | .build()
20 |
21 | return chain.proceed(authenticatedRequest)
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/features/series/src/main/java/com/chesire/nekome/app/series/item/core/BuildTitleUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.app.series.item.core
2 |
3 | import com.chesire.nekome.core.preferences.SeriesPreferences
4 | import com.chesire.nekome.core.preferences.flags.TitleLanguage
5 | import com.chesire.nekome.datasource.series.SeriesDomain
6 | import javax.inject.Inject
7 | import kotlinx.coroutines.flow.first
8 |
9 | class BuildTitleUseCase @Inject constructor(private val pref: SeriesPreferences) {
10 |
11 | suspend operator fun invoke(series: SeriesDomain): String {
12 | return when (val titleLanguage = pref.titleLanguage.first()) {
13 | TitleLanguage.Canonical -> series.title
14 | else -> series.otherTitles[titleLanguage.key]
15 | .takeIf { !it.isNullOrBlank() }
16 | ?: series.title
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/libraries/database/src/test/java/com/chesire/nekome/database/converters/SeriesStatusConverterTests.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.database.converters
2 |
3 | import com.chesire.nekome.core.flags.SeriesStatus
4 | import org.junit.Assert.assertEquals
5 | import org.junit.Test
6 |
7 | class SeriesStatusConverterTests {
8 | @Test
9 | fun `fromSeriesStatus converts to enum name from SeriesStatus`() {
10 | val converter = SeriesStatusConverter()
11 | SeriesStatus.values().forEach {
12 | assertEquals(it.name, converter.fromSeriesStatus(it))
13 | }
14 | }
15 |
16 | @Test
17 | fun `toSeriesStatus converts to SeriesStatus from name`() {
18 | val converter = SeriesStatusConverter()
19 | SeriesStatus.values().forEach {
20 | assertEquals(it, converter.toSeriesStatus(it.name))
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/features/login/src/test/java/com/chesire/nekome/app/login/credentials/core/ClearCredentialsUseCaseTest.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.app.login.credentials.core
2 |
3 | import com.chesire.nekome.datasource.auth.AccessTokenRepository
4 | import io.mockk.clearAllMocks
5 | import io.mockk.mockk
6 | import io.mockk.verify
7 | import org.junit.Before
8 | import org.junit.Test
9 |
10 | class ClearCredentialsUseCaseTest {
11 |
12 | private val authRepo = mockk(relaxed = true)
13 | private lateinit var clearCredentials: ClearCredentialsUseCase
14 |
15 | @Before
16 | fun setup() {
17 | clearAllMocks()
18 | clearCredentials = ClearCredentialsUseCase(authRepo)
19 | }
20 |
21 | @Test
22 | fun `UseCase invoke clears the auth repo`() {
23 | clearCredentials()
24 |
25 | verify { authRepo.clear() }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/libraries/kitsu/user/src/main/java/com/chesire/nekome/kitsu/user/dto/UserItemDto.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.kitsu.user.dto
2 |
3 | import com.chesire.nekome.core.models.ImageModel
4 | import com.squareup.moshi.Json
5 | import com.squareup.moshi.JsonClass
6 |
7 | /**
8 | * DTO from the Kitsu user endpoint.
9 | */
10 | @JsonClass(generateAdapter = true)
11 | data class UserItemDto(
12 | @Json(name = "id")
13 | val id: Int,
14 | @Json(name = "attributes")
15 | val attributes: Attributes
16 | ) {
17 | /**
18 | * Attributes of the user item.
19 | */
20 | @JsonClass(generateAdapter = true)
21 | data class Attributes(
22 | @Json(name = "name")
23 | val name: String,
24 | @Json(name = "avatar")
25 | val avatar: ImageModel?,
26 | @Json(name = "coverImage")
27 | val coverImage: ImageModel?
28 | )
29 | }
30 |
--------------------------------------------------------------------------------
/core/preferences/src/main/java/com/chesire/nekome/core/preferences/flags/SortOption.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.core.preferences.flags
2 |
3 | import androidx.annotation.StringRes
4 | import com.chesire.nekome.resources.StringResource
5 |
6 | /**
7 | * Options available for when sorting the series list is performed.
8 | */
9 | enum class SortOption(val index: Int, @StringRes val stringId: Int) {
10 | Default(0, StringResource.sort_by_default),
11 | Title(1, StringResource.sort_by_title),
12 | StartDate(2, StringResource.sort_by_start_date),
13 | EndDate(3, StringResource.sort_by_end_date),
14 | Rating(4, StringResource.sort_by_rating);
15 |
16 | companion object {
17 | /**
18 | * Get [SortOption] for its given [index].
19 | */
20 | fun forIndex(index: Int): SortOption = values().find { it.index == index } ?: Default
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/libraries/kitsu/trending/src/main/java/com/chesire/nekome/kitsu/trending/TrendingItemDtoMapper.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.kitsu.trending
2 |
3 | import com.chesire.nekome.core.models.ImageModel
4 | import com.chesire.nekome.datasource.trending.TrendingDomain
5 | import com.chesire.nekome.kitsu.trending.dto.TrendingItemDto
6 | import javax.inject.Inject
7 |
8 | /**
9 | * Provides ability to map instances of [TrendingItemDto] into [TrendingDomain].
10 | */
11 | class TrendingItemDtoMapper @Inject constructor() {
12 |
13 | /**
14 | * Converts an instance of [TrendingItemDto] into a [TrendingDomain].
15 | */
16 | fun toTrendingDomain(input: TrendingItemDto) =
17 | TrendingDomain(
18 | input.id,
19 | input.type,
20 | input.attributes.canonicalTitle,
21 | input.attributes.posterImage ?: ImageModel.empty
22 | )
23 | }
24 |
--------------------------------------------------------------------------------
/core/preferences/src/test/java/com/chesire/nekome/core/preferences/flags/ImageQualityTests.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.core.preferences.flags
2 |
3 | import org.junit.Assert.assertEquals
4 | import org.junit.Test
5 |
6 | class ImageQualityTests {
7 |
8 | @Test
9 | fun `forIndex ImageQuality#Low returns expected value`() {
10 | assertEquals(
11 | ImageQuality.Low,
12 | ImageQuality.forIndex(0)
13 | )
14 | }
15 |
16 | @Test
17 | fun `forIndex ImageQuality#Medium returns expected value`() {
18 | assertEquals(
19 | ImageQuality.Medium,
20 | ImageQuality.forIndex(1)
21 | )
22 | }
23 |
24 | @Test
25 | fun `forIndex ImageQuality#High returns expected value`() {
26 | assertEquals(
27 | ImageQuality.High,
28 | ImageQuality.forIndex(2)
29 | )
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/features/series/src/main/java/com/chesire/nekome/app/series/collection/core/ShouldRateSeriesUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.app.series.collection.core
2 |
3 | import com.chesire.nekome.core.preferences.SeriesPreferences
4 | import com.chesire.nekome.datasource.series.SeriesRepository
5 | import javax.inject.Inject
6 | import kotlinx.coroutines.Dispatchers
7 | import kotlinx.coroutines.flow.first
8 | import kotlinx.coroutines.withContext
9 |
10 | class ShouldRateSeriesUseCase @Inject constructor(
11 | private val repo: SeriesRepository,
12 | private val pref: SeriesPreferences
13 | ) {
14 |
15 | suspend operator fun invoke(userSeriesId: Int): Boolean {
16 | return withContext(Dispatchers.IO) {
17 | val domain = repo.getSeries(userSeriesId)
18 | pref.rateSeriesOnCompletion.first() && domain.progress + 1 == domain.totalLength
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/testing/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.library)
3 | alias(libs.plugins.kotlin.android)
4 | }
5 |
6 | android {
7 | namespace = "com.chesire.nekome.testing"
8 | compileSdk = libs.versions.sdk.get().toInt()
9 |
10 | defaultConfig {
11 | minSdk = 21
12 |
13 | consumerProguardFiles("consumer-rules.pro")
14 | }
15 | packaging {
16 | resources {
17 | excludes += listOf("META-INF/AL2.0", "META-INF/LGPL2.1", "META-INF/*.kotlin_module")
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | implementation(project(":libraries:core"))
24 | implementation(project(":libraries:database"))
25 | implementation(project(":libraries:datasource:series"))
26 | implementation(project(":libraries:datasource:user"))
27 |
28 | implementation(libs.junit)
29 | implementation(libs.kotlin.coroutines.test)
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chesire/nekome/injection/AppModule.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.injection
2 |
3 | import android.content.Context
4 | import android.content.SharedPreferences
5 | import androidx.preference.PreferenceManager
6 | import dagger.Module
7 | import dagger.Provides
8 | import dagger.Reusable
9 | import dagger.hilt.InstallIn
10 | import dagger.hilt.android.qualifiers.ApplicationContext
11 | import dagger.hilt.components.SingletonComponent
12 |
13 | /**
14 | * Dagger [Module] for generic application items.
15 | */
16 | @Module
17 | @InstallIn(SingletonComponent::class)
18 | object AppModule {
19 |
20 | /**
21 | * Provides the default [SharedPreferences] for the application.
22 | */
23 | @Provides
24 | @Reusable
25 | fun provideSharedPreferences(@ApplicationContext context: Context): SharedPreferences =
26 | PreferenceManager.getDefaultSharedPreferences(context)
27 | }
28 |
--------------------------------------------------------------------------------
/libraries/datasource/user/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.library)
3 | alias(libs.plugins.kotlin.android)
4 | }
5 |
6 | android {
7 | namespace = "com.chesire.nekome.datasource.user"
8 | compileSdk = libs.versions.sdk.get().toInt()
9 |
10 | defaultConfig {
11 | minSdk = 21
12 |
13 | consumerProguardFiles("consumer-rules.pro")
14 | }
15 | }
16 |
17 | dependencies {
18 | implementation(project(":libraries:core"))
19 | implementation(project(":libraries:database"))
20 |
21 | implementation(libs.google.hilt.android)
22 | implementation(libs.kotlin.coroutines.android)
23 | implementation(libs.kotlin.coroutines.core)
24 | implementation(libs.kotlin.result)
25 | implementation(libs.timber)
26 |
27 | testImplementation(project(":testing"))
28 | testImplementation(libs.junit)
29 | testImplementation(libs.mockk)
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chesire/nekome/LogoutHandler.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome
2 |
3 | import com.chesire.nekome.app.settings.config.LogoutExecutor
4 | import com.chesire.nekome.database.RoomDB
5 | import com.chesire.nekome.datasource.auth.AccessTokenRepository
6 | import javax.inject.Inject
7 | import kotlinx.coroutines.Dispatchers
8 | import kotlinx.coroutines.withContext
9 | import timber.log.Timber
10 |
11 | /**
12 | * Handles clearing out resources for when a log out occurs.
13 | */
14 | class LogoutHandler @Inject constructor(
15 | private val repo: AccessTokenRepository,
16 | private val db: RoomDB
17 | ) : LogoutExecutor {
18 |
19 | override suspend fun executeLogout() {
20 | withContext(Dispatchers.IO) {
21 | Timber.d("Clearing database tables")
22 | db.clearAllTables()
23 | }
24 | Timber.d("Clearing auth")
25 | repo.clear()
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/libraries/core/src/main/java/com/chesire/nekome/core/models/ErrorDomain.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.core.models
2 |
3 | import java.net.HttpURLConnection.HTTP_BAD_REQUEST
4 | import java.net.HttpURLConnection.HTTP_FORBIDDEN
5 | import java.net.HttpURLConnection.HTTP_NO_CONTENT
6 | import java.net.HttpURLConnection.HTTP_UNAUTHORIZED
7 | import java.net.HttpURLConnection.HTTP_UNAVAILABLE
8 |
9 | data class ErrorDomain(
10 | val message: String,
11 | val code: Int
12 | ) {
13 | companion object {
14 | val badRequest = ErrorDomain("", HTTP_BAD_REQUEST)
15 | val couldNotReach = ErrorDomain("Could not reach service", HTTP_UNAVAILABLE)
16 | val couldNotRefresh = ErrorDomain("Could not refresh auth", HTTP_FORBIDDEN)
17 | val emptyResponse = ErrorDomain("Response body is null", HTTP_NO_CONTENT)
18 | val invalidCredentials = ErrorDomain("", HTTP_UNAUTHORIZED)
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/libraries/database/src/test/java/com/chesire/nekome/database/converters/UserSeriesStatusConverterTests.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.database.converters
2 |
3 | import com.chesire.nekome.core.flags.UserSeriesStatus
4 | import org.junit.Assert.assertEquals
5 | import org.junit.Test
6 |
7 | class UserSeriesStatusConverterTests {
8 | @Test
9 | fun `fromUserSeriesStatus converts to enum name from UserSeriesStatus`() {
10 | val converter = UserSeriesStatusConverter()
11 | UserSeriesStatus.values().forEach {
12 | assertEquals(it.name, converter.fromUserSeriesStatus(it))
13 | }
14 | }
15 |
16 | @Test
17 | fun `toUserSeriesStatus converts to UserSeriesStatus from name`() {
18 | val converter = UserSeriesStatusConverter()
19 | UserSeriesStatus.values().forEach {
20 | assertEquals(it, converter.toUserSeriesStatus(it.name))
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/core/preferences/src/main/java/com/chesire/nekome/core/preferences/flags/TitleLanguage.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.core.preferences.flags
2 |
3 | import androidx.annotation.StringRes
4 | import com.chesire.nekome.resources.StringResource
5 |
6 | /**
7 | * Options available for the language used to display the titles.
8 | */
9 | enum class TitleLanguage(val index: Int, @StringRes val stringId: Int, val key: String) {
10 | Canonical(0, StringResource.title_language_canonical, ""),
11 | English(1, StringResource.title_language_english, "en"),
12 | Romaji(2, StringResource.title_language_romaji, "en_jp"),
13 | Japanese(3, StringResource.title_language_japanese, "ja_jp");
14 |
15 | companion object {
16 |
17 | /**
18 | * Get [TitleLanguage] for its given [index].
19 | */
20 | fun forIndex(index: Int): TitleLanguage = values().find { it.index == index } ?: Canonical
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/libraries/kitsu/user/src/main/java/com/chesire/nekome/kitsu/user/UserItemDtoMapper.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.kitsu.user
2 |
3 | import com.chesire.nekome.core.flags.Service
4 | import com.chesire.nekome.core.models.ImageModel
5 | import com.chesire.nekome.datasource.user.UserDomain
6 | import com.chesire.nekome.kitsu.user.dto.UserItemDto
7 | import javax.inject.Inject
8 |
9 | /**
10 | * Provides ability to map instances of [UserItemDto] into [UserDomain].
11 | */
12 | class UserItemDtoMapper @Inject constructor() {
13 |
14 | /**
15 | * Converts an instance of [UserItemDto] into a [UserDomain].
16 | */
17 | fun toUserDomain(input: UserItemDto) =
18 | UserDomain(
19 | input.id,
20 | input.attributes.name,
21 | input.attributes.avatar ?: ImageModel.empty,
22 | input.attributes.coverImage ?: ImageModel.empty,
23 | Service.Kitsu
24 | )
25 | }
26 |
--------------------------------------------------------------------------------
/core/preferences/src/main/java/com/chesire/nekome/core/preferences/ext/SharedPreferenceExtensions.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.core.preferences.ext
2 |
3 | import android.content.SharedPreferences
4 | import androidx.core.content.edit
5 |
6 | /**
7 | * Migrates keys to a new [SharedPreferences] instance.
8 | * Will only migrate keys that have a value of type [String].
9 | */
10 | fun SharedPreferences.migrateTo(encryptedPreferences: SharedPreferences) {
11 | // If the current list is empty, no need to migrate
12 | if (all.isEmpty()) {
13 | return
14 | }
15 |
16 | val migrated = mutableListOf()
17 | all.forEach { (key, value) ->
18 | if (value is String) {
19 | migrated.add(key)
20 | encryptedPreferences.edit {
21 | putString(key, value)
22 | }
23 | }
24 | }
25 | edit {
26 | migrated.forEach(::remove)
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/libraries/datasource/auth/src/main/java/com/chesire/nekome/datasource/auth/local/AuthProvider.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.datasource.auth.local
2 |
3 | import javax.inject.Inject
4 |
5 | /**
6 | * Provides authorization for Kitsu access.
7 | */
8 | class AuthProvider @Inject constructor(private val auth: LocalAuth) {
9 |
10 | /**
11 | * Retrieve or set the current access token.
12 | */
13 | var accessToken: String
14 | get() = auth.accessToken
15 | set(value) {
16 | auth.accessToken = value
17 | }
18 |
19 | /**
20 | * Retrieve or set the refresh token used to get a new access token.
21 | */
22 | var refreshToken: String
23 | get() = auth.refreshToken
24 | set(value) {
25 | auth.refreshToken = value
26 | }
27 |
28 | /**
29 | * Clears out the current auth credentials.
30 | */
31 | fun clearAuth() = auth.clear()
32 | }
33 |
--------------------------------------------------------------------------------
/core/preferences/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.library)
3 | alias(libs.plugins.google.dagger.hilt.android)
4 | alias(libs.plugins.google.devtools.ksp)
5 | alias(libs.plugins.kotlin.android)
6 | }
7 |
8 | android {
9 | namespace = "com.chesire.nekome.core.preferences"
10 | compileSdk = libs.versions.sdk.get().toInt()
11 |
12 | defaultConfig {
13 | minSdk = 21
14 |
15 | consumerProguardFiles("consumer-rules.pro")
16 | }
17 | }
18 |
19 | dependencies {
20 | implementation(project(":core:resources"))
21 | implementation(project(":libraries:core"))
22 |
23 | implementation(libs.androidx.appcompat)
24 | implementation(libs.androidx.datastore.preferences)
25 | implementation(libs.google.hilt.android)
26 | implementation(libs.squareup.moshi)
27 | ksp(libs.google.hilt.android.compiler)
28 |
29 | testImplementation(libs.junit)
30 | testImplementation(libs.mockk)
31 | }
32 |
--------------------------------------------------------------------------------
/libraries/kitsu/trending/src/main/java/com/chesire/nekome/kitsu/trending/dto/TrendingItemDto.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.kitsu.trending.dto
2 |
3 | import com.chesire.nekome.core.flags.SeriesType
4 | import com.chesire.nekome.core.models.ImageModel
5 | import com.squareup.moshi.Json
6 | import com.squareup.moshi.JsonClass
7 |
8 | /**
9 | * DTO from the Kitsu trending endpoint.
10 | */
11 | @JsonClass(generateAdapter = true)
12 | data class TrendingItemDto(
13 | @Json(name = "id")
14 | val id: Int,
15 | @Json(name = "type")
16 | val type: SeriesType,
17 | @Json(name = "attributes")
18 | val attributes: Attributes
19 | ) {
20 | /**
21 | * Attributes of the trending item.
22 | */
23 | @JsonClass(generateAdapter = true)
24 | data class Attributes(
25 | @Json(name = "canonicalTitle")
26 | val canonicalTitle: String,
27 | @Json(name = "posterImage")
28 | val posterImage: ImageModel?
29 | )
30 | }
31 |
--------------------------------------------------------------------------------
/libraries/datasource/series/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.library)
3 | alias(libs.plugins.kotlin.android)
4 | }
5 |
6 | android {
7 | namespace = "com.chesire.nekome.datasource.series"
8 | compileSdk = libs.versions.sdk.get().toInt()
9 |
10 | defaultConfig {
11 | minSdk = 21
12 |
13 | consumerProguardFiles("consumer-rules.pro")
14 | }
15 | }
16 |
17 | dependencies {
18 | implementation(project(":libraries:core"))
19 | implementation(project(":libraries:database"))
20 |
21 | implementation(libs.google.hilt.android)
22 | implementation(libs.kotlin.coroutines.android)
23 | implementation(libs.kotlin.coroutines.core)
24 | implementation(libs.kotlin.result)
25 | implementation(libs.timber)
26 |
27 | testImplementation(project(":testing"))
28 | testImplementation(libs.androidx.arch.core.testing)
29 | testImplementation(libs.junit)
30 | testImplementation(libs.mockk)
31 | }
32 |
--------------------------------------------------------------------------------
/libraries/datasource/series/src/main/java/com/chesire/nekome/datasource/series/SeriesDomain.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.datasource.series
2 |
3 | import com.chesire.nekome.core.flags.SeriesStatus
4 | import com.chesire.nekome.core.flags.SeriesType
5 | import com.chesire.nekome.core.flags.Subtype
6 | import com.chesire.nekome.core.flags.UserSeriesStatus
7 | import com.chesire.nekome.core.models.ImageModel
8 |
9 | /**
10 | * Domain object for a single Series item.
11 | */
12 | data class SeriesDomain(
13 | val id: Int,
14 | val userId: Int,
15 | val type: SeriesType,
16 | val subtype: Subtype,
17 | val slug: String,
18 | val title: String,
19 | val otherTitles: Map,
20 | val seriesStatus: SeriesStatus,
21 | val userSeriesStatus: UserSeriesStatus,
22 | val progress: Int,
23 | val totalLength: Int,
24 | val rating: Int,
25 | val posterImage: ImageModel,
26 | val startDate: String,
27 | val endDate: String
28 | )
29 |
--------------------------------------------------------------------------------
/libraries/kitsu/search/src/main/java/com/chesire/nekome/kitsu/search/SearchItemDtoMapper.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.kitsu.search
2 |
3 | import com.chesire.nekome.core.models.ImageModel
4 | import com.chesire.nekome.datasource.search.SearchDomain
5 | import com.chesire.nekome.kitsu.search.dto.SearchItemDto
6 | import javax.inject.Inject
7 |
8 | /**
9 | * Provides ability to map instances of [SearchItemDto] into [SearchDomain].
10 | */
11 | class SearchItemDtoMapper @Inject constructor() {
12 |
13 | /**
14 | * Converts an instance of [SearchItemDto] into a [SearchDomain].
15 | */
16 | fun toSearchDomain(input: SearchItemDto) =
17 | SearchDomain(
18 | input.id,
19 | input.type,
20 | input.attributes.synopsis,
21 | input.attributes.canonicalTitle,
22 | input.attributes.titles,
23 | input.attributes.subtype,
24 | input.attributes.posterImage ?: ImageModel.empty
25 | )
26 | }
27 |
--------------------------------------------------------------------------------
/features/login/src/main/java/com/chesire/nekome/app/login/credentials/ui/UIState.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.app.login.credentials.ui
2 |
3 | import androidx.annotation.StringRes
4 |
5 | data class UIState(
6 | val username: String,
7 | val hasUsernameError: Boolean,
8 | val password: String,
9 | val hasPasswordError: Boolean,
10 | val isPerformingLogin: Boolean,
11 | val loginButtonEnabled: Boolean,
12 | @StringRes val errorSnackbarMessage: Int?,
13 | val navigateScreenEvent: Boolean?
14 | ) {
15 | companion object {
16 | val empty: UIState
17 | get() = UIState(
18 | username = "",
19 | hasUsernameError = false,
20 | password = "",
21 | hasPasswordError = false,
22 | isPerformingLogin = false,
23 | loginButtonEnabled = false,
24 | errorSnackbarMessage = null,
25 | navigateScreenEvent = null
26 | )
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/libraries/core/src/main/res/drawable/ic_blackcat.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/libraries/database/src/main/java/com/chesire/nekome/database/converters/ImageModelConverter.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.database.converters
2 |
3 | import androidx.room.TypeConverter
4 | import com.chesire.nekome.core.models.ImageModel
5 | import com.chesire.nekome.core.models.ImageModelJsonAdapter
6 | import com.squareup.moshi.Moshi
7 |
8 | /**
9 | * Converter for [ImageModel] -> [String].
10 | *
11 | * For saving an [ImageModel] into the database.
12 | */
13 | class ImageModelConverter {
14 | private val adapter: ImageModelJsonAdapter by lazy {
15 | ImageModelJsonAdapter(Moshi.Builder().build())
16 | }
17 |
18 | /**
19 | * Converts an [ImageModel] into a [String].
20 | */
21 | @TypeConverter
22 | fun fromImageModel(model: ImageModel): String = adapter.toJson(model)
23 |
24 | /**
25 | * Converts a [String] into a [ImageModel].
26 | */
27 | @TypeConverter
28 | fun toImageModel(model: String): ImageModel = adapter.fromJson(model) ?: ImageModel.empty
29 | }
30 |
--------------------------------------------------------------------------------
/libraries/kitsu/auth/src/main/java/com/chesire/nekome/kitsu/auth/KitsuAuthService.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.kitsu.auth
2 |
3 | import com.chesire.nekome.kitsu.auth.dto.AuthResponseDto
4 | import com.chesire.nekome.kitsu.auth.dto.LoginRequestDto
5 | import com.chesire.nekome.kitsu.auth.dto.RefreshTokenRequestDto
6 | import retrofit2.Response
7 | import retrofit2.http.Body
8 | import retrofit2.http.POST
9 |
10 | /**
11 | * Constructed using Retrofit to interface with the Kitsu API for queries related to authenticating.
12 | */
13 | interface KitsuAuthService {
14 |
15 | /**
16 | * Performs a login request with the API.
17 | */
18 | @POST("api/oauth/token")
19 | suspend fun loginAsync(@Body body: LoginRequestDto): Response
20 |
21 | /**
22 | * Performs a refresh token request with the API.
23 | */
24 | @POST("api/oauth/token")
25 | suspend fun refreshAccessTokenAsync(
26 | @Body body: RefreshTokenRequestDto
27 | ): Response
28 | }
29 |
--------------------------------------------------------------------------------
/Dangerfile:
--------------------------------------------------------------------------------
1 | # Make it more obvious that a PR is a work in progress and shouldn't be merged yet
2 | warn("PR is classed as Work in Progress") if github.pr_title.include? "[WIP]"
3 |
4 | # Warn when there is a big PR
5 | warn("Big PR") if git.lines_of_code > 500
6 |
7 | # General
8 | failure "Please provide a summary in the Pull Request description" if github.pr_body.length < 5
9 | warn "This PR does not have any assignees yet." unless github.pr_json["assignee"]
10 | can_merge = github.pr_json["mergeable"]
11 | warn("This PR cannot be merged yet.", sticky: false) unless can_merge
12 | github.dismiss_out_of_range_messages
13 |
14 | # AndroidLint
15 | lint_dir = "**/reports/lint-results*.xml"
16 | Dir[lint_dir].each do |file_name|
17 | android_lint.skip_gradle_task = true
18 | android_lint.filtering = true
19 | android_lint.report_file = file_name
20 | android_lint.lint(inline_mode: true)
21 | end
22 |
23 | # CheckstyleFormat
24 | checkstyle_format.base_path = Dir.pwd
25 | checkstyle_format.report 'build/reports/detekt/detekt.xml'
26 |
--------------------------------------------------------------------------------
/libraries/core/consumer-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.kts.
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
22 |
23 | # Keep the flags that Moshi needs to generate adapters
24 | #noinspection ShrinkerUnresolvedReference
25 | -keep class com.chesire.nekome.core.flags.** { *; }
26 |
--------------------------------------------------------------------------------
/libraries/datasource/user/src/main/java/com/chesire/nekome/datasource/user/UserMapper.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.datasource.user
2 |
3 | import com.chesire.nekome.database.entity.UserEntity
4 | import javax.inject.Inject
5 |
6 | /**
7 | * Provides ability to map instances of [UserDomain].
8 | */
9 | class UserMapper @Inject constructor() {
10 |
11 | /**
12 | * Converts an instance of [UserDomain] into an instance of [UserEntity].
13 | */
14 | fun toUserEntity(input: UserDomain) =
15 | UserEntity(
16 | input.userId,
17 | input.name,
18 | input.avatar,
19 | input.coverImage,
20 | input.service
21 | )
22 |
23 | /**
24 | * Converts an instance of [UserEntity] into an instance of [UserDomain].
25 | */
26 | fun toUserDomain(input: UserEntity) =
27 | UserDomain(
28 | input.userId,
29 | input.name,
30 | input.avatar,
31 | input.coverImage,
32 | input.service
33 | )
34 | }
35 |
--------------------------------------------------------------------------------
/libraries/datasource/auth/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.library)
3 | alias(libs.plugins.kotlin.android)
4 | }
5 |
6 | android {
7 | namespace = "com.chesire.nekome.datasource.auth"
8 | compileSdk = libs.versions.sdk.get().toInt()
9 |
10 | defaultConfig {
11 | minSdk = 21
12 |
13 | consumerProguardFiles("consumer-rules.pro")
14 | }
15 | }
16 |
17 | dependencies {
18 | implementation(project(":core:preferences"))
19 | implementation(project(":libraries:core"))
20 |
21 | implementation(libs.androidx.core)
22 | implementation(libs.androidx.security.crypto)
23 | implementation(libs.google.hilt.android)
24 | implementation(libs.kotlin.coroutines.android)
25 | implementation(libs.kotlin.coroutines.core)
26 | implementation(libs.kotlin.result)
27 | implementation(libs.squareup.retrofit2)
28 | implementation(libs.timber)
29 |
30 | testImplementation(project(":testing"))
31 | testImplementation(libs.junit)
32 | testImplementation(libs.mockk)
33 | }
34 |
--------------------------------------------------------------------------------
/libraries/core/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.library)
3 | alias(libs.plugins.google.devtools.ksp)
4 | alias(libs.plugins.kotlin.android)
5 | }
6 |
7 | android {
8 | namespace = "com.chesire.nekome.core"
9 | compileSdk = libs.versions.sdk.get().toInt()
10 |
11 | defaultConfig {
12 | minSdk = 21
13 |
14 | consumerProguardFiles("consumer-rules.pro")
15 | }
16 | }
17 |
18 | dependencies {
19 | implementation(project(":core:resources"))
20 |
21 | implementation(libs.androidx.appcompat)
22 | implementation(libs.androidx.browser)
23 | implementation(libs.androidx.core)
24 | implementation(libs.androidx.room.runtime)
25 | implementation(libs.google.hilt.android)
26 | implementation(libs.google.material)
27 | implementation(libs.squareup.moshi)
28 | ksp(libs.google.hilt.android.compiler)
29 | ksp(libs.squareup.moshi.codegen)
30 |
31 | testImplementation(project(":testing"))
32 | testImplementation(libs.junit)
33 | testImplementation(libs.mockk)
34 | }
35 |
--------------------------------------------------------------------------------
/libraries/database/src/main/java/com/chesire/nekome/database/converters/MapConverter.kt:
--------------------------------------------------------------------------------
1 | package com.chesire.nekome.database.converters
2 |
3 | import androidx.room.TypeConverter
4 | import com.squareup.moshi.JsonAdapter
5 | import com.squareup.moshi.Moshi
6 | import com.squareup.moshi.Types
7 |
8 | /**
9 | * Converter for [Map] -> [String].
10 | *
11 | * For saving the otherTitles into the database.
12 | */
13 | class MapConverter {
14 |
15 | private val adapter: JsonAdapter