diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 00000000..103e00cb --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,32 @@ + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000..94a25f7f --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 0ac4ad07..54050d3c 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -2,6 +2,8 @@ plugins { id("com.android.application") id("org.jetbrains.kotlin.android") id("com.google.devtools.ksp") + + id("com.google.dagger.hilt.android") kotlin("kapt") } @@ -47,6 +49,7 @@ android { packaging { resources { excludes += "/META-INF/{AL2.0,LGPL2.1}" + excludes += "/META-INF/gradle/incremental.annotation.processors" } } } @@ -77,16 +80,13 @@ dependencies { // All dependencies implementation ("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.2") - implementation ("androidx.compose.material:material-icons-extended:$composeVersion") + implementation ("androidx.compose.material:material-icons-extended") implementation ("androidx.activity:activity-compose:1.8.2") implementation ("com.google.accompanist:accompanist-swiperefresh:0.24.2-alpha") - //Dagger - Hilt - implementation ("com.google.dagger:hilt-compiler:latest.release") - implementation ("com.google.dagger:hilt-android:latest.release") - kapt ("com.google.dagger:hilt-android-compiler:latest.release") - kapt ("androidx.hilt:hilt-compiler:latest.release") - implementation ("androidx.hilt:hilt-navigation-compose:1.1.0") + // Dagger + implementation("com.google.dagger:hilt-android:2.50") + kapt("com.google.dagger:hilt-android-compiler:2.50") // Room implementation ("androidx.room:room-runtime:2.6.1") @@ -94,4 +94,7 @@ dependencies { // Kotlin Extensions and Coroutines support for Room implementation ("androidx.room:room-ktx:2.6.1") + + // Datastore + implementation ("androidx.datastore:datastore-preferences:latest.release") } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index df9ffba9..8d22b2a4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,6 +3,7 @@ xmlns:tools="http://schemas.android.com/tools"> getData (key: Preferences.Key, defaultValue: T): Flow + suspend fun putData (key: Preferences.Key, value: T) +} \ No newline at end of file diff --git a/app/src/main/java/com/acclorite/books_history/data/local/data_store/DataStoreImpl.kt b/app/src/main/java/com/acclorite/books_history/data/local/data_store/DataStoreImpl.kt new file mode 100644 index 00000000..82e8794e --- /dev/null +++ b/app/src/main/java/com/acclorite/books_history/data/local/data_store/DataStoreImpl.kt @@ -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 getData(key: Preferences.Key, defaultValue: T): Flow = + dataStore.data.catch { exception -> + if (exception is IOException){ + emit(emptyPreferences()) + } else{ + throw exception + } + }.map { preferences -> + val result = preferences[key]?: defaultValue + result + } + + override suspend fun putData(key: Preferences.Key, value: T) { + dataStore.edit { preferences -> + preferences[key] = value + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/acclorite/books_history/data/local/dto/BookEntity.kt b/app/src/main/java/com/acclorite/books_history/data/local/dto/BookEntity.kt new file mode 100644 index 00000000..68cc6b11 --- /dev/null +++ b/app/src/main/java/com/acclorite/books_history/data/local/dto/BookEntity.kt @@ -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() + } +} diff --git a/app/src/main/java/com/acclorite/books_history/data/local/room/BookDao.kt b/app/src/main/java/com/acclorite/books_history/data/local/room/BookDao.kt new file mode 100644 index 00000000..6b0b5f28 --- /dev/null +++ b/app/src/main/java/com/acclorite/books_history/data/local/room/BookDao.kt @@ -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 + ) + + @Query( + """ + SELECT * + FROM bookentity + WHERE LOWER(title) LIKE '%' || LOWER(:query) || '%' + """ + ) + suspend fun searchBooks(query: String): List + + @Delete + suspend fun deleteBooks(books: List) + + @Update + suspend fun updateBooks(books: List) +} \ No newline at end of file diff --git a/app/src/main/java/com/acclorite/books_history/data/local/room/BookDatabase.kt b/app/src/main/java/com/acclorite/books_history/data/local/room/BookDatabase.kt new file mode 100644 index 00000000..91c12767 --- /dev/null +++ b/app/src/main/java/com/acclorite/books_history/data/local/room/BookDatabase.kt @@ -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 +} \ No newline at end of file diff --git a/app/src/main/java/com/acclorite/books_history/data/mapper/BookMapper.kt b/app/src/main/java/com/acclorite/books_history/data/mapper/BookMapper.kt new file mode 100644 index 00000000..e9a82de1 --- /dev/null +++ b/app/src/main/java/com/acclorite/books_history/data/mapper/BookMapper.kt @@ -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 +} \ No newline at end of file diff --git a/app/src/main/java/com/acclorite/books_history/data/mapper/BookMapperImpl.kt b/app/src/main/java/com/acclorite/books_history/data/mapper/BookMapperImpl.kt new file mode 100644 index 00000000..e4982442 --- /dev/null +++ b/app/src/main/java/com/acclorite/books_history/data/mapper/BookMapperImpl.kt @@ -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") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/acclorite/books_history/data/repository/BookRepositoryImpl.kt b/app/src/main/java/com/acclorite/books_history/data/repository/BookRepositoryImpl.kt new file mode 100644 index 00000000..ff022f7f --- /dev/null +++ b/app/src/main/java/com/acclorite/books_history/data/repository/BookRepositoryImpl.kt @@ -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>> { + TODO("Not yet implemented") + } + + override suspend fun insertBooks(books: List) { + database.insertBooks(books.map { it.toBookEntity() }) + } + + override suspend fun updateBooks(books: List) { + TODO("Not yet implemented") + } + + override suspend fun deleteBooks(books: List) { + TODO("Not yet implemented") + } + + override suspend fun retrieveDataFromDataStore( + key: Preferences.Key, + defaultValue: T + ): Flow { + return dataStore.getData(key, defaultValue) + } + + override suspend fun putDataToDataStore(key: Preferences.Key, value: T) { + dataStore.putData(key, value) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/acclorite/books_history/data/use_case/ChangeLanguageImpl.kt b/app/src/main/java/com/acclorite/books_history/data/use_case/ChangeLanguageImpl.kt new file mode 100644 index 00000000..b07f56e2 --- /dev/null +++ b/app/src/main/java/com/acclorite/books_history/data/use_case/ChangeLanguageImpl.kt @@ -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 + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/acclorite/books_history/domain/model/Book.kt b/app/src/main/java/com/acclorite/books_history/domain/model/Book.kt new file mode 100644 index 00000000..243945c8 --- /dev/null +++ b/app/src/main/java/com/acclorite/books_history/domain/model/Book.kt @@ -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 = emptyList(), + val progress: Float, + val file: File?, + val lastOpened: Long?, + val category: Category, + val coverImage: Bitmap? +) diff --git a/app/src/main/java/com/acclorite/books_history/domain/model/Category.kt b/app/src/main/java/com/acclorite/books_history/domain/model/Category.kt new file mode 100644 index 00000000..55c56261 --- /dev/null +++ b/app/src/main/java/com/acclorite/books_history/domain/model/Category.kt @@ -0,0 +1,5 @@ +package com.acclorite.books_history.domain.model + +enum class Category { + READING, ALREADY_READ, PLANNING, DROPPED +} diff --git a/app/src/main/java/com/acclorite/books_history/domain/model/FormattedLine.kt b/app/src/main/java/com/acclorite/books_history/domain/model/FormattedLine.kt new file mode 100644 index 00000000..afadef0c --- /dev/null +++ b/app/src/main/java/com/acclorite/books_history/domain/model/FormattedLine.kt @@ -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() +) diff --git a/app/src/main/java/com/acclorite/books_history/domain/repository/BookRepository.kt b/app/src/main/java/com/acclorite/books_history/domain/repository/BookRepository.kt new file mode 100644 index 00000000..8a830a1a --- /dev/null +++ b/app/src/main/java/com/acclorite/books_history/domain/repository/BookRepository.kt @@ -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>> + + suspend fun insertBooks( + books: List + ) + + suspend fun updateBooks( + books: List + ) + + suspend fun deleteBooks( + books: List + ) + + suspend fun retrieveDataFromDataStore( + key: Preferences.Key, + defaultValue: T + ): Flow + + suspend fun putDataToDataStore( + key: Preferences.Key, + value: T + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/acclorite/books_history/domain/use_case/ChangeLanguage.kt b/app/src/main/java/com/acclorite/books_history/domain/use_case/ChangeLanguage.kt new file mode 100644 index 00000000..957cee71 --- /dev/null +++ b/app/src/main/java/com/acclorite/books_history/domain/use_case/ChangeLanguage.kt @@ -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 + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/acclorite/books_history/presentation/MainViewModel.kt b/app/src/main/java/com/acclorite/books_history/presentation/MainViewModel.kt new file mode 100644 index 00000000..0e042594 --- /dev/null +++ b/app/src/main/java/com/acclorite/books_history/presentation/MainViewModel.kt @@ -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() { + + +} \ No newline at end of file diff --git a/app/src/main/java/com/acclorite/books_history/presentation/Navigation.kt b/app/src/main/java/com/acclorite/books_history/presentation/Navigation.kt new file mode 100644 index 00000000..96f0f110 --- /dev/null +++ b/app/src/main/java/com/acclorite/books_history/presentation/Navigation.kt @@ -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()) + private val arguments = mutableStateListOf() + + 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 { + 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( + extrasProducer = { + defaultViewModelCreationExtras.withCreationCallback { factory -> + factory.create(startScreen) + } + } + ) + + BackHandler { + if (navigator.canGoBack()) { + navigator.navigateBack() + } else { + activity.finishAffinity() + } + } + + Box( + modifier = Modifier + .fillMaxSize() + .background(colorBetweenAnimations) + ) + + content(navigator) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/acclorite/books_history/util/BookApplication.kt b/app/src/main/java/com/acclorite/books_history/util/BookApplication.kt new file mode 100644 index 00000000..efc2af1c --- /dev/null +++ b/app/src/main/java/com/acclorite/books_history/util/BookApplication.kt @@ -0,0 +1,7 @@ +package com.acclorite.books_history.util + +import android.app.Application +import dagger.hilt.android.HiltAndroidApp + +@HiltAndroidApp +class BookApplication : Application() \ No newline at end of file diff --git a/app/src/main/java/com/acclorite/books_history/util/Constants.kt b/app/src/main/java/com/acclorite/books_history/util/Constants.kt new file mode 100644 index 00000000..3d612eeb --- /dev/null +++ b/app/src/main/java/com/acclorite/books_history/util/Constants.kt @@ -0,0 +1,5 @@ +package com.acclorite.books_history.util + +object Constants { + +} \ No newline at end of file diff --git a/app/src/main/java/com/acclorite/books_history/util/DataStoreConstants.kt b/app/src/main/java/com/acclorite/books_history/util/DataStoreConstants.kt new file mode 100644 index 00000000..6174c0b0 --- /dev/null +++ b/app/src/main/java/com/acclorite/books_history/util/DataStoreConstants.kt @@ -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") +} \ No newline at end of file diff --git a/app/src/main/java/com/acclorite/books_history/util/Resource.kt b/app/src/main/java/com/acclorite/books_history/util/Resource.kt new file mode 100644 index 00000000..00ae2b9c --- /dev/null +++ b/app/src/main/java/com/acclorite/books_history/util/Resource.kt @@ -0,0 +1,7 @@ +package com.acclorite.books_history.util + +sealed class Resource (val data: T? = null, val message: String? = null) { + class Success(data: T?): Resource(data) + class Error(message: String, data: T? = null): Resource(data, message) + class Loading(val isLoading: Boolean = true): Resource() +} \ No newline at end of file diff --git a/app/src/main/java/com/acclorite/books_history/util/UIText.kt b/app/src/main/java/com/acclorite/books_history/util/UIText.kt new file mode 100644 index 00000000..b72dcbc1 --- /dev/null +++ b/app/src/main/java/com/acclorite/books_history/util/UIText.kt @@ -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) + } + } +} \ No newline at end of file diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index c04d61c3..9f7f942a 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -1,6 +1,5 @@ - Воскресіння Історії Книги - + Історія Книги: Воскресіння \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 877bf1ce..a9cfeb1d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,7 +1,7 @@ 0.0.1 - Book\'s History Resurrection + Book\'s History: Resurrection \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 94da8ef6..3896da22 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,10 +3,5 @@ plugins { id("com.android.application") version "8.2.0-rc03" apply false id("org.jetbrains.kotlin.android") version "1.9.21" apply false id("com.google.devtools.ksp") version "1.9.21-1.0.16" apply false -} - -buildscript { - dependencies { - classpath ("com.google.dagger:hilt-android-gradle-plugin:latest.release") - } + id("com.google.dagger.hilt.android") version "2.50" apply false } \ No newline at end of file