0.1.0 - Basic Setting
This commit is contained in:
parent
34d3e5c38a
commit
29a75aa18a
32 changed files with 744 additions and 16 deletions
|
|
@ -3,6 +3,7 @@
|
|||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<application
|
||||
android:name=".util.BookApplication"
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
|
|
|
|||
|
|
@ -3,14 +3,35 @@ package com.acclorite.books_history
|
|||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.datastore.core.DataStore
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.acclorite.books_history.data.use_case.ChangeLanguageImpl
|
||||
import com.acclorite.books_history.presentation.MainViewModel
|
||||
import com.acclorite.books_history.presentation.NavigationHost
|
||||
import com.acclorite.books_history.presentation.Screen
|
||||
import com.acclorite.books_history.ui.theme.BooksHistoryResurrectionTheme
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
class MainActivity : ComponentActivity() {
|
||||
private val mainViewModel: MainViewModel by viewModels()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
setContent {
|
||||
BooksHistoryResurrectionTheme {
|
||||
NavigationHost(startScreen = Screen.LIBRARY, this) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
package com.acclorite.books_history.data.di
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import androidx.datastore.core.DataStore
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import androidx.datastore.preferences.preferencesDataStore
|
||||
import androidx.room.Room
|
||||
import com.acclorite.books_history.data.local.room.BookDao
|
||||
import com.acclorite.books_history.data.local.room.BookDatabase
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
object AppModule {
|
||||
|
||||
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideBookDao(app: Application): BookDao {
|
||||
return Room.databaseBuilder(
|
||||
app,
|
||||
BookDatabase::class.java,
|
||||
"book_db"
|
||||
).build().dao
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package com.acclorite.books_history.data.di
|
||||
|
||||
import com.acclorite.books_history.data.local.data_store.DataStore
|
||||
import com.acclorite.books_history.data.local.data_store.DataStoreImpl
|
||||
import com.acclorite.books_history.data.repository.BookRepositoryImpl
|
||||
import com.acclorite.books_history.domain.repository.BookRepository
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
abstract class RepositoryModule {
|
||||
|
||||
@Binds
|
||||
abstract fun bindDataStore(
|
||||
dataStoreImpl: DataStoreImpl
|
||||
): DataStore
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
abstract fun bindBookRepository(
|
||||
bookRepositoryImpl: BookRepositoryImpl
|
||||
): BookRepository
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package com.acclorite.books_history.data.di
|
||||
|
||||
import com.acclorite.books_history.data.use_case.ChangeLanguageImpl
|
||||
import com.acclorite.books_history.domain.use_case.ChangeLanguage
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
abstract class UseCaseModule {
|
||||
|
||||
@Binds
|
||||
abstract fun bindChangeLanguage(
|
||||
changeLanguageImpl: ChangeLanguageImpl
|
||||
): ChangeLanguage
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package com.acclorite.books_history.data.local.data_store
|
||||
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface DataStore {
|
||||
suspend fun <T> getData (key: Preferences.Key<T>, defaultValue: T): Flow<T>
|
||||
suspend fun <T> putData (key: Preferences.Key<T>, value: T)
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package com.acclorite.books_history.data.local.data_store
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import androidx.datastore.preferences.core.edit
|
||||
import androidx.datastore.preferences.core.emptyPreferences
|
||||
import androidx.datastore.preferences.preferencesDataStore
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.map
|
||||
import java.io.IOException
|
||||
import javax.inject.Inject
|
||||
|
||||
private val Context.dataStore by preferencesDataStore("data_store")
|
||||
|
||||
class DataStoreImpl @Inject constructor(context: Application): DataStore {
|
||||
private val dataStore = context.dataStore
|
||||
|
||||
override suspend fun <T> getData(key: Preferences.Key<T>, defaultValue: T): Flow<T> =
|
||||
dataStore.data.catch { exception ->
|
||||
if (exception is IOException){
|
||||
emit(emptyPreferences())
|
||||
} else{
|
||||
throw exception
|
||||
}
|
||||
}.map { preferences ->
|
||||
val result = preferences[key]?: defaultValue
|
||||
result
|
||||
}
|
||||
|
||||
override suspend fun <T> putData(key: Preferences.Key<T>, value: T) {
|
||||
dataStore.edit { preferences ->
|
||||
preferences[key] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package com.acclorite.books_history.data.local.dto
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import com.acclorite.books_history.domain.model.Category
|
||||
|
||||
@Entity
|
||||
data class BookEntity(
|
||||
@PrimaryKey(true) val id: Int? = null,
|
||||
val title: String,
|
||||
val filePath: String,
|
||||
val progress: Float,
|
||||
var lastOpened: Long? = null,
|
||||
val image: ByteArray = byteArrayOf(),
|
||||
val category: Category
|
||||
) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as BookEntity
|
||||
|
||||
return image.contentEquals(other.image)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return image.contentHashCode()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package com.acclorite.books_history.data.local.room
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Update
|
||||
import com.acclorite.books_history.data.local.dto.BookEntity
|
||||
|
||||
@Dao
|
||||
interface BookDao {
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insertBooks(
|
||||
books: List<BookEntity>
|
||||
)
|
||||
|
||||
@Query(
|
||||
"""
|
||||
SELECT *
|
||||
FROM bookentity
|
||||
WHERE LOWER(title) LIKE '%' || LOWER(:query) || '%'
|
||||
"""
|
||||
)
|
||||
suspend fun searchBooks(query: String): List<BookEntity>
|
||||
|
||||
@Delete
|
||||
suspend fun deleteBooks(books: List<BookEntity>)
|
||||
|
||||
@Update
|
||||
suspend fun updateBooks(books: List<BookEntity>)
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package com.acclorite.books_history.data.local.room
|
||||
|
||||
import androidx.room.Database
|
||||
import androidx.room.RoomDatabase
|
||||
import com.acclorite.books_history.data.local.dto.BookEntity
|
||||
|
||||
@Database(
|
||||
entities = [BookEntity::class],
|
||||
version = 1,
|
||||
exportSchema = false
|
||||
)
|
||||
abstract class BookDatabase : RoomDatabase() {
|
||||
abstract val dao: BookDao
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package com.acclorite.books_history.data.mapper
|
||||
|
||||
import com.acclorite.books_history.data.local.dto.BookEntity
|
||||
import com.acclorite.books_history.domain.model.Book
|
||||
|
||||
interface BookMapper {
|
||||
fun Book.toBookEntity(): BookEntity
|
||||
|
||||
fun BookEntity.toBook(): Book
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.acclorite.books_history.data.mapper
|
||||
|
||||
import com.acclorite.books_history.data.local.dto.BookEntity
|
||||
import com.acclorite.books_history.domain.model.Book
|
||||
import javax.inject.Inject
|
||||
|
||||
class BookMapperImpl @Inject constructor(): BookMapper {
|
||||
override fun Book.toBookEntity(): BookEntity {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun BookEntity.toBook(): Book {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
package com.acclorite.books_history.data.repository
|
||||
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import com.acclorite.books_history.data.local.data_store.DataStore
|
||||
import com.acclorite.books_history.data.local.room.BookDao
|
||||
import com.acclorite.books_history.data.mapper.toBookEntity
|
||||
import com.acclorite.books_history.domain.model.Book
|
||||
import com.acclorite.books_history.domain.repository.BookRepository
|
||||
import com.acclorite.books_history.util.Resource
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class BookRepositoryImpl @Inject constructor(
|
||||
private val database: BookDao,
|
||||
private val dataStore: DataStore
|
||||
): BookRepository {
|
||||
|
||||
override suspend fun getBooks(query: String): Flow<Resource<List<Book>>> {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override suspend fun insertBooks(books: List<Book>) {
|
||||
database.insertBooks(books.map { it.toBookEntity() })
|
||||
}
|
||||
|
||||
override suspend fun updateBooks(books: List<Book>) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override suspend fun deleteBooks(books: List<Book>) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override suspend fun <T> retrieveDataFromDataStore(
|
||||
key: Preferences.Key<T>,
|
||||
defaultValue: T
|
||||
): Flow<T> {
|
||||
return dataStore.getData(key, defaultValue)
|
||||
}
|
||||
|
||||
override suspend fun <T> putDataToDataStore(key: Preferences.Key<T>, value: T) {
|
||||
dataStore.putData(key, value)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package com.acclorite.books_history.data.use_case
|
||||
|
||||
import androidx.activity.ComponentActivity
|
||||
import com.acclorite.books_history.domain.use_case.ChangeLanguage
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
|
||||
class ChangeLanguageImpl @Inject constructor() : ChangeLanguage {
|
||||
override fun execute(language: String, activity: ComponentActivity) {
|
||||
val config = activity.resources.configuration
|
||||
val resources = activity.resources
|
||||
val locale = Locale(language)
|
||||
|
||||
Locale.setDefault(locale)
|
||||
config.setLocale(locale)
|
||||
|
||||
activity.createConfigurationContext(config)
|
||||
resources.updateConfiguration(
|
||||
config,
|
||||
resources.displayMetrics
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package com.acclorite.books_history.domain.model
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import java.io.File
|
||||
|
||||
data class Book(
|
||||
val id: Int,
|
||||
val title: String,
|
||||
val author: String,
|
||||
val description: String?,
|
||||
val text: List<FormattedLine> = emptyList(),
|
||||
val progress: Float,
|
||||
val file: File?,
|
||||
val lastOpened: Long?,
|
||||
val category: Category,
|
||||
val coverImage: Bitmap?
|
||||
)
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package com.acclorite.books_history.domain.model
|
||||
|
||||
enum class Category {
|
||||
READING, ALREADY_READ, PLANNING, DROPPED
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.acclorite.books_history.domain.model
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.unit.TextUnit
|
||||
import androidx.compose.ui.unit.sp
|
||||
import java.util.UUID
|
||||
|
||||
@Immutable
|
||||
data class FormattedLine(
|
||||
val line: String,
|
||||
val style: TextStyle = TextStyle(),
|
||||
val textSize: TextUnit = 0.sp,
|
||||
val id: String = UUID.randomUUID().toString()
|
||||
)
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
package com.acclorite.books_history.domain.repository
|
||||
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import com.acclorite.books_history.domain.model.Book
|
||||
import com.acclorite.books_history.util.Resource
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface BookRepository {
|
||||
|
||||
suspend fun getBooks(
|
||||
query: String
|
||||
): Flow<Resource<List<Book>>>
|
||||
|
||||
suspend fun insertBooks(
|
||||
books: List<Book>
|
||||
)
|
||||
|
||||
suspend fun updateBooks(
|
||||
books: List<Book>
|
||||
)
|
||||
|
||||
suspend fun deleteBooks(
|
||||
books: List<Book>
|
||||
)
|
||||
|
||||
suspend fun <T> retrieveDataFromDataStore(
|
||||
key: Preferences.Key<T>,
|
||||
defaultValue: T
|
||||
): Flow<T>
|
||||
|
||||
suspend fun <T> putDataToDataStore(
|
||||
key: Preferences.Key<T>,
|
||||
value: T
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.acclorite.books_history.domain.use_case
|
||||
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.annotation.Size
|
||||
|
||||
interface ChangeLanguage {
|
||||
fun execute(
|
||||
@Size(max = 2) language: String,
|
||||
activity: ComponentActivity
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package com.acclorite.books_history.presentation
|
||||
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.acclorite.books_history.domain.repository.BookRepository
|
||||
import com.acclorite.books_history.domain.use_case.ChangeLanguage
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Stores all variables such as theme, language etc
|
||||
*/
|
||||
@HiltViewModel
|
||||
class MainViewModel @Inject constructor(
|
||||
private val stateHandle: SavedStateHandle,
|
||||
private val repository: BookRepository,
|
||||
|
||||
private val changeLanguage: ChangeLanguage
|
||||
) : ViewModel() {
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,208 @@
|
|||
package com.acclorite.books_history.presentation
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.EnterTransition
|
||||
import androidx.compose.animation.ExitTransition
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.lifecycle.AbstractSavedStateViewModelFactory
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.createSavedStateHandle
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.savedstate.SavedStateRegistryOwner
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import dagger.hilt.android.lifecycle.withCreationCallback
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
private const val CURRENT_SCREEN = "current_screen"
|
||||
private const val BACKSTACK = "back_stack"
|
||||
|
||||
/**
|
||||
* All screens are listed here, later each screen will be passed as a param for [Navigator.composable] function.
|
||||
*/
|
||||
enum class Screen {
|
||||
LIBRARY,
|
||||
HISTORY,
|
||||
BROWSE,
|
||||
|
||||
|
||||
}
|
||||
|
||||
data class Argument(
|
||||
val key: String,
|
||||
val arg: Any?
|
||||
)
|
||||
|
||||
|
||||
/**
|
||||
* Navigator. Using for navigation between screens.
|
||||
*
|
||||
* [Navigator.currentScreen] param represents current [Screen].
|
||||
* [Navigator.navigate] navigates to [Screen] passed as param.
|
||||
*/
|
||||
@HiltViewModel(assistedFactory = Navigator.Factory::class)
|
||||
class Navigator @AssistedInject constructor(
|
||||
private val savedStateHandle: SavedStateHandle,
|
||||
@Assisted startScreen: Screen
|
||||
) : ViewModel() {
|
||||
|
||||
private val currentScreen = savedStateHandle.getStateFlow(CURRENT_SCREEN, startScreen)
|
||||
private val backStack = savedStateHandle.getStateFlow(BACKSTACK, mutableListOf<Screen>())
|
||||
private val arguments = mutableStateListOf<Argument>()
|
||||
|
||||
private fun putArgument(argument: Argument) {
|
||||
var found = false
|
||||
|
||||
for ((index, arg) in arguments.withIndex()) {
|
||||
if (arg.key == argument.key) {
|
||||
arguments[index] = argument
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
arguments.add(argument)
|
||||
}
|
||||
}
|
||||
|
||||
fun retrieveArgument(key: String): Any? {
|
||||
for (arg in arguments) {
|
||||
if (arg.key == key) {
|
||||
return arg.arg
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun navigate(screen: Screen, vararg args: Argument) =
|
||||
viewModelScope.launch(Dispatchers.Default) {
|
||||
backStack.value.add(currentScreen.value)
|
||||
|
||||
args.forEach {
|
||||
putArgument(it)
|
||||
}
|
||||
|
||||
savedStateHandle[CURRENT_SCREEN] = screen
|
||||
}
|
||||
|
||||
fun navigateBack() = viewModelScope.launch(Dispatchers.Default) {
|
||||
if (canGoBack()) {
|
||||
savedStateHandle[CURRENT_SCREEN] = backStack.value.last()
|
||||
backStack.value.removeLast()
|
||||
}
|
||||
}
|
||||
|
||||
fun canGoBack(): Boolean {
|
||||
return backStack.value.isNotEmpty()
|
||||
}
|
||||
|
||||
fun clearBackStack() {
|
||||
backStack.value.clear()
|
||||
}
|
||||
|
||||
fun getCurrentScreen(): StateFlow<Screen> {
|
||||
return currentScreen
|
||||
}
|
||||
|
||||
fun init(startScreen: Screen) {
|
||||
savedStateHandle[CURRENT_SCREEN] = startScreen
|
||||
}
|
||||
|
||||
/**
|
||||
* Animated Screen. Using in [NavigationHost]. Be sure to not use the same [screen] parameter twice, it'll override the highest one in your code.
|
||||
*
|
||||
* @param screen The [Screen] that represents [content].
|
||||
* @param enterAnim Enter Animation.
|
||||
* @param exitAnim Exit Animation.
|
||||
* @param content The Screen content to show when [Navigator.currentScreen] equals [screen].
|
||||
*/
|
||||
@SuppressLint("ComposableNaming")
|
||||
@Composable
|
||||
fun composable(
|
||||
screen: Screen,
|
||||
enterAnim: EnterTransition = fadeIn(tween(300)),
|
||||
exitAnim: ExitTransition = fadeOut(tween(300)),
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
AnimatedVisibility(
|
||||
visible = getCurrentScreen().collectAsState().value == screen,
|
||||
enter = enterAnim,
|
||||
exit = exitAnim,
|
||||
label = screen.toString()
|
||||
) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(startScreen: Screen): Navigator
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigation Host. Contains [Navigator.composable]s in [content].
|
||||
*
|
||||
* @param startScreen Start Screen. Be sure to pass [Screen] that uses in one of your [Navigator.composable]s.
|
||||
* @param colorBetweenAnimations The color, that using between animations, recommended to set this to background or navigation bar color.
|
||||
* @param content Content of the [NavigationHost]. Highly recommended to use [Navigator.composable].
|
||||
*/
|
||||
|
||||
@Composable
|
||||
fun NavigationHost(
|
||||
startScreen: Screen,
|
||||
activity: ComponentActivity,
|
||||
colorBetweenAnimations: Color = MaterialTheme.colorScheme.surface,
|
||||
content: @Composable Navigator.() -> Unit
|
||||
) {
|
||||
activity.apply {
|
||||
val navigator by viewModels<Navigator>(
|
||||
extrasProducer = {
|
||||
defaultViewModelCreationExtras.withCreationCallback<Navigator.Factory> { factory ->
|
||||
factory.create(startScreen)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
BackHandler {
|
||||
if (navigator.canGoBack()) {
|
||||
navigator.navigateBack()
|
||||
} else {
|
||||
activity.finishAffinity()
|
||||
}
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(colorBetweenAnimations)
|
||||
)
|
||||
|
||||
content(navigator)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package com.acclorite.books_history.util
|
||||
|
||||
import android.app.Application
|
||||
import dagger.hilt.android.HiltAndroidApp
|
||||
|
||||
@HiltAndroidApp
|
||||
class BookApplication : Application()
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package com.acclorite.books_history.util
|
||||
|
||||
object Constants {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package com.acclorite.books_history.util
|
||||
|
||||
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||
|
||||
object DataStoreConstants {
|
||||
val LANGUAGE = stringPreferencesKey("language")
|
||||
val THEME = stringPreferencesKey("theme")
|
||||
val DARK_THEME = stringPreferencesKey("dark_theme")
|
||||
val GUIDE = stringPreferencesKey("guide")
|
||||
|
||||
val BACKGROUND_COLOR = stringPreferencesKey("background_color")
|
||||
val FONT_COLOR = stringPreferencesKey("font_color")
|
||||
val FONT = stringPreferencesKey("font")
|
||||
val FONT_STYLE = stringPreferencesKey("font_style")
|
||||
val FONT_SIZE = stringPreferencesKey("font_size")
|
||||
val LINE_HEIGHT = stringPreferencesKey("line_height")
|
||||
val PARAGRAPH_HEIGHT = stringPreferencesKey("paragraph_height")
|
||||
val PARAGRAPH_INDENTATION = stringPreferencesKey("paragraph_indentation")
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package com.acclorite.books_history.util
|
||||
|
||||
sealed class Resource <T>(val data: T? = null, val message: String? = null) {
|
||||
class Success<T>(data: T?): Resource<T>(data)
|
||||
class Error<T>(message: String, data: T? = null): Resource<T>(data, message)
|
||||
class Loading<T>(val isLoading: Boolean = true): Resource<T>()
|
||||
}
|
||||
26
app/src/main/java/com/acclorite/books_history/util/UIText.kt
Normal file
26
app/src/main/java/com/acclorite/books_history/util/UIText.kt
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
package com.acclorite.books_history.util
|
||||
|
||||
import android.content.Context
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.stringResource
|
||||
|
||||
sealed class UIText {
|
||||
data class StringValue(val value: String): UIText()
|
||||
class StringResource(@StringRes val resId: Int, vararg val args: Any): UIText()
|
||||
|
||||
@Composable
|
||||
fun asString(): String {
|
||||
return when(this) {
|
||||
is StringValue -> value
|
||||
is StringResource -> stringResource(resId, *args)
|
||||
}
|
||||
}
|
||||
|
||||
fun asString(context: Context): String {
|
||||
return when(this) {
|
||||
is StringValue -> value
|
||||
is StringResource -> context.getString(resId, *args)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
<resources>
|
||||
<!-- Basic Strings -->
|
||||
<string name="app_name">Воскресіння Історії Книги</string>
|
||||
|
||||
<string name="app_name">Історія Книги: Воскресіння</string>
|
||||
|
||||
</resources>
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
<resources>
|
||||
<!-- Basic Strings -->
|
||||
<string name="app_version" translatable="false">0.0.1</string>
|
||||
<string name="app_name">Book\'s History Resurrection</string>
|
||||
<string name="app_name">Book\'s History: Resurrection</string>
|
||||
|
||||
|
||||
</resources>
|
||||
Loading…
Add table
Add a link
Reference in a new issue