diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 17dd8b0..a665174 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -4,7 +4,7 @@ plugins { alias(libs.plugins.android.application) alias(libs.plugins.jetbrains.kotlin.android) alias(libs.plugins.compose.compiler) - id("kotlin-kapt") + id("com.google.devtools.ksp") id("com.google.dagger.hilt.android") } @@ -21,7 +21,7 @@ android { minSdk = 24 targetSdk = 34 versionCode = 1 - versionName = "1.0.0" + versionName = "1.2.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } @@ -47,23 +47,26 @@ android { jvmTarget = "1.8" } buildFeatures { + viewBinding = true compose = true buildConfig = true } - kapt { - correctErrorTypes = true - } } dependencies { // Hilt - implementation (libs.hilt.android) - kapt(libs.hilt.android.compiler) - implementation (libs.androidx.hilt.navigation.compose) + implementation(libs.hilt.android) + ksp(libs.hilt.android.compiler) + implementation(libs.androidx.hilt.navigation.compose) // Glide - implementation (libs.glide) - annotationProcessor (libs.glide.compiler) + implementation(libs.glide) + annotationProcessor(libs.glide.compiler) + + // Room + implementation(libs.androidx.room.runtime) + ksp(libs.androidx.room.compiler) + implementation(libs.androidx.room.ktx) // Retrofit implementation(libs.retrofit) @@ -80,17 +83,32 @@ dependencies { implementation(libs.androidx.lifecycle.runtime.ktx) implementation(libs.androidx.activity.compose) implementation(platform(libs.androidx.compose.bom)) + implementation(libs.material) + implementation(libs.androidx.ui.viewbinding) + implementation(libs.androidx.appcompat) + implementation(libs.androidx.activity) + implementation(libs.androidx.navigation.fragment) + implementation(libs.androidx.fragment) + implementation(libs.androidx.constraintlayout) + implementation(libs.androidx.recyclerview) implementation(libs.androidx.ui) implementation(libs.androidx.ui.graphics) implementation(libs.androidx.ui.tooling.preview) implementation(libs.androidx.material3) implementation(libs.androidx.material.icons.extended) implementation(libs.androidx.navigation.compose) + + testImplementation(libs.kotlinx.coroutines.test) testImplementation(libs.junit) + testImplementation(libs.mockk) + + androidTestImplementation(libs.hilt.android.testing) + kspAndroidTest(libs.hilt.compiler) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) androidTestImplementation(platform(libs.androidx.compose.bom)) androidTestImplementation(libs.androidx.ui.test.junit4) + debugImplementation(libs.androidx.ui.tooling) debugImplementation(libs.androidx.ui.test.manifest) } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1e71d2a..5181259 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -5,7 +5,7 @@ + android:name=".presentation.ui.MainActivity" + android:exported="true"> + + \ No newline at end of file diff --git a/app/src/main/java/com/towitty/bookreport/data/database/BookReportDatabase.kt b/app/src/main/java/com/towitty/bookreport/data/database/BookReportDatabase.kt new file mode 100644 index 0000000..f6e9966 --- /dev/null +++ b/app/src/main/java/com/towitty/bookreport/data/database/BookReportDatabase.kt @@ -0,0 +1,12 @@ +package com.towitty.bookreport.data.database + +import androidx.room.Database +import androidx.room.RoomDatabase +import com.towitty.bookreport.data.database.model.TagEntity + + +@Database(entities = [TagEntity::class], version = 1, exportSchema = false) +abstract class BookReportDatabase : RoomDatabase() { + + abstract fun tagDao(): TagDao +} \ No newline at end of file diff --git a/app/src/main/java/com/towitty/bookreport/data/database/TagDao.kt b/app/src/main/java/com/towitty/bookreport/data/database/TagDao.kt new file mode 100644 index 0000000..413e169 --- /dev/null +++ b/app/src/main/java/com/towitty/bookreport/data/database/TagDao.kt @@ -0,0 +1,28 @@ +package com.towitty.bookreport.data.database + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.Query +import androidx.room.Update +import com.towitty.bookreport.data.database.model.TagEntity +import kotlinx.coroutines.flow.Flow + +@Dao +interface TagDao { + @Insert + suspend fun insertTag(tagEntity: TagEntity) + + @Update + suspend fun updateTag(tagEntity: TagEntity) + + @Delete + suspend fun deleteTag(tagEntity: TagEntity) + + @Query("SELECT * FROM tags WHERE id = :id") + fun getTag(id: Int): Flow + + @Query("SELECT * FROM tags ORDER BY name ASC") + fun getAllTags(): Flow> + +} \ No newline at end of file diff --git a/app/src/main/java/com/towitty/bookreport/data/database/di/DaosModule.kt b/app/src/main/java/com/towitty/bookreport/data/database/di/DaosModule.kt new file mode 100644 index 0000000..6219443 --- /dev/null +++ b/app/src/main/java/com/towitty/bookreport/data/database/di/DaosModule.kt @@ -0,0 +1,19 @@ +package com.towitty.bookreport.data.database.di + +import com.towitty.bookreport.data.database.BookReportDatabase +import com.towitty.bookreport.data.database.TagDao +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@InstallIn(SingletonComponent::class) +@Module +object DaosModule { + + @Singleton + @Provides + fun provideTagDao(appDatabase: BookReportDatabase): TagDao = appDatabase.tagDao() + +} \ No newline at end of file diff --git a/app/src/main/java/com/towitty/bookreport/data/database/di/DatabaseModule.kt b/app/src/main/java/com/towitty/bookreport/data/database/di/DatabaseModule.kt new file mode 100644 index 0000000..4502567 --- /dev/null +++ b/app/src/main/java/com/towitty/bookreport/data/database/di/DatabaseModule.kt @@ -0,0 +1,27 @@ +package com.towitty.bookreport.data.database.di + +import android.content.Context +import androidx.room.Room +import com.towitty.bookreport.data.database.BookReportDatabase +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 + +@InstallIn(SingletonComponent::class) +@Module +object DatabaseModule { + + @Singleton + @Provides + fun provideBookReportDatabase( + @ApplicationContext context: Context + ): BookReportDatabase = Room.databaseBuilder( + context, + BookReportDatabase::class.java, + "book_report_db" + ).build() + +} \ No newline at end of file diff --git a/app/src/main/java/com/towitty/bookreport/data/database/model/TagEntity.kt b/app/src/main/java/com/towitty/bookreport/data/database/model/TagEntity.kt new file mode 100644 index 0000000..f19a0b0 --- /dev/null +++ b/app/src/main/java/com/towitty/bookreport/data/database/model/TagEntity.kt @@ -0,0 +1,14 @@ +package com.towitty.bookreport.data.database.model + +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "tags") +data class TagEntity( + @PrimaryKey(autoGenerate = true) + val id:Int, + val name:String, + val color: Int +) + +val emptyTagEntity = TagEntity(0, "", 0) diff --git a/app/src/main/java/com/towitty/bookreport/data/network/ApiService.kt b/app/src/main/java/com/towitty/bookreport/data/network/ApiService.kt index 09c20ca..e706970 100644 --- a/app/src/main/java/com/towitty/bookreport/data/network/ApiService.kt +++ b/app/src/main/java/com/towitty/bookreport/data/network/ApiService.kt @@ -1,7 +1,7 @@ package com.towitty.bookreport.data.network import com.towitty.bookreport.BuildConfig -import com.towitty.bookreport.model.Book +import com.towitty.bookreport.data.network.model.Book import retrofit2.Response import retrofit2.http.GET import retrofit2.http.Header diff --git a/app/src/main/java/com/towitty/bookreport/data/network/BookRemoteDataSource.kt b/app/src/main/java/com/towitty/bookreport/data/network/BookRemoteDataSource.kt index 5066fdc..e455dea 100644 --- a/app/src/main/java/com/towitty/bookreport/data/network/BookRemoteDataSource.kt +++ b/app/src/main/java/com/towitty/bookreport/data/network/BookRemoteDataSource.kt @@ -1,7 +1,10 @@ package com.towitty.bookreport.data.network import android.util.Log -import com.towitty.bookreport.model.Book +import com.towitty.bookreport.data.network.model.Book +import com.towitty.bookreport.data.network.model.emptyBook +import com.towitty.bookreport.di.BookReportDispatchers +import com.towitty.bookreport.di.Dispatcher import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.withContext import javax.inject.Inject @@ -11,21 +14,21 @@ import javax.inject.Singleton @Singleton class BookRemoteDataSource @Inject constructor( private val bookApi: ApiService, - private val ioDispatcher: CoroutineDispatcher -) { - suspend fun getBooks(query: String): Book { + @Dispatcher(BookReportDispatchers.IO) private val ioDispatcher: CoroutineDispatcher +) : IBookDataSource { + override suspend fun getBook(query: String): Book { return withContext(ioDispatcher) { try { val response = bookApi.getSearchBook(query) if (response.isSuccessful) { - response.body() ?: Book(lastBuildDate = "", total = 0, start = 0, display = 0, bookList = emptyList()) + response.body() ?: emptyBook } else { Log.e("BookRemoteDataSource", "code: ${response.code()}") - Book(lastBuildDate = "", total = 0, start = 0, display = 0, bookList = emptyList()) + emptyBook } } catch (e: Exception) { Log.e("BookRemoteDataSource", "NetworkException: ${e.message}") - Book(lastBuildDate = "", total = 0, start = 0, display = 0, bookList = emptyList()) + emptyBook } } } diff --git a/app/src/main/java/com/towitty/bookreport/data/network/BookRemoteRepository.kt b/app/src/main/java/com/towitty/bookreport/data/network/BookRemoteRepository.kt deleted file mode 100644 index 04511ef..0000000 --- a/app/src/main/java/com/towitty/bookreport/data/network/BookRemoteRepository.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.towitty.bookreport.data.network - -import com.towitty.bookreport.model.Book -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class BookRemoteRepository @Inject constructor( - private val bookRemoteDataSource: BookRemoteDataSource -) { - suspend fun searchBooks(query: String): Book { - return bookRemoteDataSource.getBooks(query) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/towitty/bookreport/data/network/IBookDataSource.kt b/app/src/main/java/com/towitty/bookreport/data/network/IBookDataSource.kt new file mode 100644 index 0000000..be79e8c --- /dev/null +++ b/app/src/main/java/com/towitty/bookreport/data/network/IBookDataSource.kt @@ -0,0 +1,7 @@ +package com.towitty.bookreport.data.network + +import com.towitty.bookreport.data.network.model.Book + +interface IBookDataSource { + suspend fun getBook(query: String): Book +} diff --git a/app/src/main/java/com/towitty/bookreport/data/network/di/DataSourceModule.kt b/app/src/main/java/com/towitty/bookreport/data/network/di/DataSourceModule.kt new file mode 100644 index 0000000..e57e58d --- /dev/null +++ b/app/src/main/java/com/towitty/bookreport/data/network/di/DataSourceModule.kt @@ -0,0 +1,16 @@ +package com.towitty.bookreport.data.network.di + +import com.towitty.bookreport.data.network.BookRemoteDataSource +import com.towitty.bookreport.data.network.IBookDataSource +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent + +@InstallIn(SingletonComponent::class) +@Module +abstract class DataSourceModule { + + @Binds + abstract fun bindsBookDataSource(bookRemoteDataSource: BookRemoteDataSource): IBookDataSource +} \ No newline at end of file diff --git a/app/src/main/java/com/towitty/bookreport/di/AppModules.kt b/app/src/main/java/com/towitty/bookreport/data/network/di/NetworkModule.kt similarity index 83% rename from app/src/main/java/com/towitty/bookreport/di/AppModules.kt rename to app/src/main/java/com/towitty/bookreport/data/network/di/NetworkModule.kt index 52b80fb..ba98ea3 100644 --- a/app/src/main/java/com/towitty/bookreport/di/AppModules.kt +++ b/app/src/main/java/com/towitty/bookreport/data/network/di/NetworkModule.kt @@ -1,11 +1,10 @@ -package com.towitty.bookreport.di +package com.towitty.bookreport.data.network.di import com.towitty.bookreport.data.network.ApiService import dagger.Module import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent -import kotlinx.coroutines.Dispatchers import retrofit2.Converter import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory @@ -13,11 +12,7 @@ import javax.inject.Singleton @InstallIn(SingletonComponent::class) @Module -class AppModules { - - @Singleton - @Provides - fun provideDispatcherIO() = Dispatchers.IO +object NetworkModule { @Singleton @Provides @@ -37,4 +32,4 @@ class AppModules { fun provideApiService( retrofit: Retrofit ): ApiService = retrofit.create(ApiService::class.java) -} +} \ No newline at end of file diff --git a/app/src/main/java/com/towitty/bookreport/model/Book.kt b/app/src/main/java/com/towitty/bookreport/data/network/model/Book.kt similarity index 84% rename from app/src/main/java/com/towitty/bookreport/model/Book.kt rename to app/src/main/java/com/towitty/bookreport/data/network/model/Book.kt index 2c035b0..7092128 100644 --- a/app/src/main/java/com/towitty/bookreport/model/Book.kt +++ b/app/src/main/java/com/towitty/bookreport/data/network/model/Book.kt @@ -1,4 +1,4 @@ -package com.towitty.bookreport.model +package com.towitty.bookreport.data.network.model import com.google.gson.annotations.SerializedName @@ -33,6 +33,14 @@ data class BookItem( val description: String, ) +val emptyBook = Book( + lastBuildDate = "", + total = 0, + start = 0, + display = 0, + bookList = emptyList() +) + val emptyBookItem = BookItem( title = "", link = "", diff --git a/app/src/main/java/com/towitty/bookreport/data/repository/BookRemoteRepository.kt b/app/src/main/java/com/towitty/bookreport/data/repository/BookRemoteRepository.kt new file mode 100644 index 0000000..9ec1f80 --- /dev/null +++ b/app/src/main/java/com/towitty/bookreport/data/repository/BookRemoteRepository.kt @@ -0,0 +1,19 @@ +package com.towitty.bookreport.data.repository + +import com.towitty.bookreport.data.network.IBookDataSource +import com.towitty.bookreport.data.network.model.BookItem +import com.towitty.bookreport.data.network.model.emptyBookItem +import javax.inject.Inject + +class BookRemoteRepository @Inject constructor( + private val bookRemoteDataSource: IBookDataSource +) : IBookRepository { + private var bookList: List = emptyList() + + override suspend fun searchBooks(query: String): List { + bookList = bookRemoteDataSource.getBook(query).bookList + return bookList + } + + override fun findBookByIsbn(isbn: String): BookItem = bookList.find { it.isbn == isbn } ?: emptyBookItem +} \ No newline at end of file diff --git a/app/src/main/java/com/towitty/bookreport/data/repository/IBookRepository.kt b/app/src/main/java/com/towitty/bookreport/data/repository/IBookRepository.kt new file mode 100644 index 0000000..0979f3b --- /dev/null +++ b/app/src/main/java/com/towitty/bookreport/data/repository/IBookRepository.kt @@ -0,0 +1,8 @@ +package com.towitty.bookreport.data.repository + +import com.towitty.bookreport.data.network.model.BookItem + +interface IBookRepository { + suspend fun searchBooks(query: String): List + fun findBookByIsbn(isbn: String): BookItem +} \ No newline at end of file diff --git a/app/src/main/java/com/towitty/bookreport/data/repository/ITagRepository.kt b/app/src/main/java/com/towitty/bookreport/data/repository/ITagRepository.kt new file mode 100644 index 0000000..5d6df12 --- /dev/null +++ b/app/src/main/java/com/towitty/bookreport/data/repository/ITagRepository.kt @@ -0,0 +1,12 @@ +package com.towitty.bookreport.data.repository + +import com.towitty.bookreport.data.database.model.TagEntity +import kotlinx.coroutines.flow.Flow + +interface ITagRepository { + suspend fun insertTag(tagEntity: TagEntity) + suspend fun updateTag(tagEntity: TagEntity) + suspend fun deleteTag(tagEntity: TagEntity) + fun getTag(id: Int): Flow + fun getAllTags(): Flow> +} \ No newline at end of file diff --git a/app/src/main/java/com/towitty/bookreport/data/repository/TagLocalRepository.kt b/app/src/main/java/com/towitty/bookreport/data/repository/TagLocalRepository.kt new file mode 100644 index 0000000..71dac3c --- /dev/null +++ b/app/src/main/java/com/towitty/bookreport/data/repository/TagLocalRepository.kt @@ -0,0 +1,19 @@ +package com.towitty.bookreport.data.repository + +import com.towitty.bookreport.data.database.TagDao +import com.towitty.bookreport.data.database.model.TagEntity +import kotlinx.coroutines.flow.Flow +import javax.inject.Inject + +class TagLocalRepository @Inject constructor(private val tagDao: TagDao) : ITagRepository { + + override suspend fun insertTag(tagEntity: TagEntity) = tagDao.insertTag(tagEntity) + + override suspend fun updateTag(tagEntity: TagEntity) = tagDao.updateTag(tagEntity) + + override suspend fun deleteTag(tagEntity: TagEntity) = tagDao.deleteTag(tagEntity) + + override fun getTag(id: Int): Flow = tagDao.getTag(id) + + override fun getAllTags(): Flow> = tagDao.getAllTags() +} \ No newline at end of file diff --git a/app/src/main/java/com/towitty/bookreport/data/repository/di/RepositoryModule.kt b/app/src/main/java/com/towitty/bookreport/data/repository/di/RepositoryModule.kt new file mode 100644 index 0000000..aa690ad --- /dev/null +++ b/app/src/main/java/com/towitty/bookreport/data/repository/di/RepositoryModule.kt @@ -0,0 +1,21 @@ +package com.towitty.bookreport.data.repository.di + +import com.towitty.bookreport.data.repository.BookRemoteRepository +import com.towitty.bookreport.data.repository.IBookRepository +import com.towitty.bookreport.data.repository.ITagRepository +import com.towitty.bookreport.data.repository.TagLocalRepository +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent + +@InstallIn(SingletonComponent::class) +@Module +abstract class RepositoryModule { + + @Binds + abstract fun bindsTagRepository(tagLocalRepository: TagLocalRepository): ITagRepository + + @Binds + abstract fun bindsBookRepository(bookRemoteRepository: BookRemoteRepository): IBookRepository +} diff --git a/app/src/main/java/com/towitty/bookreport/di/Dispatcher.kt b/app/src/main/java/com/towitty/bookreport/di/Dispatcher.kt new file mode 100644 index 0000000..5089894 --- /dev/null +++ b/app/src/main/java/com/towitty/bookreport/di/Dispatcher.kt @@ -0,0 +1,12 @@ +package com.towitty.bookreport.di + +import jakarta.inject.Qualifier + +@Qualifier +@Retention(AnnotationRetention.RUNTIME) +annotation class Dispatcher(val bookReportDispatcher: BookReportDispatchers) + +enum class BookReportDispatchers { + Default, + IO, +} \ No newline at end of file diff --git a/app/src/main/java/com/towitty/bookreport/di/DispatchersModule.kt b/app/src/main/java/com/towitty/bookreport/di/DispatchersModule.kt new file mode 100644 index 0000000..3b61076 --- /dev/null +++ b/app/src/main/java/com/towitty/bookreport/di/DispatchersModule.kt @@ -0,0 +1,22 @@ +package com.towitty.bookreport.di + +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers + +@InstallIn(SingletonComponent::class) +@Module +object DispatchersModule { + + @Dispatcher(BookReportDispatchers.IO) + @Provides + fun providesIODispatcher(): CoroutineDispatcher = Dispatchers.IO + + @Dispatcher(BookReportDispatchers.Default) + @Provides + fun providesDefaultDispatcher(): CoroutineDispatcher = Dispatchers.Default + +} \ No newline at end of file diff --git a/app/src/main/java/com/towitty/bookreport/presentation/model/ColorItem.kt b/app/src/main/java/com/towitty/bookreport/presentation/model/ColorItem.kt new file mode 100644 index 0000000..994469a --- /dev/null +++ b/app/src/main/java/com/towitty/bookreport/presentation/model/ColorItem.kt @@ -0,0 +1,13 @@ +package com.towitty.bookreport.presentation.model + +import com.towitty.bookreport.R + +enum class ColorItem(val color: Int, var selected: Boolean = false) { + RED(R.color.red, true), + ORANGE(R.color.orange), + YELLOW(R.color.yellow), + GREEN(R.color.green), + BLUE(R.color.blue), + INDIGO(R.color.indigo), + VIOLET(R.color.violet) +} \ No newline at end of file diff --git a/app/src/main/java/com/towitty/bookreport/ui/navigation/BottomNavigation.kt b/app/src/main/java/com/towitty/bookreport/presentation/navigation/BottomNavigation.kt similarity index 65% rename from app/src/main/java/com/towitty/bookreport/ui/navigation/BottomNavigation.kt rename to app/src/main/java/com/towitty/bookreport/presentation/navigation/BottomNavigation.kt index c064bb5..282eac0 100644 --- a/app/src/main/java/com/towitty/bookreport/ui/navigation/BottomNavigation.kt +++ b/app/src/main/java/com/towitty/bookreport/presentation/navigation/BottomNavigation.kt @@ -1,35 +1,29 @@ -package com.towitty.bookreport.ui.navigation +package com.towitty.bookreport.presentation.navigation import androidx.annotation.StringRes -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.CalendarMonth -import androidx.compose.material.icons.filled.Home -import androidx.compose.material.icons.filled.Search -import androidx.compose.material.icons.filled.Settings import androidx.compose.material3.Icon import androidx.compose.material3.NavigationBar import androidx.compose.material3.NavigationBarItem import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.stringResource +import androidx.navigation.NavBackStackEntry import androidx.navigation.NavGraph.Companion.findStartDestination import androidx.navigation.NavHostController -import androidx.navigation.compose.currentBackStackEntryAsState import com.towitty.bookreport.R +import com.towitty.bookreport.presentation.ui.common.BookReportIcons enum class BottomNavItem(@StringRes val label: Int, val icon: ImageVector) { - HOME(R.string.label_home, Icons.Default.Home), - CALENDAR(R.string.label_calender, Icons.Default.CalendarMonth), - SEARCH(R.string.label_search, Icons.Default.Search), - SETTINGS(R.string.label_settings, Icons.Default.Settings) + HOME(R.string.label_home, BookReportIcons.Home), + CALENDAR(R.string.label_calender, BookReportIcons.CalendarMonth), + SEARCH(R.string.label_search, BookReportIcons.Search), } @Composable -fun AppBottomNavigation(navController: NavHostController, modifier: Modifier = Modifier) { - val navBackStackEntry by navController.currentBackStackEntryAsState() +fun AppBottomNavigation(navController: NavHostController, navBackStackEntry: NavBackStackEntry?, modifier: Modifier = Modifier) { + val currentRoute = navBackStackEntry?.destination?.route NavigationBar(modifier = modifier) { diff --git a/app/src/main/java/com/towitty/bookreport/presentation/navigation/Navigation.kt b/app/src/main/java/com/towitty/bookreport/presentation/navigation/Navigation.kt new file mode 100644 index 0000000..49005eb --- /dev/null +++ b/app/src/main/java/com/towitty/bookreport/presentation/navigation/Navigation.kt @@ -0,0 +1,101 @@ +package com.towitty.bookreport.presentation.navigation + +import android.content.Intent +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.runtime.State +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.navigation.NavHostController +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import com.towitty.bookreport.data.database.model.TagEntity +import com.towitty.bookreport.data.network.model.BookItem +import com.towitty.bookreport.data.network.model.emptyBookItem +import com.towitty.bookreport.presentation.ui.SettingsActivity +import com.towitty.bookreport.presentation.ui.screens.bookreport.AddTagScreen +import com.towitty.bookreport.presentation.ui.screens.bookreport.BookInfoDetailScreen +import com.towitty.bookreport.presentation.ui.screens.bookreport.BookReportScreen +import com.towitty.bookreport.presentation.ui.screens.bookreport.BookSearchScreen +import com.towitty.bookreport.presentation.ui.screens.calendar.CalendarScreen +import com.towitty.bookreport.presentation.ui.screens.home.HomeScreen +import com.towitty.bookreport.presentation.ui.screens.search.SearchScreen + +@Composable +fun Navigation( + bookListState: State>, + addedTagListState: State>, + tagListState: State>, + findBookByIsbn: (String) -> BookItem, + searchBooks: (String) -> Unit, + onSaveBookReport: () -> Unit, + onRemoveTag: (Int) -> Unit, + onAddSelectedTag: (Int) -> Unit, + navController: NavHostController, + startDestination: String, + modifier: Modifier = Modifier, +) { + val bookList: List by bookListState + + NavHost( + navController = navController, + startDestination = startDestination, + modifier = modifier.fillMaxSize() + ) { + composable(route = BottomNavItem.HOME.name) { + HomeScreen( + moveSettings = { context -> + context.startActivity(Intent(context, SettingsActivity::class.java)) + } + ) + } + composable(route = BottomNavItem.CALENDAR.name) { + CalendarScreen() + } + composable(route = BottomNavItem.SEARCH.name) { + SearchScreen() + } + composable(route = "${Routes.DIRECTLY_BOOK_REPORT}/{isbn}") { + val previousRoute = navController.previousBackStackEntry?.destination?.route + val isbn = it.arguments?.getString("isbn") ?: "" + BookReportScreen( + onCancel = { navController.navigateUp() }, + addedTagListState = addedTagListState, + onAddSelectedTag = onAddSelectedTag, + onRemoveTag = onRemoveTag, + onSaveBookReport = onSaveBookReport, + onNavigateAddTag = { navController.navigate(Routes.ADD_TAG) }, + bookItem = if (previousRoute == "${Routes.BOOK_INFO_DETAIL}/{isbn}") findBookByIsbn(isbn) else emptyBookItem + ) + } + composable(route = Routes.BOOK_SEARCH_FOR_BOOK_REPORT) { + BookSearchScreen( + onNavigateUp = { navController.navigateUp() }, + searchBook = searchBooks, + bookList = bookList, + onItemClicked = { isbn -> + navController.navigate("${Routes.BOOK_INFO_DETAIL}/$isbn") + }, + title = {/*미사용*/ } + ) + } + + composable(route = Routes.BOOK_INFO_DETAIL + "/{isbn}") { + val isbn = it.arguments?.getString("isbn") ?: "" + BookInfoDetailScreen( + onNavigateUp = { navController.navigateUp() }, + onSelection = { navController.navigate("${Routes.DIRECTLY_BOOK_REPORT}/$isbn") }, + bookItem = findBookByIsbn(isbn) + ) + } + + composable(route = Routes.ADD_TAG) { + AddTagScreen( + onNavigateUp = { navController.navigateUp() }, + onAddSelectedTag = onAddSelectedTag, + onRemoveTag = onRemoveTag, + tagListState = tagListState + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/towitty/bookreport/ui/navigation/Routes.kt b/app/src/main/java/com/towitty/bookreport/presentation/navigation/Routes.kt similarity index 66% rename from app/src/main/java/com/towitty/bookreport/ui/navigation/Routes.kt rename to app/src/main/java/com/towitty/bookreport/presentation/navigation/Routes.kt index 263f395..d897921 100644 --- a/app/src/main/java/com/towitty/bookreport/ui/navigation/Routes.kt +++ b/app/src/main/java/com/towitty/bookreport/presentation/navigation/Routes.kt @@ -1,6 +1,7 @@ -package com.towitty.bookreport.ui.navigation +package com.towitty.bookreport.presentation.navigation object Routes { + const val ADD_TAG: String = "book_report_add_tag" const val BOOK_INFO_DETAIL: String = "book_info_detail" const val BOOK_SEARCH_FOR_BOOK_REPORT: String = "book_report_book_search" const val DIRECTLY_BOOK_REPORT = "directly_book_report" diff --git a/app/src/main/java/com/towitty/bookreport/ui/theme/Color.kt b/app/src/main/java/com/towitty/bookreport/presentation/theme/Color.kt similarity index 83% rename from app/src/main/java/com/towitty/bookreport/ui/theme/Color.kt rename to app/src/main/java/com/towitty/bookreport/presentation/theme/Color.kt index cde1da7..e5efa5b 100644 --- a/app/src/main/java/com/towitty/bookreport/ui/theme/Color.kt +++ b/app/src/main/java/com/towitty/bookreport/presentation/theme/Color.kt @@ -1,4 +1,4 @@ -package com.towitty.bookreport.ui.theme +package com.towitty.bookreport.presentation.theme import androidx.compose.ui.graphics.Color diff --git a/app/src/main/java/com/towitty/bookreport/ui/theme/Theme.kt b/app/src/main/java/com/towitty/bookreport/presentation/theme/Theme.kt similarity index 97% rename from app/src/main/java/com/towitty/bookreport/ui/theme/Theme.kt rename to app/src/main/java/com/towitty/bookreport/presentation/theme/Theme.kt index 232e4bd..f8fa7b0 100644 --- a/app/src/main/java/com/towitty/bookreport/ui/theme/Theme.kt +++ b/app/src/main/java/com/towitty/bookreport/presentation/theme/Theme.kt @@ -1,4 +1,4 @@ -package com.towitty.bookreport.ui.theme +package com.towitty.bookreport.presentation.theme import android.os.Build import androidx.compose.foundation.isSystemInDarkTheme diff --git a/app/src/main/java/com/towitty/bookreport/ui/theme/Type.kt b/app/src/main/java/com/towitty/bookreport/presentation/theme/Type.kt similarity index 95% rename from app/src/main/java/com/towitty/bookreport/ui/theme/Type.kt rename to app/src/main/java/com/towitty/bookreport/presentation/theme/Type.kt index b72b377..8a6714e 100644 --- a/app/src/main/java/com/towitty/bookreport/ui/theme/Type.kt +++ b/app/src/main/java/com/towitty/bookreport/presentation/theme/Type.kt @@ -1,4 +1,4 @@ -package com.towitty.bookreport.ui.theme +package com.towitty.bookreport.presentation.theme import androidx.compose.material3.Typography import androidx.compose.ui.text.TextStyle diff --git a/app/src/main/java/com/towitty/bookreport/presentation/ui/BookReportViewModel.kt b/app/src/main/java/com/towitty/bookreport/presentation/ui/BookReportViewModel.kt new file mode 100644 index 0000000..0b2050c --- /dev/null +++ b/app/src/main/java/com/towitty/bookreport/presentation/ui/BookReportViewModel.kt @@ -0,0 +1,66 @@ +package com.towitty.bookreport.presentation.ui + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.towitty.bookreport.data.database.model.TagEntity +import com.towitty.bookreport.data.network.model.BookItem +import com.towitty.bookreport.data.repository.BookRemoteRepository +import com.towitty.bookreport.data.repository.ITagRepository +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class BookReportViewModel @Inject constructor( + private val bookRemoteRepository: BookRemoteRepository, + private val tagLocalRepository: ITagRepository +) : ViewModel() { + private val _bookList = MutableStateFlow>(emptyList()) + val bookList: StateFlow> = _bookList + + private val _tagList = MutableStateFlow>(emptyList()) + val tagList: StateFlow> = _tagList + + private val _addedTagList = MutableStateFlow>(emptyList()) + val addedTagList: StateFlow> = _addedTagList + + init { + getAllTags() + } + + fun searchBooks(query: String) { + viewModelScope.launch { + _bookList.value = bookRemoteRepository.searchBooks(query) + } + } + + fun findBookByIsbn(isbn: String): BookItem { + return bookRemoteRepository.findBookByIsbn(isbn) + } + + private fun getAllTags() { + viewModelScope.launch { + tagLocalRepository.getAllTags().collect { tags -> + _tagList.value = tags + } + } + } + + fun addSelectedTag(id: Int) { + viewModelScope.launch { + tagLocalRepository.getTag(id).collect { tag -> + if (_addedTagList.value.none { it.id == tag.id }) { + _addedTagList.value += tag + } + } + } + } + + fun removeAddedTag(id: Int) { + viewModelScope.launch { + _addedTagList.value = _addedTagList.value.filter { it.id != id } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/towitty/bookreport/ui/MainActivity.kt b/app/src/main/java/com/towitty/bookreport/presentation/ui/MainActivity.kt similarity index 50% rename from app/src/main/java/com/towitty/bookreport/ui/MainActivity.kt rename to app/src/main/java/com/towitty/bookreport/presentation/ui/MainActivity.kt index d885755..07fff02 100644 --- a/app/src/main/java/com/towitty/bookreport/ui/MainActivity.kt +++ b/app/src/main/java/com/towitty/bookreport/presentation/ui/MainActivity.kt @@ -1,44 +1,58 @@ -package com.towitty.bookreport.ui +package com.towitty.bookreport.presentation.ui import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.activity.viewModels import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Book import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.Icon import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable +import androidx.compose.runtime.State +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource -import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.rememberNavController import com.towitty.bookreport.R -import com.towitty.bookreport.ui.components.FabModal -import com.towitty.bookreport.ui.navigation.AppBottomNavigation -import com.towitty.bookreport.ui.navigation.BottomNavItem -import com.towitty.bookreport.ui.navigation.Navigation -import com.towitty.bookreport.ui.theme.BookReportTheme +import com.towitty.bookreport.data.database.model.TagEntity +import com.towitty.bookreport.data.network.model.BookItem +import com.towitty.bookreport.presentation.navigation.AppBottomNavigation +import com.towitty.bookreport.presentation.navigation.BottomNavItem +import com.towitty.bookreport.presentation.navigation.Navigation +import com.towitty.bookreport.presentation.theme.BookReportTheme +import com.towitty.bookreport.presentation.ui.common.BookReportIcons +import com.towitty.bookreport.presentation.ui.components.FabModal import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint class MainActivity : ComponentActivity() { + private val viewModel: BookReportViewModel by viewModels() + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { BookReportTheme { - BookReportApp() + BookReportApp( + bookListState = viewModel.bookList.collectAsState(), + addedTagListState = viewModel.addedTagList.collectAsState(), + tagListState = viewModel.tagList.collectAsState(), + searchBooks = viewModel::searchBooks, + onSaveBookReport = { /*TODO*/ }, + onRemoveTag = viewModel::removeAddedTag, + onAddSelectedTag = viewModel::addSelectedTag, + findBookByIsbn = viewModel::findBookByIsbn, + ) } } } @@ -46,17 +60,28 @@ class MainActivity : ComponentActivity() { @Composable fun BookReportApp( - viewModel: BookReportViewModel = hiltViewModel() + bookListState: State>, + addedTagListState: State>, + tagListState: State>, + searchBooks: (String) -> Unit, + onSaveBookReport: () -> Unit, + onAddSelectedTag: (Int) -> Unit, + onRemoveTag: (Int) -> Unit, + findBookByIsbn: (String) -> BookItem, ) { val navController = rememberNavController() val navBackStackEntry by navController.currentBackStackEntryAsState() val currentRoute = navBackStackEntry?.destination?.route var isShowFabModal by remember { mutableStateOf(false) } - Scaffold( modifier = Modifier.fillMaxSize(), - bottomBar = { AppBottomNavigation(navController = navController) }, + bottomBar = { + AppBottomNavigation( + navController = navController, + navBackStackEntry = navBackStackEntry + ) + }, floatingActionButton = { if (currentRoute == BottomNavItem.HOME.name) { FloatingActionButton( @@ -64,25 +89,31 @@ fun BookReportApp( shape = CircleShape ) { Icon( - imageVector = Icons.Default.Book, - contentDescription = stringResource(R.string.home_fab_description) + imageVector = BookReportIcons.Book, + contentDescription = stringResource(R.string.description_home_fab) ) } } } ) { innerPadding -> Navigation( - viewModel = viewModel, + bookListState = bookListState, + addedTagListState = addedTagListState, + tagListState = tagListState, + findBookByIsbn = findBookByIsbn, + searchBooks = searchBooks, + onSaveBookReport = onSaveBookReport, + onRemoveTag = onRemoveTag, + onAddSelectedTag = onAddSelectedTag, navController = navController, startDestination = BottomNavItem.HOME.name, - modifier = Modifier.padding(innerPadding) + modifier = Modifier.padding(innerPadding), ) if (isShowFabModal) { FabModal( onClicked = { navController.navigate(it) }, onDismissRequest = { isShowFabModal = false }, modifier = Modifier.wrapContentHeight() - ) } } diff --git a/app/src/main/java/com/towitty/bookreport/presentation/ui/SettingsActivity.kt b/app/src/main/java/com/towitty/bookreport/presentation/ui/SettingsActivity.kt new file mode 100644 index 0000000..52da70a --- /dev/null +++ b/app/src/main/java/com/towitty/bookreport/presentation/ui/SettingsActivity.kt @@ -0,0 +1,15 @@ +package com.towitty.bookreport.presentation.ui + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import com.towitty.bookreport.R +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class SettingsActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_settings) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/towitty/bookreport/presentation/ui/common/BookReportIcons.kt b/app/src/main/java/com/towitty/bookreport/presentation/ui/common/BookReportIcons.kt new file mode 100644 index 0000000..b30076c --- /dev/null +++ b/app/src/main/java/com/towitty/bookreport/presentation/ui/common/BookReportIcons.kt @@ -0,0 +1,24 @@ +package com.towitty.bookreport.presentation.ui.common + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBackIosNew +import androidx.compose.material.icons.filled.Book +import androidx.compose.material.icons.filled.CalendarMonth +import androidx.compose.material.icons.filled.FilterAlt +import androidx.compose.material.icons.filled.Home +import androidx.compose.material.icons.filled.Keyboard +import androidx.compose.material.icons.filled.PhotoCamera +import androidx.compose.material.icons.filled.Search +import androidx.compose.material.icons.filled.Settings + +object BookReportIcons { + val Keyboard = Icons.Default.Keyboard + val Search = Icons.Default.Search + val Book = Icons.Default.Book + val Home = Icons.Default.Home + val CalendarMonth = Icons.Default.CalendarMonth + val PhotoCamera = Icons.Default.PhotoCamera + val ArrowBackIosNew = Icons.Default.ArrowBackIosNew + val FilterAlt = Icons.Default.FilterAlt + val Settings = Icons.Default.Settings +} \ No newline at end of file diff --git a/app/src/main/java/com/towitty/bookreport/ui/components/Modal.kt b/app/src/main/java/com/towitty/bookreport/presentation/ui/components/Modal.kt similarity index 88% rename from app/src/main/java/com/towitty/bookreport/ui/components/Modal.kt rename to app/src/main/java/com/towitty/bookreport/presentation/ui/components/Modal.kt index b1d440e..2393a0b 100644 --- a/app/src/main/java/com/towitty/bookreport/ui/components/Modal.kt +++ b/app/src/main/java/com/towitty/bookreport/presentation/ui/components/Modal.kt @@ -1,4 +1,4 @@ -package com.towitty.bookreport.ui.components +package com.towitty.bookreport.presentation.ui.components import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -9,9 +9,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Keyboard -import androidx.compose.material.icons.filled.Search import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme @@ -26,7 +23,8 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.unit.dp import com.towitty.bookreport.R -import com.towitty.bookreport.ui.navigation.Routes +import com.towitty.bookreport.presentation.navigation.Routes +import com.towitty.bookreport.presentation.ui.common.BookReportIcons @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -47,15 +45,15 @@ fun FabModal( .padding(16.dp), ) { FabModalSheetItem( - icon = Icons.Default.Keyboard, + icon = BookReportIcons.Keyboard, label = stringResource(R.string.fab_modal_keyboard), onClicked = { onDismissRequest() - onClicked(Routes.DIRECTLY_BOOK_REPORT) + onClicked("${Routes.DIRECTLY_BOOK_REPORT}/") }, ) FabModalSheetItem( - icon = Icons.Default.Search, + icon = BookReportIcons.Search, label = stringResource(R.string.fab_modal_book_search), onClicked = { onDismissRequest() diff --git a/app/src/main/java/com/towitty/bookreport/presentation/ui/fragment/SettingsFragment.kt b/app/src/main/java/com/towitty/bookreport/presentation/ui/fragment/SettingsFragment.kt new file mode 100644 index 0000000..5292a47 --- /dev/null +++ b/app/src/main/java/com/towitty/bookreport/presentation/ui/fragment/SettingsFragment.kt @@ -0,0 +1,64 @@ +package com.towitty.bookreport.presentation.ui.fragment + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ArrayAdapter +import androidx.fragment.app.Fragment +import androidx.navigation.fragment.findNavController +import com.towitty.bookreport.R +import com.towitty.bookreport.databinding.FragmentSettingsBinding + +class SettingsFragment : Fragment() { + private var _binding: FragmentSettingsBinding? = null + private val binding get() = _binding!! + + private lateinit var settingItems: List + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View { + super.onCreateView(inflater, container, savedInstanceState) + _binding = FragmentSettingsBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + setNavigationList() + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + + private fun setNavigationList() { + settingItems = listOf( + getString(R.string.alarm_setting), + getString(R.string.tag_management), + getString(R.string.backup_and_sync), + getString(R.string.theme), + getString(R.string.user_feedback_and_improvement), + getString(R.string.reset_settings) + ) + + binding.lvSettings.adapter = ArrayAdapter( + requireContext(), R.layout.item_settings, R.id.tv_settings_item, settingItems + ) + + binding.lvSettings.setOnItemClickListener { _, _, position, _ -> + navigateToFragment(settingItems[position]) + } + } + + private fun navigateToFragment(selected: String) { + val action = when (selected) { + getString(R.string.tag_management) -> R.id.action_settingsFragment_to_tagManagementFragment + else -> R.id.action_settingsFragment_to_tagManagementFragment + } + findNavController().navigate(action) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/towitty/bookreport/presentation/ui/fragment/addtag/AddTagBottomSheetColorAdapter.kt b/app/src/main/java/com/towitty/bookreport/presentation/ui/fragment/addtag/AddTagBottomSheetColorAdapter.kt new file mode 100644 index 0000000..e9149b7 --- /dev/null +++ b/app/src/main/java/com/towitty/bookreport/presentation/ui/fragment/addtag/AddTagBottomSheetColorAdapter.kt @@ -0,0 +1,46 @@ +package com.towitty.bookreport.presentation.ui.fragment.addtag + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.towitty.bookreport.databinding.ItemSelectableColorBinding +import com.towitty.bookreport.presentation.model.ColorItem + +class AddTagBottomSheetColorAdapter(private val items: List) : + RecyclerView.Adapter() { + + private var selectedColor = items[0] + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ColorViewHolder { + + val binding = ItemSelectableColorBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return ColorViewHolder(binding) + } + + override fun onBindViewHolder(holder: ColorViewHolder, position: Int) { + holder.bind(items[position], position) + } + + override fun getItemCount(): Int = items.size + + inner class ColorViewHolder(private val binding: ItemSelectableColorBinding) : + RecyclerView.ViewHolder(binding.root) { + + @SuppressLint("NotifyDataSetChanged") + fun bind(colorItem: ColorItem, position: Int) { + binding.ivColor.setBackgroundResource(colorItem.color) + + binding.ivColor.setOnClickListener { + if (selectedColor !== colorItem) { + selectedColor.selected = false + notifyItemChanged(items.indexOf(selectedColor)) + colorItem.selected = true + selectedColor = colorItem + notifyItemChanged(position) + } + } + binding.ivColor.strokeWidth = if (colorItem.selected) 5f else 0f + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/towitty/bookreport/presentation/ui/fragment/addtag/AddTagBottomSheetFragment.kt b/app/src/main/java/com/towitty/bookreport/presentation/ui/fragment/addtag/AddTagBottomSheetFragment.kt new file mode 100644 index 0000000..ecfa4d4 --- /dev/null +++ b/app/src/main/java/com/towitty/bookreport/presentation/ui/fragment/addtag/AddTagBottomSheetFragment.kt @@ -0,0 +1,71 @@ +package com.towitty.bookreport.presentation.ui.fragment.addtag + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.activityViewModels +import androidx.lifecycle.lifecycleScope +import com.google.android.material.bottomsheet.BottomSheetDialogFragment +import com.towitty.bookreport.data.database.model.TagEntity +import com.towitty.bookreport.databinding.FragmentAddTagBottomSheetBinding +import com.towitty.bookreport.presentation.model.ColorItem +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.launch + +@AndroidEntryPoint +class AddTagBottomSheetFragment : BottomSheetDialogFragment() { + + private var _binding: FragmentAddTagBottomSheetBinding? = null + private val binding get() = _binding!! + + private val viewModel: AddTagViewModel by activityViewModels() + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + super.onCreateView(inflater, container, savedInstanceState) + _binding = FragmentAddTagBottomSheetBinding.inflate(inflater, container, false) + + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + setButtonEvents() + setColorAdapter() + } + + override fun onDestroyView() { + super.onDestroyView() + binding.etTagName.text?.clear() + _binding = null + } + + private fun setButtonEvents() { + binding.btnCancel.setOnClickListener { + dismiss() + } + + binding.btnSave.setOnClickListener { + val color = ColorItem.entries.find { it.selected }?.color + val tagEntity = TagEntity( + id = 0, + name = binding.etTagName.text.toString(), + color = color ?: ColorItem.RED.color + ) + viewLifecycleOwner.lifecycleScope.launch { + viewModel.insertTag(tagEntity) + dismiss() + } + + } + } + + private fun setColorAdapter() { + val colorItems = ColorItem.entries.toList() + binding.rvColorList.adapter = AddTagBottomSheetColorAdapter(colorItems) + } +} diff --git a/app/src/main/java/com/towitty/bookreport/presentation/ui/fragment/addtag/AddTagViewModel.kt b/app/src/main/java/com/towitty/bookreport/presentation/ui/fragment/addtag/AddTagViewModel.kt new file mode 100644 index 0000000..a69793c --- /dev/null +++ b/app/src/main/java/com/towitty/bookreport/presentation/ui/fragment/addtag/AddTagViewModel.kt @@ -0,0 +1,32 @@ +package com.towitty.bookreport.presentation.ui.fragment.addtag + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.towitty.bookreport.data.database.model.TagEntity +import com.towitty.bookreport.data.repository.ITagRepository +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class AddTagViewModel @Inject constructor( + private val tagRepository: ITagRepository +) : ViewModel() { + + private val _tagInserted = MutableStateFlow(false) + val tagInserted: StateFlow = _tagInserted.asStateFlow() + + fun insertTag(tagEntity: TagEntity) { + viewModelScope.launch { + try { + tagRepository.insertTag(tagEntity) + _tagInserted.emit(true) + } catch (e: Exception) { + _tagInserted.emit(false) + } + } + } +} diff --git a/app/src/main/java/com/towitty/bookreport/presentation/ui/fragment/tag/TagManagementAdapter.kt b/app/src/main/java/com/towitty/bookreport/presentation/ui/fragment/tag/TagManagementAdapter.kt new file mode 100644 index 0000000..d2e2dc0 --- /dev/null +++ b/app/src/main/java/com/towitty/bookreport/presentation/ui/fragment/tag/TagManagementAdapter.kt @@ -0,0 +1,44 @@ +package com.towitty.bookreport.presentation.ui.fragment.tag + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.core.content.ContextCompat +import androidx.recyclerview.widget.RecyclerView +import com.towitty.bookreport.data.database.model.TagEntity +import com.towitty.bookreport.databinding.ItemTagBinding + +class TagManagementAdapter( + private val onDeleteClick: (TagEntity) -> Unit +) : RecyclerView.Adapter() { + + private var tags: List = emptyList() + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TagViewHolder { + val binding = ItemTagBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return TagViewHolder(binding) + } + + override fun onBindViewHolder(holder: TagViewHolder, position: Int) { + holder.bind(tags[position]) + } + + override fun getItemCount(): Int = tags.size + + @SuppressLint("NotifyDataSetChanged") + fun setTags(tags: List) { + this.tags = tags + notifyDataSetChanged() + } + + inner class TagViewHolder(private val binding: ItemTagBinding) : RecyclerView.ViewHolder(binding.root) { + fun bind(tag: TagEntity) { + binding.tvTagName.backgroundTintList = ContextCompat.getColorStateList(binding.root.context, tag.color) + binding.tvTagName.text = tag.name + + binding.btnDelete.setOnClickListener { + onDeleteClick(tag) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/towitty/bookreport/presentation/ui/fragment/tag/TagManagementFragment.kt b/app/src/main/java/com/towitty/bookreport/presentation/ui/fragment/tag/TagManagementFragment.kt new file mode 100644 index 0000000..c00d034 --- /dev/null +++ b/app/src/main/java/com/towitty/bookreport/presentation/ui/fragment/tag/TagManagementFragment.kt @@ -0,0 +1,90 @@ +package com.towitty.bookreport.presentation.ui.fragment.tag + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels +import androidx.fragment.app.viewModels +import androidx.lifecycle.lifecycleScope +import androidx.navigation.fragment.findNavController +import com.towitty.bookreport.R +import com.towitty.bookreport.databinding.FragmentTagManagementBinding +import com.towitty.bookreport.presentation.ui.fragment.addtag.AddTagBottomSheetFragment +import com.towitty.bookreport.presentation.ui.fragment.addtag.AddTagViewModel +import dagger.hilt.android.AndroidEntryPoint + +import kotlinx.coroutines.launch + +@AndroidEntryPoint +class TagManagementFragment : Fragment() { + private var _binding: FragmentTagManagementBinding? = null + private val binding get() = _binding!! + + private val tagViewModel: TagManagementViewModel by viewModels() + private val addTagViewModel: AddTagViewModel by activityViewModels() + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = FragmentTagManagementBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + setNavigation() + showAddTagBottomSheet() + setTagList() + observeAddTagStatus() + } + + private fun observeAddTagStatus() { + viewLifecycleOwner.lifecycleScope.launch { + addTagViewModel.tagInserted.collect { isSuccess -> + if (isSuccess) { + Toast.makeText( + requireContext(), + getString(R.string.succeeded_to_save_tag), + Toast.LENGTH_SHORT + ).show() + } else { + Toast.makeText( + requireContext(), + getString(R.string.failed_to_save_tag), + Toast.LENGTH_SHORT + ).show() + } + } + } + } + + private fun setTagList() { + binding.rvTagList.adapter = TagManagementAdapter { tagViewModel.deleteTag(it) } + tagViewModel.getAllTags() + // TODO: Tag 크기에 맞게 spanCount 를 변경할 수 있도록 수정 + + viewLifecycleOwner.lifecycleScope.launch { + tagViewModel.tagList.collect { tags -> + (binding.rvTagList.adapter as TagManagementAdapter).setTags(tags) + } + } + } + + private fun setNavigation() { + binding.tbTagManagement.setNavigationOnClickListener { + findNavController().navigateUp() + } + } + + private fun showAddTagBottomSheet() { + binding.btnAddTag.setOnClickListener { + val addTagBottomSheetFragment = AddTagBottomSheetFragment() + addTagBottomSheetFragment.show(parentFragmentManager, addTagBottomSheetFragment.tag) + } + } +} diff --git a/app/src/main/java/com/towitty/bookreport/presentation/ui/fragment/tag/TagManagementViewModel.kt b/app/src/main/java/com/towitty/bookreport/presentation/ui/fragment/tag/TagManagementViewModel.kt new file mode 100644 index 0000000..fe0454f --- /dev/null +++ b/app/src/main/java/com/towitty/bookreport/presentation/ui/fragment/tag/TagManagementViewModel.kt @@ -0,0 +1,47 @@ +package com.towitty.bookreport.presentation.ui.fragment.tag + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.towitty.bookreport.data.database.model.TagEntity +import com.towitty.bookreport.data.database.model.emptyTagEntity +import com.towitty.bookreport.data.repository.ITagRepository +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch +import javax.inject.Inject + + +@HiltViewModel +class TagManagementViewModel @Inject constructor( + private val tagLocalRepository: ITagRepository +) : ViewModel() { + + private val _tagList = MutableStateFlow>(emptyList()) + val tagList: StateFlow> = _tagList.asStateFlow() + + fun getTagById(id: Int): TagEntity { + var tagEntity: TagEntity? = null + viewModelScope.launch { + tagLocalRepository.getTag(id).collect { tag -> + tagEntity = tag + } + } + return tagEntity ?: emptyTagEntity + } + + fun getAllTags() { + viewModelScope.launch { + tagLocalRepository.getAllTags().collect { tags -> + _tagList.value = tags + } + } + } + + fun deleteTag(tagEntity: TagEntity) { + viewModelScope.launch { + tagLocalRepository.deleteTag(tagEntity) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/towitty/bookreport/presentation/ui/screens/bookreport/AddTagScreen.kt b/app/src/main/java/com/towitty/bookreport/presentation/ui/screens/bookreport/AddTagScreen.kt new file mode 100644 index 0000000..a1a014f --- /dev/null +++ b/app/src/main/java/com/towitty/bookreport/presentation/ui/screens/bookreport/AddTagScreen.kt @@ -0,0 +1,111 @@ +package com.towitty.bookreport.presentation.ui.screens.bookreport + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.CenterAlignedTopAppBar +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.runtime.State +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.core.content.ContextCompat +import com.towitty.bookreport.R +import com.towitty.bookreport.data.database.model.TagEntity +import com.towitty.bookreport.presentation.ui.common.BookReportIcons + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun AddTagScreen( + onNavigateUp: () -> Unit, + onAddSelectedTag: (Int) -> Unit, + onRemoveTag: (Int) -> Unit, + tagListState: State> +) { + Scaffold( + topBar = { + CenterAlignedTopAppBar( + title = { Text("태그 추가") }, + navigationIcon = { + IconButton(onClick = onNavigateUp) { + Icon( + BookReportIcons.ArrowBackIosNew, + contentDescription = stringResource(id = R.string.description_go_back) + ) + } + } + ) + } + + ) { paddingValues -> + val tagList by tagListState + + LazyColumn( + modifier = Modifier.padding(paddingValues) + ) { + items(tagList) { tag -> + TagItem( + tag = tag, + onClicked = onAddSelectedTag, + onRemoveTag = onRemoveTag + ) + } + } + } +} + +@Composable +fun TagItem( + tag: TagEntity, + icon: Int = -1, + onClicked: (Int) -> Unit, + onRemoveTag: (Int) -> Unit +) { + Box( + modifier = Modifier + .padding(4.dp) + .clip(RoundedCornerShape(24.dp)) + .background(Color(ContextCompat.getColor(LocalContext.current, tag.color))) + .also { + if (icon == -1) { + it.clickable { + onClicked(tag.id) + } + } + } + ) { + Row { + TextButton( + onClick = { onClicked(tag.id) }, + ) { + Text(tag.name,color = Color.White) + } + if (icon != -1) { + IconButton( + onClick = { onRemoveTag(tag.id) }, + ) { + Icon( + painter = painterResource(id = icon), + contentDescription = stringResource(R.string.description_delete_tag) + ) + } + } + } + } +} diff --git a/app/src/main/java/com/towitty/bookreport/ui/bookreport/BookInfoDetailScreen.kt b/app/src/main/java/com/towitty/bookreport/presentation/ui/screens/bookreport/BookInfoDetailScreen.kt similarity index 95% rename from app/src/main/java/com/towitty/bookreport/ui/bookreport/BookInfoDetailScreen.kt rename to app/src/main/java/com/towitty/bookreport/presentation/ui/screens/bookreport/BookInfoDetailScreen.kt index c61dbbe..6575021 100644 --- a/app/src/main/java/com/towitty/bookreport/ui/bookreport/BookInfoDetailScreen.kt +++ b/app/src/main/java/com/towitty/bookreport/presentation/ui/screens/bookreport/BookInfoDetailScreen.kt @@ -1,4 +1,4 @@ -package com.towitty.bookreport.ui.bookreport +package com.towitty.bookreport.presentation.ui.screens.bookreport import android.graphics.Bitmap import android.graphics.drawable.Drawable @@ -19,8 +19,6 @@ import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowBackIosNew -import androidx.compose.material.icons.filled.PhotoCamera import androidx.compose.material.icons.outlined.FavoriteBorder import androidx.compose.material.icons.twotone.Favorite import androidx.compose.material3.Card @@ -51,7 +49,8 @@ import com.bumptech.glide.Glide import com.bumptech.glide.request.target.CustomTarget import com.bumptech.glide.request.transition.Transition import com.towitty.bookreport.R -import com.towitty.bookreport.model.BookItem +import com.towitty.bookreport.data.network.model.BookItem +import com.towitty.bookreport.presentation.ui.common.BookReportIcons @Composable fun BookInfoDetailScreen( @@ -77,7 +76,7 @@ fun BookInfoDetailScreen( modifier = modifier ) { innerPadding -> Column( - modifier = Modifier + modifier = Modifier .padding(innerPadding) .padding(start = 16.dp, end = 16.dp) .verticalScroll(rememberScrollState()) @@ -113,7 +112,7 @@ fun BookInfoDetailTopAppbar(onBack: () -> Unit, onSelection: () -> Unit, modifie title = {}, navigationIcon = { IconButton(onClick = onBack) { - Icon(Icons.Default.ArrowBackIosNew, contentDescription = stringResource(R.string.description_go_back)) + Icon(BookReportIcons.ArrowBackIosNew, contentDescription = stringResource(R.string.description_go_back)) } }, actions = { @@ -174,7 +173,7 @@ fun BookInfoImage( .clip(CircleShape) ) { Icon( - imageVector = Icons.Default.PhotoCamera, + imageVector = BookReportIcons.PhotoCamera, contentDescription = stringResource(R.string.description_camera_btn), tint = Color.White, modifier = Modifier.size(34.dp) diff --git a/app/src/main/java/com/towitty/bookreport/ui/bookreport/BookReportScreen.kt b/app/src/main/java/com/towitty/bookreport/presentation/ui/screens/bookreport/BookReportScreen.kt similarity index 76% rename from app/src/main/java/com/towitty/bookreport/ui/bookreport/BookReportScreen.kt rename to app/src/main/java/com/towitty/bookreport/presentation/ui/screens/bookreport/BookReportScreen.kt index 20a857d..621054f 100644 --- a/app/src/main/java/com/towitty/bookreport/ui/bookreport/BookReportScreen.kt +++ b/app/src/main/java/com/towitty/bookreport/presentation/ui/screens/bookreport/BookReportScreen.kt @@ -1,9 +1,10 @@ -package com.towitty.bookreport.ui.bookreport +package com.towitty.bookreport.presentation.ui.screens.bookreport import android.graphics.Bitmap import android.graphics.drawable.Drawable import androidx.compose.foundation.Image import androidx.compose.foundation.border +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -14,6 +15,8 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults @@ -25,6 +28,7 @@ import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable +import androidx.compose.runtime.State import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.rememberSaveable @@ -46,17 +50,22 @@ import com.bumptech.glide.Glide import com.bumptech.glide.request.target.CustomTarget import com.bumptech.glide.request.transition.Transition import com.towitty.bookreport.R -import com.towitty.bookreport.model.BookItem +import com.towitty.bookreport.data.database.model.TagEntity +import com.towitty.bookreport.data.network.model.BookItem @Composable fun BookReportScreen( + addedTagListState: State>, onCancel: () -> Unit, - onSave: () -> Unit, + onSaveBookReport: () -> Unit, + onAddSelectedTag: (Int) -> Unit, + onRemoveTag: (Int) -> Unit, + onNavigateAddTag: () -> Unit, modifier: Modifier = Modifier, bookItem: BookItem ) { Scaffold( - topBar = { BookReportTopAppbar(onCancel, onSave) }, + topBar = { BookReportTopAppbar(onCancel, onSaveBookReport) }, modifier = modifier ) { innerPadding -> Column(modifier = Modifier.padding(innerPadding)) { @@ -82,17 +91,24 @@ fun BookReportScreen( Spacer(modifier = Modifier.size(4.dp)) BookReportContent( - Modifier.fillMaxWidth() + addedTagListState = addedTagListState, + onAddSelectedTag = onAddSelectedTag, + onRemoveTag = onRemoveTag, + onNavigateAddTag = onNavigateAddTag, + modifier = Modifier.fillMaxWidth() ) } - } } @Composable @OptIn(ExperimentalMaterial3Api::class) -private fun BookReportTopAppbar(onCancel: () -> Unit, onSave: () -> Unit, modifier: Modifier = Modifier) { +private fun BookReportTopAppbar( + onCancel: () -> Unit, + onSaveBookReport: () -> Unit, + modifier: Modifier = Modifier +) { TopAppBar( title = {}, navigationIcon = { @@ -106,7 +122,7 @@ private fun BookReportTopAppbar(onCancel: () -> Unit, onSave: () -> Unit, modifi }, actions = { TextButton( - onClick = onSave, + onClick = onSaveBookReport, modifier = Modifier .border(1.dp, color = Color.Gray, shape = RoundedCornerShape(4.dp)) ) { @@ -208,13 +224,16 @@ fun BookReportBookInfo( } @Composable -fun BookReportContent(modifier: Modifier = Modifier) { - var bookReportTitle by rememberSaveable { - mutableStateOf("") - } - var bookReportContent by rememberSaveable { - mutableStateOf("") - } +fun BookReportContent( + addedTagListState: State>, + onAddSelectedTag: (Int) -> Unit, + onRemoveTag: (Int) -> Unit, + onNavigateAddTag: () -> Unit, + modifier: Modifier = Modifier +) { + var bookReportTitle by rememberSaveable { mutableStateOf("") } + var bookReportContent by rememberSaveable { mutableStateOf("") } + Column(modifier = modifier) { Spacer(modifier = Modifier.size(8.dp)) OutlinedTextField( @@ -226,6 +245,13 @@ fun BookReportContent(modifier: Modifier = Modifier) { modifier = Modifier.fillMaxWidth() ) Spacer(modifier = Modifier.size(8.dp)) + TagListWithAddButton( + addedTagListState, + onNavigateAddTag, + onAddSelectedTag, + onRemoveTag, + ) + Spacer(modifier = Modifier.size(8.dp)) OutlinedTextField( value = bookReportContent, onValueChange = { bookReportContent = it }, @@ -239,10 +265,41 @@ fun BookReportContent(modifier: Modifier = Modifier) { } +@Composable +fun TagListWithAddButton( + addedTagListState: State>, + onNavigateAddTag: () -> Unit, + onAddSelectedTag: (Int) -> Unit, + onRemoveTag: (Int) -> Unit, +) { + val addedTagList by addedTagListState + Row( + modifier = Modifier.fillMaxWidth() + ) { + LazyRow( + modifier = Modifier + .fillMaxWidth() + .weight(0.8f) + ) { + items(addedTagList) { tag -> + TagItem(tag = tag, icon = R.drawable.ic_outline_cancel, onClicked = onAddSelectedTag, onRemoveTag = onRemoveTag) + } + } + Image( + painter = painterResource(id = R.drawable.ic_rounded_add_circle), + contentDescription = stringResource(R.string.description_add_tag), + modifier = Modifier + .size(48.dp) + .align(Alignment.CenterVertically) + .clickable { onNavigateAddTag() } + ) + } +} + @Preview(showBackground = true) @Composable fun BookReportContentPreview(modifier: Modifier = Modifier) { - BookReportContent() + } @Preview(showBackground = true) diff --git a/app/src/main/java/com/towitty/bookreport/ui/bookreport/BookSearchScreen.kt b/app/src/main/java/com/towitty/bookreport/presentation/ui/screens/bookreport/BookSearchScreen.kt similarity index 94% rename from app/src/main/java/com/towitty/bookreport/ui/bookreport/BookSearchScreen.kt rename to app/src/main/java/com/towitty/bookreport/presentation/ui/screens/bookreport/BookSearchScreen.kt index f74f20f..6eed4d4 100644 --- a/app/src/main/java/com/towitty/bookreport/ui/bookreport/BookSearchScreen.kt +++ b/app/src/main/java/com/towitty/bookreport/presentation/ui/screens/bookreport/BookSearchScreen.kt @@ -1,4 +1,4 @@ -package com.towitty.bookreport.ui.bookreport +package com.towitty.bookreport.presentation.ui.screens.bookreport import android.graphics.Bitmap import android.graphics.drawable.Drawable @@ -23,10 +23,6 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.KeyboardActionScope import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowBackIosNew -import androidx.compose.material.icons.filled.FilterAlt -import androidx.compose.material.icons.filled.Search import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Card @@ -59,7 +55,8 @@ import com.bumptech.glide.Glide import com.bumptech.glide.request.target.CustomTarget import com.bumptech.glide.request.transition.Transition import com.towitty.bookreport.R -import com.towitty.bookreport.model.BookItem +import com.towitty.bookreport.data.network.model.BookItem +import com.towitty.bookreport.presentation.ui.common.BookReportIcons @OptIn(ExperimentalMaterial3Api::class) @@ -82,7 +79,7 @@ fun BookSearchScreen( navigationIcon = { IconButton(onClick = onNavigateUp) { Icon( - imageVector = Icons.Default.ArrowBackIosNew, contentDescription = stringResource( + imageVector = BookReportIcons.ArrowBackIosNew, contentDescription = stringResource( R.string.description_go_back ) ) @@ -133,7 +130,7 @@ fun BookSearchBar( value = searchText, onValueChange = onValueChange, leadingIcon = { - Icon(imageVector = Icons.Default.Search, contentDescription = null) + Icon(imageVector = BookReportIcons.Search, contentDescription = null) }, placeholder = { Text(stringResource(id = R.string.placeholder_search)) @@ -157,7 +154,7 @@ fun SearchFilter( horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = modifier.wrapContentHeight() ) { - Icon(imageVector = Icons.Default.FilterAlt, contentDescription = null) + Icon(imageVector = BookReportIcons.FilterAlt, contentDescription = null) TextButton( onClick = { onSelectedFilter("title") }, diff --git a/app/src/main/java/com/towitty/bookreport/ui/calendar/CalendarScreen.kt b/app/src/main/java/com/towitty/bookreport/presentation/ui/screens/calendar/CalendarScreen.kt similarity index 86% rename from app/src/main/java/com/towitty/bookreport/ui/calendar/CalendarScreen.kt rename to app/src/main/java/com/towitty/bookreport/presentation/ui/screens/calendar/CalendarScreen.kt index aa2438c..9c5ddea 100644 --- a/app/src/main/java/com/towitty/bookreport/ui/calendar/CalendarScreen.kt +++ b/app/src/main/java/com/towitty/bookreport/presentation/ui/screens/calendar/CalendarScreen.kt @@ -1,4 +1,4 @@ -package com.towitty.bookreport.ui.calendar +package com.towitty.bookreport.presentation.ui.screens.calendar import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.Text diff --git a/app/src/main/java/com/towitty/bookreport/presentation/ui/screens/home/HomeScreen.kt b/app/src/main/java/com/towitty/bookreport/presentation/ui/screens/home/HomeScreen.kt new file mode 100644 index 0000000..2242c5a --- /dev/null +++ b/app/src/main/java/com/towitty/bookreport/presentation/ui/screens/home/HomeScreen.kt @@ -0,0 +1,44 @@ +package com.towitty.bookreport.presentation.ui.screens.home + +import android.content.Context +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.CenterAlignedTopAppBar +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import com.towitty.bookreport.R +import com.towitty.bookreport.presentation.ui.common.BookReportIcons + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun HomeScreen( + moveSettings: (context: Context) -> Unit, +) { + Surface(modifier = Modifier.fillMaxSize()) { + val context = LocalContext.current + Column { + CenterAlignedTopAppBar( + title = { Text(stringResource(id = R.string.app_name)) }, + actions = { + IconButton( + onClick = { moveSettings(context) }, + content = { Icon(BookReportIcons.Settings, contentDescription = null) } + ) + } + ) + } + } +} + +@Preview(showBackground = true) +@Composable +fun HomeScreenPreview(modifier: Modifier = Modifier) { +} \ No newline at end of file diff --git a/app/src/main/java/com/towitty/bookreport/ui/search/SearchScreen.kt b/app/src/main/java/com/towitty/bookreport/presentation/ui/screens/search/SearchScreen.kt similarity index 86% rename from app/src/main/java/com/towitty/bookreport/ui/search/SearchScreen.kt rename to app/src/main/java/com/towitty/bookreport/presentation/ui/screens/search/SearchScreen.kt index 1c61ba9..97c3c31 100644 --- a/app/src/main/java/com/towitty/bookreport/ui/search/SearchScreen.kt +++ b/app/src/main/java/com/towitty/bookreport/presentation/ui/screens/search/SearchScreen.kt @@ -1,4 +1,4 @@ -package com.towitty.bookreport.ui.search +package com.towitty.bookreport.presentation.ui.screens.search import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.Text diff --git a/app/src/main/java/com/towitty/bookreport/ui/BookReportViewModel.kt b/app/src/main/java/com/towitty/bookreport/ui/BookReportViewModel.kt deleted file mode 100644 index 552d8fb..0000000 --- a/app/src/main/java/com/towitty/bookreport/ui/BookReportViewModel.kt +++ /dev/null @@ -1,34 +0,0 @@ -package com.towitty.bookreport.ui - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.towitty.bookreport.data.network.BookRemoteRepository -import com.towitty.bookreport.model.BookItem -import com.towitty.bookreport.model.emptyBookItem -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.launch -import javax.inject.Inject - -@HiltViewModel -class BookReportViewModel @Inject constructor( - private val bookRemoteRepository: BookRemoteRepository, -) : ViewModel() { - private val _bookList = MutableStateFlow>(emptyList()) - val bookList: StateFlow> = _bookList - - private val _selectedBook = MutableStateFlow(emptyBookItem) - val selectedBook: StateFlow = _selectedBook - - fun searchBooks(query: String) { - viewModelScope.launch { - val books = bookRemoteRepository.searchBooks(query) - _bookList.value = books.bookList - } - } - - fun findBookByIsbn(isbn: String) { - _selectedBook.value = _bookList.value.find { it.isbn == isbn } ?: emptyBookItem - } -} \ No newline at end of file diff --git a/app/src/main/java/com/towitty/bookreport/ui/home/HomeScreen.kt b/app/src/main/java/com/towitty/bookreport/ui/home/HomeScreen.kt deleted file mode 100644 index 051862c..0000000 --- a/app/src/main/java/com/towitty/bookreport/ui/home/HomeScreen.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.towitty.bookreport.ui.home - -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.tooling.preview.Preview - -@Composable -fun HomeScreen() { - -} - -@Preview(showBackground = true) -@Composable -fun HomeScreenPreview(modifier: Modifier = Modifier) { - HomeScreen() -} \ No newline at end of file diff --git a/app/src/main/java/com/towitty/bookreport/ui/navigation/Navigation.kt b/app/src/main/java/com/towitty/bookreport/ui/navigation/Navigation.kt deleted file mode 100644 index 1ed5e3d..0000000 --- a/app/src/main/java/com/towitty/bookreport/ui/navigation/Navigation.kt +++ /dev/null @@ -1,78 +0,0 @@ -package com.towitty.bookreport.ui.navigation - -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.ui.Modifier -import androidx.navigation.NavHostController -import androidx.navigation.compose.NavHost -import androidx.navigation.compose.composable -import com.towitty.bookreport.model.BookItem -import com.towitty.bookreport.model.emptyBookItem -import com.towitty.bookreport.ui.BookReportViewModel -import com.towitty.bookreport.ui.bookreport.BookInfoDetailScreen -import com.towitty.bookreport.ui.bookreport.BookReportScreen -import com.towitty.bookreport.ui.bookreport.BookSearchScreen -import com.towitty.bookreport.ui.calendar.CalendarScreen -import com.towitty.bookreport.ui.home.HomeScreen -import com.towitty.bookreport.ui.search.SearchScreen -import com.towitty.bookreport.ui.settings.SettingsScreen - -@Composable -fun Navigation( - viewModel: BookReportViewModel, - navController: NavHostController, - startDestination: String, - modifier: Modifier = Modifier, -) { - val bookList: List by viewModel.bookList.collectAsState() - val selectedBook: BookItem by viewModel.selectedBook.collectAsState() - - NavHost( - navController = navController, - startDestination = startDestination, - modifier = modifier.fillMaxSize() - ) { - composable(route = BottomNavItem.HOME.name) { - HomeScreen() - } - composable(route = BottomNavItem.CALENDAR.name) { - CalendarScreen() - } - composable(route = BottomNavItem.SEARCH.name) { - SearchScreen() - } - composable(route = BottomNavItem.SETTINGS.name) { - SettingsScreen() - } - composable(route = Routes.DIRECTLY_BOOK_REPORT) { - val previousRoute = navController.previousBackStackEntry?.destination?.route - BookReportScreen( - onCancel = { navController.navigateUp() }, - onSave = {/*TODO*/ }, - bookItem = if (previousRoute == Routes.BOOK_INFO_DETAIL) selectedBook else emptyBookItem - ) - } - composable(route = Routes.BOOK_SEARCH_FOR_BOOK_REPORT) { - BookSearchScreen( - onNavigateUp = { navController.navigateUp() }, - searchBook = { viewModel.searchBooks(it) }, - bookList = bookList, - onItemClicked = { isbn -> - viewModel.findBookByIsbn(isbn) - navController.navigate(Routes.BOOK_INFO_DETAIL) - }, - title = {/*미사용*/ } - ) - } - - composable(route = Routes.BOOK_INFO_DETAIL) { - BookInfoDetailScreen( - onNavigateUp = { navController.navigateUp() }, - onSelection = { navController.navigate(Routes.DIRECTLY_BOOK_REPORT) }, - bookItem = selectedBook - ) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/towitty/bookreport/ui/settings/SettingsScreen.kt b/app/src/main/java/com/towitty/bookreport/ui/settings/SettingsScreen.kt deleted file mode 100644 index 27fb58a..0000000 --- a/app/src/main/java/com/towitty/bookreport/ui/settings/SettingsScreen.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.towitty.bookreport.ui.settings - -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import com.towitty.bookreport.R - -@Composable -fun SettingsScreen(modifier: Modifier = Modifier) { - Text(text = stringResource(id = R.string.label_settings),modifier = modifier.fillMaxSize()) -} \ No newline at end of file diff --git a/app/src/main/res/drawable/circle_border.xml b/app/src/main/res/drawable/circle_border.xml new file mode 100644 index 0000000..012a714 --- /dev/null +++ b/app/src/main/res/drawable/circle_border.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_baseline_arrow_forward.xml b/app/src/main/res/drawable/ic_baseline_arrow_forward.xml new file mode 100644 index 0000000..19201cb --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_arrow_forward.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_outline_arrow_back.xml b/app/src/main/res/drawable/ic_outline_arrow_back.xml new file mode 100644 index 0000000..afe2d49 --- /dev/null +++ b/app/src/main/res/drawable/ic_outline_arrow_back.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_outline_cancel.xml b/app/src/main/res/drawable/ic_outline_cancel.xml new file mode 100644 index 0000000..e24726a --- /dev/null +++ b/app/src/main/res/drawable/ic_outline_cancel.xml @@ -0,0 +1,12 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_outline_photo_camera.xml b/app/src/main/res/drawable/ic_outline_photo_camera.xml new file mode 100644 index 0000000..e71e765 --- /dev/null +++ b/app/src/main/res/drawable/ic_outline_photo_camera.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_rounded_add_circle.xml b/app/src/main/res/drawable/ic_rounded_add_circle.xml new file mode 100644 index 0000000..fbd91ea --- /dev/null +++ b/app/src/main/res/drawable/ic_rounded_add_circle.xml @@ -0,0 +1,8 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_sharp_person_edit.xml b/app/src/main/res/drawable/ic_sharp_person_edit.xml new file mode 100644 index 0000000..3f6d2f1 --- /dev/null +++ b/app/src/main/res/drawable/ic_sharp_person_edit.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/rounded_button.xml b/app/src/main/res/drawable/rounded_button.xml new file mode 100644 index 0000000..d3917fe --- /dev/null +++ b/app/src/main/res/drawable/rounded_button.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml new file mode 100644 index 0000000..33c1dd2 --- /dev/null +++ b/app/src/main/res/layout/activity_settings.xml @@ -0,0 +1,21 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_add_tag_bottom_sheet.xml b/app/src/main/res/layout/fragment_add_tag_bottom_sheet.xml new file mode 100644 index 0000000..1c841de --- /dev/null +++ b/app/src/main/res/layout/fragment_add_tag_bottom_sheet.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + +