Skip to content

Commit

Permalink
Implement content language change support (#224)
Browse files Browse the repository at this point in the history
  • Loading branch information
AfigAliyev committed Nov 8, 2022
1 parent d9db314 commit df79de5
Show file tree
Hide file tree
Showing 55 changed files with 981 additions and 185 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright 2022 Maximillian Leonov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.maximillianleonov.cinemax.di

import android.content.Context
import androidx.datastore.preferences.preferencesDataStore
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
object DataStoreModule {
@Provides
@Singleton
fun providePreferencesDataStore(@ApplicationContext context: Context) =
context.preferencesDataStore
}

private const val PREFERENCES_NAME = "preferences"
private val Context.preferencesDataStore by preferencesDataStore(name = PREFERENCES_NAME)
2 changes: 2 additions & 0 deletions app/src/main/res/values-night/themes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,7 @@
<item name="android:navigationBarColor">@color/primaryDark</item>
<item name="android:windowLightStatusBar" tools:targetApi="23">false</item>
<item name="android:windowLightNavigationBar" tools:targetApi="27">false</item>
<item name="android:dialogTheme">@style/Theme.Cinemax.Dialog</item>
</style>

</resources>
6 changes: 6 additions & 0 deletions app/src/main/res/values/themes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,17 @@
<item name="android:navigationBarColor">@color/primaryLight</item>
<item name="android:windowLightStatusBar" tools:targetApi="23">true</item>
<item name="android:windowLightNavigationBar" tools:targetApi="27">true</item>
<item name="android:dialogTheme">@style/Theme.Cinemax.Dialog</item>
</style>

<style name="Theme.Cinemax.Splash" parent="Theme.SplashScreen">
<item name="windowSplashScreenBackground">@color/primaryDark</item>
<item name="windowSplashScreenAnimatedIcon">@drawable/ic_splash</item>
<item name="postSplashScreenTheme">@style/Theme.Cinemax</item>
</style>

<style name="Theme.Cinemax.Dialog" parent="android:Theme.Material.Light.Dialog">
<item name="android:backgroundDimEnabled">false</item>
</style>

</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,16 @@ import com.maximillianleonov.cinemax.core.database.model.common.MediaType
import com.maximillianleonov.cinemax.core.database.model.movie.MovieEntity
import com.maximillianleonov.cinemax.core.database.model.movie.MovieRemoteKeyEntity
import com.maximillianleonov.cinemax.core.database.source.MovieDatabaseDataSource
import com.maximillianleonov.cinemax.core.datastore.PreferencesDataStoreDataSource
import com.maximillianleonov.cinemax.core.network.source.MovieNetworkDataSource
import kotlinx.coroutines.flow.first
import java.io.IOException

@OptIn(ExperimentalPagingApi::class)
class MovieRemoteMediator(
private val databaseDataSource: MovieDatabaseDataSource,
private val networkDataSource: MovieNetworkDataSource,
private val preferencesDataStoreDataSource: PreferencesDataStoreDataSource,
private val mediaType: MediaType.Movie
) : RemoteMediator<Int, MovieEntity>() {
@Suppress("ReturnCount")
Expand Down Expand Up @@ -71,6 +74,7 @@ class MovieRemoteMediator(

val response = networkDataSource.getByMediaType(
mediaType = mediaType.asNetworkMediaType(),
language = preferencesDataStoreDataSource.getContentLanguage().first(),
page = currentPage
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,18 @@ import com.maximillianleonov.cinemax.core.common.result.isFailure
import com.maximillianleonov.cinemax.core.common.result.isSuccess
import com.maximillianleonov.cinemax.core.data.mapper.asMovieModel
import com.maximillianleonov.cinemax.core.data.util.Constants
import com.maximillianleonov.cinemax.core.datastore.PreferencesDataStoreDataSource
import com.maximillianleonov.cinemax.core.domain.model.MovieModel
import com.maximillianleonov.cinemax.core.network.model.movie.NetworkMovie
import com.maximillianleonov.cinemax.core.network.source.MovieNetworkDataSource
import com.maximillianleonov.cinemax.core.network.util.DEFAULT_PAGE
import kotlinx.coroutines.flow.first
import java.io.IOException

class SearchMoviePagingSource(
private val query: String,
private val networkDataSource: MovieNetworkDataSource
private val networkDataSource: MovieNetworkDataSource,
private val preferencesDataStoreDataSource: PreferencesDataStoreDataSource
) : PagingSource<Int, MovieModel>() {

override fun getRefreshKey(state: PagingState<Int, MovieModel>) = state.anchorPosition
Expand All @@ -40,7 +43,11 @@ class SearchMoviePagingSource(
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, MovieModel> {
return try {
val currentPage = params.key ?: DEFAULT_PAGE
val response = networkDataSource.search(query = query, page = currentPage)
val response = networkDataSource.search(
query = query,
language = preferencesDataStoreDataSource.getContentLanguage().first(),
page = currentPage
)

when {
response.isSuccess() -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,18 @@ import com.maximillianleonov.cinemax.core.common.result.isFailure
import com.maximillianleonov.cinemax.core.common.result.isSuccess
import com.maximillianleonov.cinemax.core.data.mapper.asTvShowModel
import com.maximillianleonov.cinemax.core.data.util.Constants
import com.maximillianleonov.cinemax.core.datastore.PreferencesDataStoreDataSource
import com.maximillianleonov.cinemax.core.domain.model.TvShowModel
import com.maximillianleonov.cinemax.core.network.model.tvshow.NetworkTvShow
import com.maximillianleonov.cinemax.core.network.source.TvShowNetworkDataSource
import com.maximillianleonov.cinemax.core.network.util.DEFAULT_PAGE
import kotlinx.coroutines.flow.first
import java.io.IOException

class SearchTvShowPagingSource(
private val query: String,
private val networkDataSource: TvShowNetworkDataSource
private val networkDataSource: TvShowNetworkDataSource,
private val preferencesDataStoreDataSource: PreferencesDataStoreDataSource
) : PagingSource<Int, TvShowModel>() {

override fun getRefreshKey(state: PagingState<Int, TvShowModel>) = state.anchorPosition
Expand All @@ -40,7 +43,11 @@ class SearchTvShowPagingSource(
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, TvShowModel> {
return try {
val currentPage = params.key ?: DEFAULT_PAGE
val response = networkDataSource.search(query = query, page = currentPage)
val response = networkDataSource.search(
query = query,
language = preferencesDataStoreDataSource.getContentLanguage().first(),
page = currentPage
)

when {
response.isSuccess() -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,16 @@ import com.maximillianleonov.cinemax.core.database.model.common.MediaType
import com.maximillianleonov.cinemax.core.database.model.tvshow.TvShowEntity
import com.maximillianleonov.cinemax.core.database.model.tvshow.TvShowRemoteKeyEntity
import com.maximillianleonov.cinemax.core.database.source.TvShowDatabaseDataSource
import com.maximillianleonov.cinemax.core.datastore.PreferencesDataStoreDataSource
import com.maximillianleonov.cinemax.core.network.source.TvShowNetworkDataSource
import kotlinx.coroutines.flow.first
import java.io.IOException

@OptIn(ExperimentalPagingApi::class)
class TvShowRemoteMediator(
private val databaseDataSource: TvShowDatabaseDataSource,
private val networkDataSource: TvShowNetworkDataSource,
private val preferencesDataStoreDataSource: PreferencesDataStoreDataSource,
private val mediaType: MediaType.TvShow
) : RemoteMediator<Int, TvShowEntity>() {
@Suppress("ReturnCount")
Expand Down Expand Up @@ -71,6 +74,7 @@ class TvShowRemoteMediator(

val response = networkDataSource.getByMediaType(
mediaType = mediaType.asNetworkMediaType(),
language = preferencesDataStoreDataSource.getContentLanguage().first(),
page = currentPage
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,22 @@ import com.maximillianleonov.cinemax.core.data.mapper.asMovieDetailsModel
import com.maximillianleonov.cinemax.core.data.mapper.listMap
import com.maximillianleonov.cinemax.core.database.source.MovieDetailsDatabaseDataSource
import com.maximillianleonov.cinemax.core.database.source.WishlistDatabaseDataSource
import com.maximillianleonov.cinemax.core.datastore.PreferencesDataStoreDataSource
import com.maximillianleonov.cinemax.core.domain.model.MovieDetailsModel
import com.maximillianleonov.cinemax.core.domain.repository.MovieDetailsRepository
import com.maximillianleonov.cinemax.core.network.common.networkBoundResource
import com.maximillianleonov.cinemax.core.network.model.movie.NetworkMovieDetails
import com.maximillianleonov.cinemax.core.network.source.MovieDetailsNetworkDataSource
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import javax.inject.Inject

class MovieDetailsRepositoryImpl @Inject constructor(
private val databaseDataSource: MovieDetailsDatabaseDataSource,
private val networkDataSource: MovieDetailsNetworkDataSource,
private val wishlistDatabaseDataSource: WishlistDatabaseDataSource
private val wishlistDatabaseDataSource: WishlistDatabaseDataSource,
private val preferencesDataStoreDataSource: PreferencesDataStoreDataSource
) : MovieDetailsRepository {
override fun getById(id: Int): Flow<CinemaxResult<MovieDetailsModel?>> = networkBoundResource(
query = {
Expand All @@ -44,7 +47,12 @@ class MovieDetailsRepositoryImpl @Inject constructor(
)
}
},
fetch = { networkDataSource.getById(id) },
fetch = {
networkDataSource.getById(
id = id,
language = preferencesDataStoreDataSource.getContentLanguage().first()
)
},
saveFetchResult = { response ->
databaseDataSource.deleteAndInsert(response.asMovieDetailsEntity())
}
Expand All @@ -59,7 +67,12 @@ class MovieDetailsRepositoryImpl @Inject constructor(
)
}
},
fetch = { networkDataSource.getByIds(ids) },
fetch = {
networkDataSource.getByIds(
ids = ids,
language = preferencesDataStoreDataSource.getContentLanguage().first()
)
},
saveFetchResult = { response ->
databaseDataSource.deleteAndInsertAll(
response.map(NetworkMovieDetails::asMovieDetailsEntity)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,21 @@ import com.maximillianleonov.cinemax.core.data.paging.SearchMoviePagingSource
import com.maximillianleonov.cinemax.core.data.util.defaultPagingConfig
import com.maximillianleonov.cinemax.core.database.model.movie.MovieEntity
import com.maximillianleonov.cinemax.core.database.source.MovieDatabaseDataSource
import com.maximillianleonov.cinemax.core.datastore.PreferencesDataStoreDataSource
import com.maximillianleonov.cinemax.core.domain.model.MediaTypeModel
import com.maximillianleonov.cinemax.core.domain.model.MovieModel
import com.maximillianleonov.cinemax.core.domain.repository.MovieRepository
import com.maximillianleonov.cinemax.core.network.common.networkBoundResource
import com.maximillianleonov.cinemax.core.network.source.MovieNetworkDataSource
import com.maximillianleonov.cinemax.core.network.util.PAGE_SIZE
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import javax.inject.Inject

class MovieRepositoryImpl @Inject constructor(
private val databaseDataSource: MovieDatabaseDataSource,
private val networkDataSource: MovieNetworkDataSource
private val networkDataSource: MovieNetworkDataSource,
private val preferencesDataStoreDataSource: PreferencesDataStoreDataSource
) : MovieRepository {
override fun getByMediaType(
mediaTypeModel: MediaTypeModel.Movie
Expand All @@ -55,7 +58,12 @@ class MovieRepositoryImpl @Inject constructor(
pageSize = PAGE_SIZE
).listMap(MovieEntity::asMovieModel)
},
fetch = { networkDataSource.getByMediaType(mediaType.asNetworkMediaType()) },
fetch = {
networkDataSource.getByMediaType(
mediaType = mediaType.asNetworkMediaType(),
language = preferencesDataStoreDataSource.getContentLanguage().first()
)
},
saveFetchResult = { response ->
databaseDataSource.deleteByMediaTypeAndInsertAll(
mediaType = mediaType,
Expand All @@ -72,13 +80,24 @@ class MovieRepositoryImpl @Inject constructor(
val mediaType = mediaTypeModel.asMediaType()
return Pager(
config = defaultPagingConfig,
remoteMediator = MovieRemoteMediator(databaseDataSource, networkDataSource, mediaType),
remoteMediator = MovieRemoteMediator(
databaseDataSource = databaseDataSource,
networkDataSource = networkDataSource,
preferencesDataStoreDataSource = preferencesDataStoreDataSource,
mediaType = mediaType
),
pagingSourceFactory = { databaseDataSource.getPagingByMediaType(mediaType) }
).flow.pagingMap(MovieEntity::asMovieModel)
}

override fun search(query: String): Flow<PagingData<MovieModel>> = Pager(
config = defaultPagingConfig,
pagingSourceFactory = { SearchMoviePagingSource(query, networkDataSource) }
pagingSourceFactory = {
SearchMoviePagingSource(
query = query,
networkDataSource = networkDataSource,
preferencesDataStoreDataSource = preferencesDataStoreDataSource
)
}
).flow
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,33 @@

package com.maximillianleonov.cinemax.core.data.repository

import com.maximillianleonov.cinemax.core.data.util.titlecase
import com.maximillianleonov.cinemax.core.database.source.SettingsDatabaseDataSource
import com.maximillianleonov.cinemax.core.datastore.PreferencesDataStoreDataSource
import com.maximillianleonov.cinemax.core.domain.repository.SettingsRepository
import java.util.Locale
import javax.inject.Inject

class SettingsRepositoryImpl @Inject constructor(
databaseDataSource: SettingsDatabaseDataSource
databaseDataSource: SettingsDatabaseDataSource,
private val preferencesDataStoreDataSource: PreferencesDataStoreDataSource
) : SettingsRepository {
override val repoUrl = databaseDataSource.repoUrl
override val privacyPolicyUrl = databaseDataSource.privacyPolicyUrl
override val version = databaseDataSource.version

override fun getAvailableLanguages(): Map<String, String> {
val languages = mutableMapOf<String, String>()
Locale.getAvailableLocales()
.sortedBy(Locale::getDisplayLanguage)
.forEach { locale ->
languages[locale.language] = locale.displayLanguage.titlecase()
}
return languages
}

override fun getContentLanguage() = preferencesDataStoreDataSource.getContentLanguage()
override suspend fun setContentLanguage(contentLanguage: String) {
preferencesDataStoreDataSource.setContentLanguage(contentLanguage)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,22 @@ import com.maximillianleonov.cinemax.core.data.mapper.asTvShowDetailsModel
import com.maximillianleonov.cinemax.core.data.mapper.listMap
import com.maximillianleonov.cinemax.core.database.source.TvShowDetailsDatabaseDataSource
import com.maximillianleonov.cinemax.core.database.source.WishlistDatabaseDataSource
import com.maximillianleonov.cinemax.core.datastore.PreferencesDataStoreDataSource
import com.maximillianleonov.cinemax.core.domain.model.TvShowDetailsModel
import com.maximillianleonov.cinemax.core.domain.repository.TvShowDetailsRepository
import com.maximillianleonov.cinemax.core.network.common.networkBoundResource
import com.maximillianleonov.cinemax.core.network.model.tvshow.NetworkTvShowDetails
import com.maximillianleonov.cinemax.core.network.source.TvShowDetailsNetworkDataSource
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import javax.inject.Inject

class TvShowDetailsRepositoryImpl @Inject constructor(
private val databaseDataSource: TvShowDetailsDatabaseDataSource,
private val networkDataSource: TvShowDetailsNetworkDataSource,
private val wishlistDatabaseDataSource: WishlistDatabaseDataSource
private val wishlistDatabaseDataSource: WishlistDatabaseDataSource,
private val preferencesDataStoreDataSource: PreferencesDataStoreDataSource
) : TvShowDetailsRepository {
override fun getById(id: Int): Flow<CinemaxResult<TvShowDetailsModel?>> = networkBoundResource(
query = {
Expand All @@ -44,7 +47,12 @@ class TvShowDetailsRepositoryImpl @Inject constructor(
)
}
},
fetch = { networkDataSource.getById(id) },
fetch = {
networkDataSource.getById(
id = id,
language = preferencesDataStoreDataSource.getContentLanguage().first()
)
},
saveFetchResult = { response ->
databaseDataSource.deleteAndInsert(response.asTvShowDetailsEntity())
}
Expand All @@ -59,7 +67,12 @@ class TvShowDetailsRepositoryImpl @Inject constructor(
)
}
},
fetch = { networkDataSource.getByIds(ids) },
fetch = {
networkDataSource.getByIds(
ids = ids,
language = preferencesDataStoreDataSource.getContentLanguage().first()
)
},
saveFetchResult = { response ->
databaseDataSource.deleteAndInsertAll(
response.map(NetworkTvShowDetails::asTvShowDetailsEntity)
Expand Down
Loading

0 comments on commit df79de5

Please sign in to comment.