diff --git a/app/build.gradle.kts b/app/build.gradle.kts index e86faf7..3fddd23 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -7,6 +7,10 @@ android { namespace = "com.alom.androidstudy1" compileSdk = 34 + buildFeatures { + viewBinding = true + } + defaultConfig { applicationId = "com.alom.androidstudy1" minSdk = 26 @@ -42,4 +46,11 @@ dependencies { testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) + + implementation(libs.lifecycle.viewmodel.ktx) + implementation(libs.lifecycle.livedata.ktx) + implementation(libs.lifecycle.runtime.ktx) + + testImplementation(libs.spring.boot.starter.test) + testImplementation(libs.mockk) } \ No newline at end of file diff --git a/app/src/main/java/com/alom/androidstudy1/MainActivity.kt b/app/src/main/java/com/alom/androidstudy1/MainActivity.kt index c1bcbbf..234d2ef 100644 --- a/app/src/main/java/com/alom/androidstudy1/MainActivity.kt +++ b/app/src/main/java/com/alom/androidstudy1/MainActivity.kt @@ -1,20 +1,52 @@ package com.alom.androidstudy1 import android.os.Bundle -import androidx.activity.enableEdgeToEdge +import android.util.Log +import android.widget.Toast import androidx.appcompat.app.AppCompatActivity -import androidx.core.view.ViewCompat -import androidx.core.view.WindowInsetsCompat +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import com.alom.androidstudy1.databinding.ActivityMainBinding +import kotlinx.coroutines.launch class MainActivity : AppCompatActivity() { + private lateinit var binding:ActivityMainBinding + private lateinit var mainViewModel: MainViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - enableEdgeToEdge() - setContentView(R.layout.activity_main) - ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets -> - val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) - v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom) - insets + binding = ActivityMainBinding.inflate(layoutInflater) + setContentView(binding.root) + + val sharedPreference = getSharedPreferences("alom", MODE_PRIVATE) + + // viewModel의 인자값으로 repository를 넘겨 줌 + val repository = RepositoryImpl(sharedPreference) + val factory = ViewModelFactory(repository) + + mainViewModel = ViewModelProvider(this, factory).get(MainViewModel::class.java) + + lifecycleScope.launch { + lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { + launch { + mainViewModel.currentMemo.collect{ memo -> + binding.etMemo.setText(memo) + Log.d("MainActivity", "output memo: $memo") + } + } + } + } + + binding.btnSave.setOnClickListener { + val newMemo = binding.etMemo.text.toString().trim() + if(newMemo.isEmpty()){ + Toast.makeText(this, "저장할 메모를 입력해 주세요", Toast.LENGTH_SHORT).show() + } + else { + mainViewModel.updateValue(newMemo) + Toast.makeText(this, "저장됨", Toast.LENGTH_SHORT).show() + } } } } \ No newline at end of file diff --git a/app/src/main/java/com/alom/androidstudy1/MainViewModel.kt b/app/src/main/java/com/alom/androidstudy1/MainViewModel.kt new file mode 100644 index 0000000..eee4c0b --- /dev/null +++ b/app/src/main/java/com/alom/androidstudy1/MainViewModel.kt @@ -0,0 +1,39 @@ +package com.alom.androidstudy1 + +import android.annotation.SuppressLint +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + +class MainViewModel(private val repository: Repository): ViewModel() { + private var _currentMemo = MutableStateFlow("") + + val currentMemo: StateFlow + get() = _currentMemo.asStateFlow() + + init { + + viewModelScope.launch { + val memo = withContext(Dispatchers.IO) { + repository.getMemo() + } + _currentMemo.emit(memo) + } + } + + + @SuppressLint("CommitPrefEdits") + fun updateValue(input: String) { + viewModelScope.launch { + _currentMemo.emit(input) + withContext(Dispatchers.IO) { + repository.setMemo(input) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/alom/androidstudy1/Repository.kt b/app/src/main/java/com/alom/androidstudy1/Repository.kt new file mode 100644 index 0000000..51e6a69 --- /dev/null +++ b/app/src/main/java/com/alom/androidstudy1/Repository.kt @@ -0,0 +1,9 @@ +package com.alom.androidstudy1 + +import android.content.SharedPreferences +import kotlinx.coroutines.flow.MutableStateFlow + +interface Repository { + suspend fun getMemo(): String + suspend fun setMemo(input: String) +} \ No newline at end of file diff --git a/app/src/main/java/com/alom/androidstudy1/RepositoryImpl.kt b/app/src/main/java/com/alom/androidstudy1/RepositoryImpl.kt new file mode 100644 index 0000000..ddda8db --- /dev/null +++ b/app/src/main/java/com/alom/androidstudy1/RepositoryImpl.kt @@ -0,0 +1,14 @@ +package com.alom.androidstudy1 + +import android.content.SharedPreferences +import kotlinx.coroutines.flow.MutableStateFlow + +class RepositoryImpl(private val sharedPreference: SharedPreferences): Repository { + override suspend fun getMemo(): String { + return sharedPreference.getString("memo", "").toString() + } + + override suspend fun setMemo(input: String) { + sharedPreference.edit().putString("memo", input).apply() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/alom/androidstudy1/ViewModelFactory.kt b/app/src/main/java/com/alom/androidstudy1/ViewModelFactory.kt new file mode 100644 index 0000000..2a66f79 --- /dev/null +++ b/app/src/main/java/com/alom/androidstudy1/ViewModelFactory.kt @@ -0,0 +1,14 @@ +package com.alom.androidstudy1 + +import android.content.SharedPreferences +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider + +class ViewModelFactory(private val repository: Repository): ViewModelProvider.Factory { + override fun create(modelClass: Class): T { + if (modelClass.isAssignableFrom(MainViewModel::class.java)) { + return MainViewModel(repository) as T + } + throw IllegalArgumentException("ViewModel class not found") + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 86a5d97..ac18dc2 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -8,12 +8,44 @@ tools:context=".MainActivity"> + + + + + app:layout_constraintEnd_toEndOf="parent" + android:layout_marginBottom="10dp" + android:text="저장" + android:textColor="@color/white" + android:background="#9281CD" + /> \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e0e8c2c..97afca5 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,6 +9,9 @@ appcompat = "1.7.0" material = "1.12.0" activity = "1.9.2" constraintlayout = "2.1.4" +lifecycleVersion = "2.8.4" +mockk = "1.12.5" + [libraries] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } @@ -20,6 +23,13 @@ material = { group = "com.google.android.material", name = "material", version.r androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "activity" } androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } +lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycleVersion"} +lifecycle-livedata-ktx = { group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version.ref = "lifecycleVersion"} +lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleVersion"} + +mockk = { module = "io.mockk:mockk", version.ref = "mockk" } +spring-boot-starter-test = { module = "org.springframework.boot:spring-boot-starter-test" } + [plugins] android-application = { id = "com.android.application", version.ref = "agp" } jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }