From 2a597be43e98a693f7a291fd211a1b89680bcc43 Mon Sep 17 00:00:00 2001 From: kkyoungchan Date: Fri, 18 Jul 2025 17:27:06 +0900 Subject: [PATCH 1/2] assignment12 --- .../com/example/bcsd_android_2025_1/AddEditWordActivity.kt | 4 ++++ app/src/main/java/com/example/bcsd_android_2025_1/Word.kt | 4 ++++ .../java/com/example/bcsd_android_2025_1/WordAdapter.kt | 4 ++++ .../main/java/com/example/bcsd_android_2025_1/WordDao.kt | 4 ++++ .../java/com/example/bcsd_android_2025_1/WordDatabase.kt | 4 ++++ .../java/com/example/bcsd_android_2025_1/WordRepository.kt | 4 ++++ .../java/com/example/bcsd_android_2025_1/WordViewModel.kt | 4 ++++ app/src/main/res/drawable/add_icon.xml | 5 +++++ app/src/main/res/drawable/delete_icon.xml | 5 +++++ app/src/main/res/drawable/edit_icon.xml | 5 +++++ app/src/main/res/layout/activity_add_edit__word.xml | 6 ++++++ app/src/main/res/layout/item_word.xml | 6 ++++++ 12 files changed, 55 insertions(+) create mode 100644 app/src/main/java/com/example/bcsd_android_2025_1/AddEditWordActivity.kt create mode 100644 app/src/main/java/com/example/bcsd_android_2025_1/Word.kt create mode 100644 app/src/main/java/com/example/bcsd_android_2025_1/WordAdapter.kt create mode 100644 app/src/main/java/com/example/bcsd_android_2025_1/WordDao.kt create mode 100644 app/src/main/java/com/example/bcsd_android_2025_1/WordDatabase.kt create mode 100644 app/src/main/java/com/example/bcsd_android_2025_1/WordRepository.kt create mode 100644 app/src/main/java/com/example/bcsd_android_2025_1/WordViewModel.kt create mode 100644 app/src/main/res/drawable/add_icon.xml create mode 100644 app/src/main/res/drawable/delete_icon.xml create mode 100644 app/src/main/res/drawable/edit_icon.xml create mode 100644 app/src/main/res/layout/activity_add_edit__word.xml create mode 100644 app/src/main/res/layout/item_word.xml diff --git a/app/src/main/java/com/example/bcsd_android_2025_1/AddEditWordActivity.kt b/app/src/main/java/com/example/bcsd_android_2025_1/AddEditWordActivity.kt new file mode 100644 index 0000000..f96cdad --- /dev/null +++ b/app/src/main/java/com/example/bcsd_android_2025_1/AddEditWordActivity.kt @@ -0,0 +1,4 @@ +package com.example.bcsd_android_2025_1 + +class AddEditWordActivity { +} \ No newline at end of file diff --git a/app/src/main/java/com/example/bcsd_android_2025_1/Word.kt b/app/src/main/java/com/example/bcsd_android_2025_1/Word.kt new file mode 100644 index 0000000..86790cb --- /dev/null +++ b/app/src/main/java/com/example/bcsd_android_2025_1/Word.kt @@ -0,0 +1,4 @@ +package com.example.bcsd_android_2025_1 + +class Word { +} \ No newline at end of file diff --git a/app/src/main/java/com/example/bcsd_android_2025_1/WordAdapter.kt b/app/src/main/java/com/example/bcsd_android_2025_1/WordAdapter.kt new file mode 100644 index 0000000..552e09c --- /dev/null +++ b/app/src/main/java/com/example/bcsd_android_2025_1/WordAdapter.kt @@ -0,0 +1,4 @@ +package com.example.bcsd_android_2025_1 + +class WordAdapter { +} \ No newline at end of file diff --git a/app/src/main/java/com/example/bcsd_android_2025_1/WordDao.kt b/app/src/main/java/com/example/bcsd_android_2025_1/WordDao.kt new file mode 100644 index 0000000..29c7a04 --- /dev/null +++ b/app/src/main/java/com/example/bcsd_android_2025_1/WordDao.kt @@ -0,0 +1,4 @@ +package com.example.bcsd_android_2025_1 + +class WordDao { +} \ No newline at end of file diff --git a/app/src/main/java/com/example/bcsd_android_2025_1/WordDatabase.kt b/app/src/main/java/com/example/bcsd_android_2025_1/WordDatabase.kt new file mode 100644 index 0000000..da4f4aa --- /dev/null +++ b/app/src/main/java/com/example/bcsd_android_2025_1/WordDatabase.kt @@ -0,0 +1,4 @@ +package com.example.bcsd_android_2025_1 + +class WordDatabase { +} \ No newline at end of file diff --git a/app/src/main/java/com/example/bcsd_android_2025_1/WordRepository.kt b/app/src/main/java/com/example/bcsd_android_2025_1/WordRepository.kt new file mode 100644 index 0000000..b5216d7 --- /dev/null +++ b/app/src/main/java/com/example/bcsd_android_2025_1/WordRepository.kt @@ -0,0 +1,4 @@ +package com.example.bcsd_android_2025_1 + +class WordRepository { +} \ No newline at end of file diff --git a/app/src/main/java/com/example/bcsd_android_2025_1/WordViewModel.kt b/app/src/main/java/com/example/bcsd_android_2025_1/WordViewModel.kt new file mode 100644 index 0000000..af2c14e --- /dev/null +++ b/app/src/main/java/com/example/bcsd_android_2025_1/WordViewModel.kt @@ -0,0 +1,4 @@ +package com.example.bcsd_android_2025_1 + +class WordViewModel { +} \ No newline at end of file diff --git a/app/src/main/res/drawable/add_icon.xml b/app/src/main/res/drawable/add_icon.xml new file mode 100644 index 0000000..9f83b8f --- /dev/null +++ b/app/src/main/res/drawable/add_icon.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/delete_icon.xml b/app/src/main/res/drawable/delete_icon.xml new file mode 100644 index 0000000..883bcaa --- /dev/null +++ b/app/src/main/res/drawable/delete_icon.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/edit_icon.xml b/app/src/main/res/drawable/edit_icon.xml new file mode 100644 index 0000000..3c53db7 --- /dev/null +++ b/app/src/main/res/drawable/edit_icon.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/layout/activity_add_edit__word.xml b/app/src/main/res/layout/activity_add_edit__word.xml new file mode 100644 index 0000000..cdc89f2 --- /dev/null +++ b/app/src/main/res/layout/activity_add_edit__word.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_word.xml b/app/src/main/res/layout/item_word.xml new file mode 100644 index 0000000..cdc89f2 --- /dev/null +++ b/app/src/main/res/layout/item_word.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file From 53237ce549bb9302cecef731d7d96f7ad66e9abe Mon Sep 17 00:00:00 2001 From: kkyoungchan Date: Fri, 18 Jul 2025 17:30:32 +0900 Subject: [PATCH 2/2] assignment12 --- .idea/.gitignore | 3 + .idea/.name | 1 + .idea/AndroidProjectSystem.xml | 6 ++ .idea/compiler.xml | 6 ++ .idea/deploymentTargetSelector.xml | 10 +++ .idea/encodings.xml | 6 ++ .idea/gradle.xml | 19 +++++ .idea/migrations.xml | 10 +++ .idea/misc.xml | 10 +++ .idea/runConfigurations.xml | 17 +++++ .idea/vcs.xml | 6 ++ app/build.gradle.kts | 9 +++ app/src/main/AndroidManifest.xml | 1 + .../AddEditWordActivity.kt | 37 +++++++++- .../bcsd_android_2025_1/MainActivity.kt | 50 ++++++++++++- .../com/example/bcsd_android_2025_1/Word.kt | 13 +++- .../bcsd_android_2025_1/WordAdapter.kt | 39 +++++++++- .../example/bcsd_android_2025_1/WordDao.kt | 17 ++++- .../bcsd_android_2025_1/WordDatabase.kt | 26 ++++++- .../bcsd_android_2025_1/WordRepository.kt | 9 ++- .../bcsd_android_2025_1/WordViewModel.kt | 24 +++++- .../res/layout/activity_add_edit__word.xml | 47 ++++++++++-- app/src/main/res/layout/activity_main.xml | 73 ++++++++++++++----- app/src/main/res/layout/item_word.xml | 64 ++++++++++++++-- app/src/main/res/values/strings.xml | 4 + gradle/libs.versions.toml | 10 +++ settings.gradle.kts | 1 + 27 files changed, 478 insertions(+), 40 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/.name create mode 100644 .idea/AndroidProjectSystem.xml create mode 100644 .idea/compiler.xml create mode 100644 .idea/deploymentTargetSelector.xml create mode 100644 .idea/encodings.xml create mode 100644 .idea/gradle.xml create mode 100644 .idea/migrations.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/runConfigurations.xml create mode 100644 .idea/vcs.xml diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..c4e4683 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +BCSD_Android_2025-1 \ No newline at end of file diff --git a/.idea/AndroidProjectSystem.xml b/.idea/AndroidProjectSystem.xml new file mode 100644 index 0000000..4a53bee --- /dev/null +++ b/.idea/AndroidProjectSystem.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..b86273d --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml new file mode 100644 index 0000000..b268ef3 --- /dev/null +++ b/.idea/deploymentTargetSelector.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..97626ba --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..639c779 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/migrations.xml b/.idea/migrations.xml new file mode 100644 index 0000000..f8051a6 --- /dev/null +++ b/.idea/migrations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..74dd639 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000..16660f1 --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /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 5791375..8b7827d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,6 +1,7 @@ plugins { alias(libs.plugins.android.application) alias(libs.plugins.kotlin.android) + id("org.jetbrains.kotlin.kapt") } android { @@ -16,6 +17,10 @@ android { testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } + buildFeatures { + dataBinding = true + viewBinding = true + } buildTypes { release { @@ -37,6 +42,10 @@ android { dependencies { + implementation(libs.androidx.room.runtime) + implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.0") + implementation(libs.androidx.room.ktx) + kapt(libs.androidx.room.compiler) implementation(libs.androidx.core.ktx) implementation(libs.androidx.appcompat) implementation(libs.material) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4c80941..13fa638 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -21,6 +21,7 @@ + \ No newline at end of file diff --git a/app/src/main/java/com/example/bcsd_android_2025_1/AddEditWordActivity.kt b/app/src/main/java/com/example/bcsd_android_2025_1/AddEditWordActivity.kt index f96cdad..fd84cc0 100644 --- a/app/src/main/java/com/example/bcsd_android_2025_1/AddEditWordActivity.kt +++ b/app/src/main/java/com/example/bcsd_android_2025_1/AddEditWordActivity.kt @@ -1,4 +1,39 @@ package com.example.bcsd_android_2025_1 +import android.content.Intent +import android.os.Bundle +import android.widget.Toast +import androidx.appcompat.app.AppCompatActivity +import com.example.bcsd_android_2025_1.databinding.ActivityAddEditWordBinding -class AddEditWordActivity { +class AddEditWordActivity : AppCompatActivity() { + private lateinit var binding: ActivityAddEditWordBinding + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityAddEditWordBinding.inflate(layoutInflater) + setContentView(binding.root) + + val existingWord = intent.getSerializableExtra("word") as? Word + + if (existingWord != null) { + binding.etWord.setText(existingWord.word) + binding.etMeaning.setText(existingWord.meaning) + } + + binding.btnSave.setOnClickListener { + val word = binding.etWord.text.toString() + val meaning = binding.etMeaning.text.toString() + if (word.isNotBlank() && meaning.isNotBlank()) { + val result = Word( + id = existingWord?.id ?: 0, + word = word, + meaning = meaning + ) + setResult(RESULT_OK, Intent().putExtra("result", result)) + finish() + } else { + Toast.makeText(this, R.string.toast_msg, Toast.LENGTH_SHORT).show() + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/example/bcsd_android_2025_1/MainActivity.kt b/app/src/main/java/com/example/bcsd_android_2025_1/MainActivity.kt index 3ffa0eb..96d5605 100644 --- a/app/src/main/java/com/example/bcsd_android_2025_1/MainActivity.kt +++ b/app/src/main/java/com/example/bcsd_android_2025_1/MainActivity.kt @@ -1,14 +1,56 @@ package com.example.bcsd_android_2025_1 +import android.content.Intent import android.os.Bundle -import androidx.activity.enableEdgeToEdge +import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity -import androidx.core.view.ViewCompat -import androidx.core.view.WindowInsetsCompat +import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.LinearLayoutManager +import com.example.bcsd_android_2025_1.databinding.ActivityMainBinding class MainActivity : AppCompatActivity() { + private lateinit var viewModel: WordViewModel + private lateinit var adapter: WordAdapter + + private val launcher = + registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> + if (result.resultCode == RESULT_OK) { + val word = result.data?.getSerializableExtra("result") as? Word + word?.let { + if (it.id == 0) viewModel.insert(it) else viewModel.update(it) + } + } + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main) + val binding = ActivityMainBinding.inflate(layoutInflater) + setContentView(binding.root) + + viewModel = ViewModelProvider(this)[WordViewModel::class.java] + binding.viewModel = viewModel + binding.lifecycleOwner = this + + adapter = WordAdapter( + onItemClick = { word -> viewModel.selectWord(word) }, + onEditClick = { word -> + val intent = Intent(this, AddEditWordActivity::class.java) + intent.putExtra("word", word) + launcher.launch(intent) + }, + onDeleteClick = { word -> viewModel.delete(word) } + ) + + binding.recyclerView.layoutManager = LinearLayoutManager(this) + binding.recyclerView.adapter = adapter + + viewModel.allWords.observe(this) { + adapter.submitList(it) + } + + binding.fab.setOnClickListener { + val intent = Intent(this, AddEditWordActivity::class.java) + launcher.launch(intent) + } } } \ No newline at end of file diff --git a/app/src/main/java/com/example/bcsd_android_2025_1/Word.kt b/app/src/main/java/com/example/bcsd_android_2025_1/Word.kt index 86790cb..9e2484c 100644 --- a/app/src/main/java/com/example/bcsd_android_2025_1/Word.kt +++ b/app/src/main/java/com/example/bcsd_android_2025_1/Word.kt @@ -1,4 +1,13 @@ package com.example.bcsd_android_2025_1 -class Word { -} \ No newline at end of file +import androidx.room.Entity +import androidx.room.PrimaryKey +import java.io.Serializable + +@Entity(tableName = "word_table") + +data class Word( + @PrimaryKey(autoGenerate = true) val id : Int = 0, + val word: String, + val meaning: String +) : Serializable \ No newline at end of file diff --git a/app/src/main/java/com/example/bcsd_android_2025_1/WordAdapter.kt b/app/src/main/java/com/example/bcsd_android_2025_1/WordAdapter.kt index 552e09c..962d66f 100644 --- a/app/src/main/java/com/example/bcsd_android_2025_1/WordAdapter.kt +++ b/app/src/main/java/com/example/bcsd_android_2025_1/WordAdapter.kt @@ -1,4 +1,41 @@ package com.example.bcsd_android_2025_1 -class WordAdapter { +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import com.example.bcsd_android_2025_1.databinding.ItemWordBinding + +class WordAdapter( + private val onItemClick: (Word) -> Unit, + private val onEditClick: (Word) -> Unit, + private val onDeleteClick: (Word) -> Unit +) : ListAdapter(DiffCallback()){ + + inner class WordViewHolder(private val binding: ItemWordBinding) : + RecyclerView.ViewHolder(binding.root) { + fun bind(word: Word) { + binding.word = word + binding.root.setOnClickListener { onItemClick(word) } + binding.btnEdit.setOnClickListener { onEditClick(word) } + binding.btnDelete.setOnClickListener { onDeleteClick(word) } + binding.executePendingBindings() + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WordViewHolder { + val inflater = LayoutInflater.from(parent.context) + val binding = ItemWordBinding.inflate(inflater, parent, false) + return WordViewHolder(binding) + } + + override fun onBindViewHolder(holder: WordViewHolder, position: Int) { + holder.bind(getItem(position)) + } + + class DiffCallback : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: Word, newItem: Word) = oldItem.id == newItem.id + override fun areContentsTheSame(oldItem: Word, newItem: Word) = oldItem == newItem + } } \ No newline at end of file diff --git a/app/src/main/java/com/example/bcsd_android_2025_1/WordDao.kt b/app/src/main/java/com/example/bcsd_android_2025_1/WordDao.kt index 29c7a04..3e08961 100644 --- a/app/src/main/java/com/example/bcsd_android_2025_1/WordDao.kt +++ b/app/src/main/java/com/example/bcsd_android_2025_1/WordDao.kt @@ -1,4 +1,19 @@ package com.example.bcsd_android_2025_1 -class WordDao { +import androidx.lifecycle.LiveData +import androidx.room.* + +@Dao +interface WordDao { + @Query("SELECT * FROM word_table ORDER BY id DESC") + fun getAllWords() : LiveData> + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insert(word: Word) + + @Update + suspend fun update(word: Word) + + @Delete + suspend fun delete(word: Word) } \ No newline at end of file diff --git a/app/src/main/java/com/example/bcsd_android_2025_1/WordDatabase.kt b/app/src/main/java/com/example/bcsd_android_2025_1/WordDatabase.kt index da4f4aa..dec457e 100644 --- a/app/src/main/java/com/example/bcsd_android_2025_1/WordDatabase.kt +++ b/app/src/main/java/com/example/bcsd_android_2025_1/WordDatabase.kt @@ -1,4 +1,28 @@ package com.example.bcsd_android_2025_1 -class WordDatabase { +import android.content.Context +import androidx.room.Database +import androidx.room.Room +import androidx.room.RoomDatabase + +@Database(entities = [Word::class], version = 1) +abstract class WordDatabase : RoomDatabase() { + abstract fun wordDao(): WordDao + + companion object { + @Volatile + private var INSTANCE: WordDatabase? = null + + fun getDatabase(context: Context): WordDatabase { + return INSTANCE ?: synchronized(this) { + val instance = Room.databaseBuilder( + context.applicationContext, + WordDatabase::class.java, + "word_database" + ).build() + INSTANCE = instance + instance + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/example/bcsd_android_2025_1/WordRepository.kt b/app/src/main/java/com/example/bcsd_android_2025_1/WordRepository.kt index b5216d7..847169e 100644 --- a/app/src/main/java/com/example/bcsd_android_2025_1/WordRepository.kt +++ b/app/src/main/java/com/example/bcsd_android_2025_1/WordRepository.kt @@ -1,4 +1,11 @@ package com.example.bcsd_android_2025_1 -class WordRepository { +import androidx.lifecycle.LiveData + +class WordRepository(private val dao: WordDao) { + val allWords : LiveData> = dao.getAllWords() + + suspend fun insert(word: Word) = dao.insert(word) + suspend fun update(word: Word) = dao.update(word) + suspend fun delete(word: Word) = dao.delete(word) } \ No newline at end of file diff --git a/app/src/main/java/com/example/bcsd_android_2025_1/WordViewModel.kt b/app/src/main/java/com/example/bcsd_android_2025_1/WordViewModel.kt index af2c14e..127afaf 100644 --- a/app/src/main/java/com/example/bcsd_android_2025_1/WordViewModel.kt +++ b/app/src/main/java/com/example/bcsd_android_2025_1/WordViewModel.kt @@ -1,4 +1,24 @@ package com.example.bcsd_android_2025_1 -class WordViewModel { -} \ No newline at end of file +import android.app.Application +import androidx.lifecycle.* +import kotlinx.coroutines.launch + +class WordViewModel(application: Application) : AndroidViewModel(application) { + private val repository: WordRepository + val allWords: LiveData> + private val _selectedWord = MutableLiveData() + val selectedWord: LiveData get() = _selectedWord + + init { + val dao = WordDatabase.getDatabase(application).wordDao() + repository = WordRepository(dao) + allWords = repository.allWords + } + fun insert(word: Word) = viewModelScope.launch { repository.insert(word) } + fun update(word: Word) = viewModelScope.launch { repository.update(word) } + fun delete(word: Word) = viewModelScope.launch { repository.delete(word) } + fun selectWord(word: Word) { + _selectedWord.value = word + } +} diff --git a/app/src/main/res/layout/activity_add_edit__word.xml b/app/src/main/res/layout/activity_add_edit__word.xml index cdc89f2..726b467 100644 --- a/app/src/main/res/layout/activity_add_edit__word.xml +++ b/app/src/main/res/layout/activity_add_edit__word.xml @@ -1,6 +1,43 @@ - - + - \ No newline at end of file + + + + + + + + +