diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index d3941a7..24aaf57 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -1,6 +1,19 @@
+import java.util.Properties
+
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.jetbrains.kotlin.android)
+
+}
+
+
+fun getApiKey(): String {
+ val properties = Properties()
+ val localPropertiesFile = rootProject.file("local.properties")
+ if (localPropertiesFile.exists()) {
+ localPropertiesFile.inputStream().use { properties.load(it) }
+ }
+ return properties.getProperty("API_KEY") ?: ""
}
android {
@@ -13,6 +26,8 @@ android {
targetSdk = 34
versionCode = 1
versionName = "1.0"
+ buildConfigField("String", "API_KEY", "\"${getApiKey()}\"")
+
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
@@ -27,6 +42,12 @@ android {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
+ viewBinding {
+ enable = true
+ }
+ buildFeatures{
+ buildConfig=true
+ }
kotlinOptions {
jvmTarget = "1.8"
}
@@ -42,4 +63,13 @@ dependencies {
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
+ implementation("com.github.bumptech.glide:glide:4.16.0")
+ annotationProcessor("com.github.bumptech.glide:compiler:4.16.0")
+
+ implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0")
+ implementation ("com.google.code.gson:gson:2.8.6")
+ implementation("com.squareup.retrofit2:retrofit:2.9.0")
+ implementation("com.squareup.retrofit2:converter-gson:2.9.0")
+ implementation("com.squareup.okhttp3:logging-interceptor:4.9.0")
+ implementation("com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2")
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 0acee2a..56af475 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -12,6 +12,9 @@
android:supportsRtl="true"
android:theme="@style/Theme.Android_study_14_2"
tools:targetApi="31">
+
diff --git a/app/src/main/java/com/alom/androidstudy2/AddMemoActivity.kt b/app/src/main/java/com/alom/androidstudy2/AddMemoActivity.kt
new file mode 100644
index 0000000..3cce763
--- /dev/null
+++ b/app/src/main/java/com/alom/androidstudy2/AddMemoActivity.kt
@@ -0,0 +1,52 @@
+package com.alom.androidstudy2
+
+import android.content.Context
+import android.os.Bundle
+import androidx.activity.enableEdgeToEdge
+import androidx.activity.viewModels
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.view.ViewCompat
+import androidx.core.view.WindowInsetsCompat
+import com.alom.androidstudy2.databinding.ActivityAddMemoBinding
+
+class AddMemoActivity : AppCompatActivity() {
+
+ private lateinit var binding: ActivityAddMemoBinding
+
+ private val viewModel: MainViewModel by viewModels {
+ ViewModelFactory(
+ RepositoryImpl(
+ getSharedPreferences("MyPrefs", Context.MODE_PRIVATE),
+ RetrofitClient.apiService
+ )
+ )
+ }
+
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ binding = ActivityAddMemoBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ binding.btnSave.setOnClickListener {
+ val title = binding.etTitle.text.toString()
+ val price = binding.etPrice.text.toString()
+ val time = binding.etTime.text.toString()
+
+
+ val newMemo = Memo(
+ title = title,
+ price = price,
+ imageUrl = "",
+ time = time,
+ id = (0..9999999).random()
+ )
+
+
+ viewModel.addMemo(newMemo)
+
+
+ finish()
+ }
+ }
+}
diff --git a/app/src/main/java/com/alom/androidstudy2/AddMemoRequest.kt b/app/src/main/java/com/alom/androidstudy2/AddMemoRequest.kt
new file mode 100644
index 0000000..37318ec
--- /dev/null
+++ b/app/src/main/java/com/alom/androidstudy2/AddMemoRequest.kt
@@ -0,0 +1,11 @@
+package com.alom.androidstudy2
+
+import com.google.gson.annotations.SerializedName
+
+
+
+data class AddMemoRequest(
+ @SerializedName("p_title") val title: String,
+ @SerializedName("p_price") val price: String,
+ @SerializedName("p_time") val time: String
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/alom/androidstudy2/AddMemoResponse.kt b/app/src/main/java/com/alom/androidstudy2/AddMemoResponse.kt
new file mode 100644
index 0000000..95d5a1c
--- /dev/null
+++ b/app/src/main/java/com/alom/androidstudy2/AddMemoResponse.kt
@@ -0,0 +1,8 @@
+package com.alom.androidstudy2
+
+import com.google.gson.annotations.SerializedName
+
+data class AddMemoResponse(
+ @SerializedName("result") val result: Int
+
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/alom/androidstudy2/GetMemoResponse.kt b/app/src/main/java/com/alom/androidstudy2/GetMemoResponse.kt
new file mode 100644
index 0000000..5bde647
--- /dev/null
+++ b/app/src/main/java/com/alom/androidstudy2/GetMemoResponse.kt
@@ -0,0 +1,9 @@
+package com.alom.androidstudy2
+
+import com.google.gson.annotations.SerializedName
+
+data class GetMemoResponse(
+ @SerializedName("result") val result: Int,
+ @SerializedName("message") val message: String,
+ @SerializedName("data") val data: List
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/alom/androidstudy2/MainActivity.kt b/app/src/main/java/com/alom/androidstudy2/MainActivity.kt
index 54ce3ce..9ef042d 100644
--- a/app/src/main/java/com/alom/androidstudy2/MainActivity.kt
+++ b/app/src/main/java/com/alom/androidstudy2/MainActivity.kt
@@ -1,20 +1,51 @@
package com.alom.androidstudy2
+import android.content.Context
+import android.content.Intent
import android.os.Bundle
import androidx.activity.enableEdgeToEdge
+import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
+import androidx.lifecycle.lifecycleScope
+import com.alom.androidstudy2.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
+
+ private lateinit var binding: ActivityMainBinding
+ private lateinit var memoListAdapter: MemoListAdapter
+
+
+ private val viewModel: MainViewModel by viewModels {
+ ViewModelFactory(
+ RepositoryImpl(
+ getSharedPreferences("MyPrefs", Context.MODE_PRIVATE),
+ RetrofitClient.apiService
+ )
+ )
+ }
+
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)
+
+
+ memoListAdapter = MemoListAdapter()
+ binding.recyclerView.adapter = memoListAdapter
+
+
+ lifecycleScope.launchWhenStarted {
+ viewModel.currentMemo.collect { memoList ->
+ memoListAdapter.submitList(memoList)
+ }
+ }
+
+
+ binding.btnAdd.setOnClickListener {
+ val intent = Intent(this, AddMemoActivity::class.java)
+ startActivity(intent)
}
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/com/alom/androidstudy2/MainViewModel.kt b/app/src/main/java/com/alom/androidstudy2/MainViewModel.kt
new file mode 100644
index 0000000..7543aff
--- /dev/null
+++ b/app/src/main/java/com/alom/androidstudy2/MainViewModel.kt
@@ -0,0 +1,42 @@
+package com.alom.androidstudy2
+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: MemoRepository) : ViewModel() {
+
+ private var _currentMemo = MutableStateFlow>(emptyList())
+ val currentMemo: StateFlow> get() = _currentMemo.asStateFlow()
+
+ init {
+
+ loadMemoList()
+ }
+
+ fun loadMemoList() {
+ viewModelScope.launch {
+ val memos = withContext(Dispatchers.IO) {
+ repository.getMemos()
+ }
+ _currentMemo.emit(memos)
+ }
+ }
+
+ fun addMemo(memo: Memo) {
+ viewModelScope.launch {
+
+ withContext(Dispatchers.IO) {
+ repository.addMemo(memo)
+ }
+
+ val memos = repository.getMemos()
+
+ _currentMemo.emit(memos)
+ }
+ }
+}
diff --git a/app/src/main/java/com/alom/androidstudy2/MainviewModelFactory.kt b/app/src/main/java/com/alom/androidstudy2/MainviewModelFactory.kt
new file mode 100644
index 0000000..dfbd991
--- /dev/null
+++ b/app/src/main/java/com/alom/androidstudy2/MainviewModelFactory.kt
@@ -0,0 +1,13 @@
+package com.alom.androidstudy2
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+
+class ViewModelFactory(private val repository: MemoRepository): 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/java/com/alom/androidstudy2/Memo.kt b/app/src/main/java/com/alom/androidstudy2/Memo.kt
new file mode 100644
index 0000000..39a6cc2
--- /dev/null
+++ b/app/src/main/java/com/alom/androidstudy2/Memo.kt
@@ -0,0 +1,12 @@
+package com.alom.androidstudy2
+
+import com.google.gson.annotations.SerializedName
+
+data class Memo(
+ @SerializedName("id") val id: Int,
+ @SerializedName("title") val title: String,
+ @SerializedName("price") val price: String,
+ @SerializedName("image_url") val imageUrl: String,
+ @SerializedName("time") val time: String
+)
+
diff --git a/app/src/main/java/com/alom/androidstudy2/MemoListAdapter.kt b/app/src/main/java/com/alom/androidstudy2/MemoListAdapter.kt
new file mode 100644
index 0000000..1111896
--- /dev/null
+++ b/app/src/main/java/com/alom/androidstudy2/MemoListAdapter.kt
@@ -0,0 +1,46 @@
+package com.alom.androidstudy2
+
+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.alom.androidstudy2.databinding.ItemMemoBinding
+import com.bumptech.glide.Glide
+
+
+object MemoDiffUtil : DiffUtil.ItemCallback() {
+ override fun areItemsTheSame(oldItem: Memo, newItem: Memo): Boolean {
+
+ return oldItem.id == newItem.id
+ }
+
+ override fun areContentsTheSame(oldItem: Memo, newItem: Memo): Boolean {
+
+ return oldItem == newItem
+ }
+}
+
+
+class MemoListAdapter : ListAdapter(MemoDiffUtil) {
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MemoViewHolder {
+ val inflater = LayoutInflater.from(parent.context)
+ val binding = ItemMemoBinding.inflate(inflater, parent, false)
+ return MemoViewHolder(binding)
+ }
+
+ override fun onBindViewHolder(holder: MemoViewHolder, position: Int) {
+ val memoItem = getItem(position)
+ holder.bind(memoItem)
+ }
+
+ inner class MemoViewHolder(private val binding: ItemMemoBinding) : RecyclerView.ViewHolder(binding.root) {
+ fun bind(memo: Memo) {
+ binding.tvTitle.text = memo.title
+ binding.tvPrice.text = memo.price
+ binding.tvTime.text=memo.time
+ Glide.with(binding.imgItem).load(memo.imageUrl).into(binding.imgItem)
+ }
+ }
+}
diff --git a/app/src/main/java/com/alom/androidstudy2/MemoRepository.kt b/app/src/main/java/com/alom/androidstudy2/MemoRepository.kt
new file mode 100644
index 0000000..e214235
--- /dev/null
+++ b/app/src/main/java/com/alom/androidstudy2/MemoRepository.kt
@@ -0,0 +1,10 @@
+package com.alom.androidstudy2
+
+interface MemoRepository {
+ suspend fun getMemos(): List
+ suspend fun addMemo(memo: Memo): AddMemoResponse
+ suspend fun setMemo(memo: Memo)
+
+
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/alom/androidstudy2/MemoRepositoryImpl.kt b/app/src/main/java/com/alom/androidstudy2/MemoRepositoryImpl.kt
new file mode 100644
index 0000000..d74f7b6
--- /dev/null
+++ b/app/src/main/java/com/alom/androidstudy2/MemoRepositoryImpl.kt
@@ -0,0 +1,59 @@
+package com.alom.androidstudy2
+
+
+
+import android.content.SharedPreferences
+import com.google.gson.Gson
+
+class RepositoryImpl(
+ private val sharedPreferences: SharedPreferences,
+ private val apiService: RetrofitInterface
+) : MemoRepository {
+
+ private val gson = Gson()
+ private val memoKey = "memo_list"
+
+
+ override suspend fun setMemo(memo: Memo) {
+
+ val memoJson = sharedPreferences.getString(memoKey, null)
+
+ val memoList = if (memoJson.isNullOrEmpty()) {
+ mutableListOf()
+ } else {
+ gson.fromJson(memoJson, Array::class.java).toMutableList()
+ }
+
+ memoList.add(memo)
+
+ val updatedJson = gson.toJson(memoList)
+
+ sharedPreferences.edit().putString(memoKey, updatedJson).apply()
+ }
+
+ override suspend fun getMemos(): List {
+ val memoJson = sharedPreferences.getString(memoKey, null)
+ return if (memoJson.isNullOrEmpty()) {
+ emptyList()
+ } else {
+ gson.fromJson(memoJson, Array::class.java).toList()
+ }
+ }
+
+
+
+ override suspend fun addMemo(memo: Memo): AddMemoResponse {
+ setMemo(memo)
+
+ val request = AddMemoRequest(memo.title, memo.price, memo.time)
+ val response = apiService.addMemo(request)
+
+ return if (response.isSuccessful) {
+
+ response.body() ?: AddMemoResponse(result = 200)
+ } else {
+
+ AddMemoResponse(result = response.code())
+ }
+ }
+}
diff --git a/app/src/main/java/com/alom/androidstudy2/RetrofitClient.kt b/app/src/main/java/com/alom/androidstudy2/RetrofitClient.kt
new file mode 100644
index 0000000..9c25284
--- /dev/null
+++ b/app/src/main/java/com/alom/androidstudy2/RetrofitClient.kt
@@ -0,0 +1,41 @@
+package com.alom.androidstudy2
+import okhttp3.Interceptor
+import okhttp3.OkHttpClient
+import retrofit2.Retrofit
+import retrofit2.converter.gson.GsonConverterFactory
+import com.google.gson.GsonBuilder
+import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory
+
+
+object RetrofitClient {
+ val apiService: RetrofitInterface
+ get() = instance.create(RetrofitInterface::class.java)
+
+ private val instance: Retrofit
+ get() {
+ val gson = GsonBuilder()
+ .setLenient()
+ .create()
+
+
+ val apiKeyInterceptor = Interceptor { chain ->
+ val originalRequest = chain.request()
+ val newRequest = originalRequest.newBuilder()
+ .addHeader("getApiKey", BuildConfig.API_KEY)
+ .build()
+ chain.proceed(newRequest)
+ }
+
+
+ val okHttpClient = OkHttpClient.Builder()
+ .addInterceptor(apiKeyInterceptor)
+ .build()
+
+ return Retrofit.Builder()
+ .baseUrl("https://goaplrynweyxovekoezl.supabase.co/rest/v1/")
+ .client(okHttpClient)
+ .addConverterFactory(GsonConverterFactory.create(gson))
+ .addCallAdapterFactory(CoroutineCallAdapterFactory())
+ .build()
+ }
+}
diff --git a/app/src/main/java/com/alom/androidstudy2/RetrofitInterface.kt b/app/src/main/java/com/alom/androidstudy2/RetrofitInterface.kt
new file mode 100644
index 0000000..3232951
--- /dev/null
+++ b/app/src/main/java/com/alom/androidstudy2/RetrofitInterface.kt
@@ -0,0 +1,18 @@
+package com.alom.androidstudy2
+import retrofit2.Response
+import retrofit2.http.Body
+import retrofit2.http.GET
+import retrofit2.http.POST
+
+interface RetrofitInterface {
+ @POST("/rpc/add_item4")
+ suspend fun addMemo(
+ @Body request:AddMemoRequest
+ ): Response
+
+ @GET("/rpc/get_item4")
+ suspend fun getMemo(
+ ):Response
+
+
+}
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_add_memo.xml b/app/src/main/res/layout/activity_add_memo.xml
new file mode 100644
index 0000000..43e3c40
--- /dev/null
+++ b/app/src/main/res/layout/activity_add_memo.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ 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..fbff796 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -6,14 +6,25 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
+
-
+ app:layout_constraintBottom_toBottomOf="parent"
+ android:layout_margin="16dp"/>
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_memo.xml b/app/src/main/res/layout/item_memo.xml
new file mode 100644
index 0000000..a04e729
--- /dev/null
+++ b/app/src/main/res/layout/item_memo.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+