From 2fca150cd8ba91e05228c5383f43470dc8920515 Mon Sep 17 00:00:00 2001 From: TTRR1007 Date: Mon, 14 Jul 2025 18:15:56 +0900 Subject: [PATCH] =?UTF-8?q?[=EA=B9=80=EC=98=81=EC=88=98=5FAndroid]=2012?= =?UTF-8?q?=EC=A3=BC=EC=B0=A8=20=EA=B3=BC=EC=A0=9C=20=EC=A0=9C=EC=B6=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/.name | 1 + .idea/AndroidProjectSystem.xml | 6 ++ .idea/compiler.xml | 6 ++ .idea/deploymentTargetSelector.xml | 10 +++ .idea/gradle.xml | 19 ++++ .idea/kotlinc.xml | 6 ++ .idea/migrations.xml | 10 +++ .idea/misc.xml | 5 ++ .idea/runConfigurations.xml | 17 ++++ ...Checkout_at_2025-07-13__8_26__Changes_.xml | 4 + .../shelved.patch" | 49 ++++++++++ .../shelved.patch" | 0 .idea/vcs.xml | 6 ++ .kotlin/errors/errors-1752480219597.log | 57 ++++++++++++ app/build.gradle.kts | 30 ++++++- app/src/main/AndroidManifest.xml | 1 + .../bcsd_android_2025_1/AddEditActivity.kt | 49 ++++++++++ .../bcsd_android_2025_1/MainActivity.kt | 75 +++++++++++++++- .../com/example/bcsd_android_2025_1/Word.kt | 12 +++ .../bcsd_android_2025_1/WordAdapter.kt | 35 ++++++++ .../bcsd_android_2025_1/WordDatabase.kt | 27 ++++++ .../bcsd_android_2025_1/WordRepository.kt | 11 +++ .../example/bcsd_android_2025_1/WordRoom.kt | 24 +++++ .../bcsd_android_2025_1/WordViewModel.kt | 28 ++++++ app/src/main/res/layout/activity_add_edit.xml | 61 +++++++++++++ app/src/main/res/layout/activity_main.xml | 90 +++++++++++++++---- app/src/main/res/layout/item_word.xml | 31 +++++++ app/src/main/res/values/strings.xml | 16 ++++ build.gradle.kts | 8 ++ gradle/libs.versions.toml | 33 +++++-- settings.gradle.kts | 1 + 31 files changed, 698 insertions(+), 30 deletions(-) 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/gradle.xml create mode 100644 .idea/kotlinc.xml create mode 100644 .idea/migrations.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/runConfigurations.xml create mode 100644 .idea/shelf/Uncommitted_changes_before_Checkout_at_2025-07-13__8_26__Changes_.xml create mode 100644 ".idea/shelf/Uncommitted_changes_before_Checkout_at_2025-07-13_\354\230\244\355\233\204_8_26_[Changes]/shelved.patch" create mode 100644 ".idea/shelf/Uncommitted_changes_before_Checkout_at_2025-07-13_\354\230\244\355\233\204_8_26_[Changes]1/shelved.patch" create mode 100644 .idea/vcs.xml create mode 100644 .kotlin/errors/errors-1752480219597.log create mode 100644 app/src/main/java/com/example/bcsd_android_2025_1/AddEditActivity.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/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/WordRoom.kt create mode 100644 app/src/main/java/com/example/bcsd_android_2025_1/WordViewModel.kt create mode 100644 app/src/main/res/layout/activity_add_edit.xml create mode 100644 app/src/main/res/layout/item_word.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/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/kotlinc.xml b/.idea/kotlinc.xml new file mode 100644 index 0000000..c22b6fa --- /dev/null +++ b/.idea/kotlinc.xml @@ -0,0 +1,6 @@ + + + + + \ 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..2cdc89a --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,5 @@ + + + + + \ 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/shelf/Uncommitted_changes_before_Checkout_at_2025-07-13__8_26__Changes_.xml b/.idea/shelf/Uncommitted_changes_before_Checkout_at_2025-07-13__8_26__Changes_.xml new file mode 100644 index 0000000..9984e75 --- /dev/null +++ b/.idea/shelf/Uncommitted_changes_before_Checkout_at_2025-07-13__8_26__Changes_.xml @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git "a/.idea/shelf/Uncommitted_changes_before_Checkout_at_2025-07-13_\354\230\244\355\233\204_8_26_[Changes]/shelved.patch" "b/.idea/shelf/Uncommitted_changes_before_Checkout_at_2025-07-13_\354\230\244\355\233\204_8_26_[Changes]/shelved.patch" new file mode 100644 index 0000000..2ad04a8 --- /dev/null +++ "b/.idea/shelf/Uncommitted_changes_before_Checkout_at_2025-07-13_\354\230\244\355\233\204_8_26_[Changes]/shelved.patch" @@ -0,0 +1,49 @@ +Index: app/src/main/java/com/example/bcsd_android_2025_1/MainActivity.kt +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP +<+>package com.example.bcsd_android_2025_1\r\nimport android.os.Bundle\r\nimport android.widget.Button\r\nimport android.widget.TextView\r\nimport androidx.appcompat.app.AppCompatActivity\r\nimport androidx.lifecycle.lifecycleScope\r\nimport androidx.recyclerview.widget.LinearLayoutManager\r\nimport androidx.recyclerview.widget.RecyclerView\r\nimport kotlinx.coroutines.Job\r\nimport kotlinx.coroutines.delay\r\nimport kotlinx.coroutines.isActive\r\nimport kotlinx.coroutines.launch\r\n\r\nclass MainActivity : AppCompatActivity() {\r\n\r\n private lateinit var tvTimer : TextView\r\n private lateinit var btnLap : Button\r\n private lateinit var btnStart : Button\r\n private lateinit var btnStop : Button\r\n\r\n private lateinit var rvRecord : RecyclerView\r\n private lateinit var lapAdapter: LapAdapter\r\n\r\n private var isRunning = false\r\n private var runningTime = 0L\r\n private var job: Job? = null\r\n\r\n override fun onCreate(savedInstanceState: Bundle?) {\r\n super.onCreate(savedInstanceState)\r\n setContentView(R.layout.activity_main)\r\n\r\n tvTimer = findViewById(R.id.tv_main_timer)\r\n btnLap = findViewById(R.id.btn_main_lap)\r\n btnStart = findViewById(R.id.btn_main_start)\r\n btnStop = findViewById(R.id.btn_main_stop)\r\n rvRecord = findViewById(R.id.rv_main_record)\r\n rvRecord.layoutManager = LinearLayoutManager(this)\r\n lapAdapter = LapAdapter()\r\n rvRecord.adapter = lapAdapter\r\n\r\n btnStart.setOnClickListener {\r\n if (isRunning) {\r\n pauseTimer()\r\n } else {\r\n startTimer()\r\n }\r\n }\r\n\r\n btnStop.setOnClickListener {\r\n stopTimer()\r\n }\r\n\r\n btnLap.setOnClickListener {\r\n if (isRunning) {\r\n lapAdapter.addLap(formatTime(runningTime))\r\n }\r\n }\r\n }\r\n\r\n private fun startTimer() {\r\n val startTime = System.currentTimeMillis() - runningTime\r\n job = lifecycleScope.launch {\r\n while (isActive) {\r\n runningTime = System.currentTimeMillis() - startTime\r\n tvTimer.text = formatTime(runningTime)\r\n delay(10)\r\n }\r\n }\r\n isRunning = true\r\n btnStart.text = getString(R.string.btn_pause)\r\n }\r\n\r\n private fun pauseTimer() {\r\n job?.cancel()\r\n isRunning = false\r\n btnStart.text = getString(R.string.btn_start)\r\n }\r\n\r\n private fun stopTimer() {\r\n job?.cancel()\r\n runningTime = 0L\r\n isRunning = false\r\n tvTimer.text = getString(R.string.tv_time)\r\n btnStart.text = getString(R.string.btn_start)\r\n lapAdapter.clear()\r\n }\r\n\r\n private fun formatTime(ms: Long): String {\r\n val min = (ms/1000)/60\r\n val sec = (ms/1000)%60\r\n val millisec = (ms%1000) / 10\r\n return String.format(\"%02d:%02d:%02d\", min, sec, millisec)\r\n }\r\n\r\n\r\n}\r\n\r\n +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +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 +--- a/app/src/main/java/com/example/bcsd_android_2025_1/MainActivity.kt (revision a566fccc5cae9f9de481ed1c810a31555d2dca50) ++++ b/app/src/main/java/com/example/bcsd_android_2025_1/MainActivity.kt (date 1751870590293) +@@ -10,6 +10,7 @@ + import kotlinx.coroutines.delay + import kotlinx.coroutines.isActive + import kotlinx.coroutines.launch ++import java.util.Locale + + class MainActivity : AppCompatActivity() { + +@@ -89,7 +90,7 @@ + val min = (ms/1000)/60 + val sec = (ms/1000)%60 + val millisec = (ms%1000) / 10 +- return String.format("%02d:%02d:%02d", min, sec, millisec) ++ return String.format(Locale.getDefault(),"%02d:%02d:%02d", min, sec, millisec) + } + + +Index: app/src/main/res/layout/item.xml +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP +<+>\r\n\r\n\r\n \r\n \r\n\r\n\r\n +=================================================================== +diff --git a/app/src/main/res/layout/item.xml b/app/src/main/res/layout/item.xml +--- a/app/src/main/res/layout/item.xml (revision a566fccc5cae9f9de481ed1c810a31555d2dca50) ++++ b/app/src/main/res/layout/item.xml (date 1752170182341) +@@ -1,5 +1,6 @@ + + + +@@ -18,4 +19,5 @@ + android:layout_marginStart="5dp"/> + + ++ + +\ No newline at end of file diff --git "a/.idea/shelf/Uncommitted_changes_before_Checkout_at_2025-07-13_\354\230\244\355\233\204_8_26_[Changes]1/shelved.patch" "b/.idea/shelf/Uncommitted_changes_before_Checkout_at_2025-07-13_\354\230\244\355\233\204_8_26_[Changes]1/shelved.patch" new file mode 100644 index 0000000..e69de29 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/.kotlin/errors/errors-1752480219597.log b/.kotlin/errors/errors-1752480219597.log new file mode 100644 index 0000000..a44172d --- /dev/null +++ b/.kotlin/errors/errors-1752480219597.log @@ -0,0 +1,57 @@ +kotlin version: 2.1.10 +error message: java.lang.IncompatibleClassChangeError: class com.google.devtools.ksp.common.PersistentMap cannot inherit from final class org.jetbrains.kotlin.com.intellij.util.io.PersistentHashMap + at java.base/java.lang.ClassLoader.defineClass1(Native Method) + at java.base/java.lang.ClassLoader.defineClass(Unknown Source) + at java.base/java.security.SecureClassLoader.defineClass(Unknown Source) + at java.base/java.net.URLClassLoader.defineClass(Unknown Source) + at java.base/java.net.URLClassLoader$1.run(Unknown Source) + at java.base/java.net.URLClassLoader$1.run(Unknown Source) + at java.base/java.security.AccessController.doPrivileged(Unknown Source) + at java.base/java.net.URLClassLoader.findClass(Unknown Source) + at java.base/java.lang.ClassLoader.loadClass(Unknown Source) + at java.base/java.lang.ClassLoader.loadClass(Unknown Source) + at java.base/java.lang.ClassLoader.defineClass1(Native Method) + at java.base/java.lang.ClassLoader.defineClass(Unknown Source) + at java.base/java.security.SecureClassLoader.defineClass(Unknown Source) + at java.base/java.net.URLClassLoader.defineClass(Unknown Source) + at java.base/java.net.URLClassLoader$1.run(Unknown Source) + at java.base/java.net.URLClassLoader$1.run(Unknown Source) + at java.base/java.security.AccessController.doPrivileged(Unknown Source) + at java.base/java.net.URLClassLoader.findClass(Unknown Source) + at java.base/java.lang.ClassLoader.loadClass(Unknown Source) + at java.base/java.lang.ClassLoader.loadClass(Unknown Source) + at com.google.devtools.ksp.common.IncrementalContextBase.(IncrementalContextBase.kt:103) + at com.google.devtools.ksp.IncrementalContext.(IncrementalContext.kt:64) + at com.google.devtools.ksp.AbstractKotlinSymbolProcessingExtension$doAnalysis$2.invoke(KotlinSymbolProcessingExtension.kt:192) + at com.google.devtools.ksp.AbstractKotlinSymbolProcessingExtension$doAnalysis$2.invoke(KotlinSymbolProcessingExtension.kt:189) + at com.google.devtools.ksp.AbstractKotlinSymbolProcessingExtension.handleException(KotlinSymbolProcessingExtension.kt:414) + at com.google.devtools.ksp.AbstractKotlinSymbolProcessingExtension.doAnalysis(KotlinSymbolProcessingExtension.kt:189) + at org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(TopDownAnalyzerFacadeForJVM.kt:112) + at org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration$default(TopDownAnalyzerFacadeForJVM.kt:75) + at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.analyze$lambda$9(KotlinToJVMBytecodeCompiler.kt:356) + at org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport.analyzeAndReport(AnalyzerWithCompilerReport.kt:112) + at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.analyze(KotlinToJVMBytecodeCompiler.kt:347) + at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.runFrontendAndGenerateIrUsingClassicFrontend(KotlinToJVMBytecodeCompiler.kt:177) + at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli(KotlinToJVMBytecodeCompiler.kt:102) + at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:169) + at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:43) + at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:102) + at org.jetbrains.kotlin.cli.common.CLICompiler.exec(CLICompiler.kt:316) + at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1706) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source) + at java.base/java.lang.reflect.Method.invoke(Unknown Source) + at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(Unknown Source) + at java.rmi/sun.rmi.transport.Transport$1.run(Unknown Source) + at java.rmi/sun.rmi.transport.Transport$1.run(Unknown Source) + at java.base/java.security.AccessController.doPrivileged(Unknown Source) + at java.rmi/sun.rmi.transport.Transport.serviceCall(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(Unknown Source) + at java.base/java.security.AccessController.doPrivileged(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(Unknown Source) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) + at java.base/java.lang.Thread.run(Unknown Source) + + diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 5791375..fa454ae 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,22 +1,34 @@ plugins { alias(libs.plugins.android.application) alias(libs.plugins.kotlin.android) + id("com.google.devtools.ksp") version "2.1.10-1.0.29" + + } +apply(plugin = "kotlin-kapt") + android { namespace = "com.example.bcsd_android_2025_1" - compileSdk = 34 + compileSdk = 35 defaultConfig { applicationId = "com.example.bcsd_android_2025_1" minSdk = 26 - targetSdk = 34 + targetSdk = 35 versionCode = 1 versionName = "1.0" + multiDexEnabled = true + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } + buildFeatures { + dataBinding = true + } + + buildTypes { release { isMinifyEnabled = false @@ -33,10 +45,10 @@ android { kotlinOptions { jvmTarget = "11" } + } dependencies { - implementation(libs.androidx.core.ktx) implementation(libs.androidx.appcompat) implementation(libs.material) @@ -45,4 +57,16 @@ dependencies { testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) + + implementation(libs.androidx.room.runtime) + implementation(libs.androidx.room.ktx) + ksp(libs.androidx.room.compiler) + + implementation(libs.androidx.lifecycle.viewmodel.ktx) + implementation(libs.androidx.lifecycle.livedata.ktx) + implementation(libs.androidx.lifecycle.runtime.ktx) + implementation(libs.androidx.lifecycle.viewmodel.savedstate) + + implementation(libs.kotlinx.coroutines.android) + ksp("androidx.room:room-compiler:2.7.2") } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4c80941..67a4a40 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -12,6 +12,7 @@ android:supportsRtl="true" android:theme="@style/Theme.BCSD_Android_20251" tools:targetApi="31"> + diff --git a/app/src/main/java/com/example/bcsd_android_2025_1/AddEditActivity.kt b/app/src/main/java/com/example/bcsd_android_2025_1/AddEditActivity.kt new file mode 100644 index 0000000..9c89a26 --- /dev/null +++ b/app/src/main/java/com/example/bcsd_android_2025_1/AddEditActivity.kt @@ -0,0 +1,49 @@ +package com.example.bcsd_android_2025_1 + +import android.content.Intent +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.ViewModelProvider +import com.example.bcsd_android_2025_1.databinding.ActivityAddEditBinding + +class AddEditActivity : AppCompatActivity() { + private lateinit var binding: ActivityAddEditBinding + private var originalWord: Word? = null + private lateinit var wordViewModel: WordViewModel + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityAddEditBinding.inflate(layoutInflater) + setContentView(binding.root) + + wordViewModel = ViewModelProvider(this)[WordViewModel::class.java] + + originalWord = intent.getSerializableExtra("word_to_edit") as? Word + originalWord?.let { + binding.editTvAddWord.setText(it.word) + binding.editTvAddMean.setText(it.meaning) + } + + binding.button.setOnClickListener { + val word = binding.editTvAddWord.text.toString() + val mean = binding.editTvAddMean.text.toString() + + if (word.isBlank() || mean.isBlank()) { + return@setOnClickListener + } + + if (originalWord != null) { + val updated = originalWord!!.copy(word = word, meaning = mean) + val result = Intent().apply { + putExtra("updated_word", updated) + } + setResult(RESULT_OK, result) + finish() + } else { + wordViewModel.insert(Word(word = word, meaning = mean)) + finish() + } + + } + } +} \ 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..6a2f320 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,81 @@ package com.example.bcsd_android_2025_1 +import android.content.Intent +import android.os.Build 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 binding: ActivityMainBinding + private lateinit var wordViewModel: WordViewModel + private lateinit var wordAdapter: WordAdapter + + private var selectedWord: Word? = null + + private val editWordLauncher = registerForActivityResult( + ActivityResultContracts.StartActivityForResult() + ) { result -> + if (result.resultCode == RESULT_OK) { + val updateWord = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + result.data?.getSerializableExtra("updated_word", Word::class.java) + } else { + intent.getSerializableExtra("updated_word") as? Word + } + updateWord?.let { + wordViewModel.update(it) + selectedWord = it + showSelectedWord(it) + } + } + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main) + binding = ActivityMainBinding.inflate(layoutInflater) + setContentView(binding.root) + + wordViewModel = ViewModelProvider(this)[WordViewModel::class.java] + + wordAdapter = WordAdapter { word -> + selectedWord = word + showSelectedWord(word) + } + + binding.recyclerviewMain.adapter = wordAdapter + binding.recyclerviewMain.layoutManager = LinearLayoutManager(this) + + wordViewModel.allWords.observe(this) { words -> + wordAdapter.submitList(words) + } + + binding.imageBtnMainDelete.setOnClickListener{ + selectedWord?.let{ + wordViewModel.delete(it) + clearSelectedWord() + } + } + binding.imageBtnMainEdit.setOnClickListener { + selectedWord?.let{ + val intent = Intent(this, AddEditActivity::class.java) + intent.putExtra("word_to_edit",it) + editWordLauncher.launch(intent) + } + } + binding.imageBtnMainPlus.setOnClickListener { + startActivity(Intent(this, AddEditActivity::class.java)) + } + } + private fun showSelectedWord(word: Word) { + binding.tvMainWord.text = word.word + binding.tvMainMean.text = word.meaning + } + private fun clearSelectedWord() { + binding.tvMainWord.text = getString(R.string.tv_main_note_word) + binding.tvMainMean.text = getString(R.string.tv_main_note_mean) + selectedWord = null } } \ 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..872749e --- /dev/null +++ b/app/src/main/java/com/example/bcsd_android_2025_1/Word.kt @@ -0,0 +1,12 @@ +package com.example.bcsd_android_2025_1 + +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 new file mode 100644 index 0000000..a01b81c --- /dev/null +++ b/app/src/main/java/com/example/bcsd_android_2025_1/WordAdapter.kt @@ -0,0 +1,35 @@ +package com.example.bcsd_android_2025_1 + +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 +) : ListAdapter(DiffCallback()){ + inner class WordViewHolder(private val binding: ItemWordBinding) : + RecyclerView.ViewHolder(binding.root) { + fun bind(word: Word) { + binding.itemTvWord.text = word.word + binding.itemTvMean.text = word.meaning + binding.root.setOnClickListener { onItemClick(word) } + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WordViewHolder { + val binding = ItemWordBinding.inflate(LayoutInflater.from(parent.context), 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/WordDatabase.kt b/app/src/main/java/com/example/bcsd_android_2025_1/WordDatabase.kt new file mode 100644 index 0000000..e49b56c --- /dev/null +++ b/app/src/main/java/com/example/bcsd_android_2025_1/WordDatabase.kt @@ -0,0 +1,27 @@ +package com.example.bcsd_android_2025_1 + +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 wordRoom(): WordRoom + + 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 new file mode 100644 index 0000000..bb72254 --- /dev/null +++ b/app/src/main/java/com/example/bcsd_android_2025_1/WordRepository.kt @@ -0,0 +1,11 @@ +package com.example.bcsd_android_2025_1 + +import androidx.lifecycle.LiveData + +class WordRepository(private val dao: WordRoom) { + 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/WordRoom.kt b/app/src/main/java/com/example/bcsd_android_2025_1/WordRoom.kt new file mode 100644 index 0000000..a79c462 --- /dev/null +++ b/app/src/main/java/com/example/bcsd_android_2025_1/WordRoom.kt @@ -0,0 +1,24 @@ +package com.example.bcsd_android_2025_1 + +import androidx.lifecycle.LiveData +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import androidx.room.Update + +@Dao +interface WordRoom{ + @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/WordViewModel.kt b/app/src/main/java/com/example/bcsd_android_2025_1/WordViewModel.kt new file mode 100644 index 0000000..20cd00d --- /dev/null +++ b/app/src/main/java/com/example/bcsd_android_2025_1/WordViewModel.kt @@ -0,0 +1,28 @@ +package com.example.bcsd_android_2025_1 + +import android.app.Application +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.LiveData +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.launch + +class WordViewModel(application: Application) : AndroidViewModel(application){ + private val repository: WordRepository + val allWords: LiveData> + + init { + val dao = WordDatabase.getDatabase(application).wordRoom() + 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) + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_add_edit.xml b/app/src/main/res/layout/activity_add_edit.xml new file mode 100644 index 0000000..04b3643 --- /dev/null +++ b/app/src/main/res/layout/activity_add_edit.xml @@ -0,0 +1,61 @@ + + + + + + + + + + +