diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..b589d56 --- /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..d1d02e7 --- /dev/null +++ b/.idea/deploymentTargetSelector.xml @@ -0,0 +1,18 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..0897082 --- /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..fdf8d99 --- /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..adb8ae0 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/other.xml b/.idea/other.xml new file mode 100644 index 0000000..1811ddc --- /dev/null +++ b/.idea/other.xml @@ -0,0 +1,450 @@ + + + + + + \ 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 d3941a7..eb80b66 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,12 +1,21 @@ +import java.util.Properties + plugins { alias(libs.plugins.android.application) alias(libs.plugins.jetbrains.kotlin.android) } +val properties = Properties() +properties.load(project.rootProject.file("local.properties").inputStream()) + android { namespace = "com.alom.androidstudy2" compileSdk = 34 + buildFeatures{ + viewBinding = true + } + defaultConfig { applicationId = "com.alom.androidstudy2" minSdk = 26 @@ -15,6 +24,7 @@ android { versionName = "1.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + buildConfigField ("String", "API_KEY", properties.getProperty("API_KEY")) } buildTypes { @@ -23,6 +33,9 @@ android { proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") } } + buildFeatures { + buildConfig = true + } compileOptions { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 @@ -42,4 +55,11 @@ dependencies { testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) + + implementation(libs.retrofit) + implementation(libs.converter.gson) + implementation(libs.okhttp) + implementation(libs.logging.interceptor) + implementation(libs.androidx.lifecycle.viewmodel.ktx) + implementation(libs.glide) } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0acee2a..a4a40b3 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,6 +2,8 @@ + + + diff --git a/app/src/main/java/com/alom/androidstudy2/AddGoodsActivity.kt b/app/src/main/java/com/alom/androidstudy2/AddGoodsActivity.kt new file mode 100644 index 0000000..1b6ec76 --- /dev/null +++ b/app/src/main/java/com/alom/androidstudy2/AddGoodsActivity.kt @@ -0,0 +1,44 @@ +package com.alom.androidstudy2 + +import android.content.Intent +import android.os.Bundle +import android.widget.Toast +import androidx.activity.enableEdgeToEdge +import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.lifecycle.ViewModelProvider +import com.alom.androidstudy2.databinding.ActivityAddGoodsBinding + +class AddGoodsActivity : AppCompatActivity() { + private lateinit var binding: ActivityAddGoodsBinding + private lateinit var mainViewModel: MainViewModel + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityAddGoodsBinding.inflate(layoutInflater) + setContentView(binding.root) + + val repository = ItemRepositoryImpl() + val factory = ViewModelFactory(repository) + + mainViewModel = ViewModelProvider(this, factory)[MainViewModel::class.java] + + binding.btnSaveGoods.setOnClickListener{ + val title = binding.etGoodsTitle.text.toString() + val price = binding.etGoodsPrice.text.toString() + val time = binding.etGoodsTime.text.toString() + + val item = itemRequest(title, price, time) + + mainViewModel.addValue(item) + + Toast.makeText(this, "item 추가됨", Toast.LENGTH_SHORT).show() + finish() + } + + binding.tvBack.setOnClickListener { + finish() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/alom/androidstudy2/ItemAPI.kt b/app/src/main/java/com/alom/androidstudy2/ItemAPI.kt new file mode 100644 index 0000000..11ff21a --- /dev/null +++ b/app/src/main/java/com/alom/androidstudy2/ItemAPI.kt @@ -0,0 +1,15 @@ +package com.alom.androidstudy2 + +import retrofit2.Call +import retrofit2.http.Body +import retrofit2.http.POST + +interface ItemAPI { + @POST("rpc/add_item2") + suspend fun addItems( + @Body body: itemRequest + ): Unit + + @POST("rpc/get_item2") + suspend fun getItems(): ItemResponse +} \ No newline at end of file diff --git a/app/src/main/java/com/alom/androidstudy2/ItemListAdapter.kt b/app/src/main/java/com/alom/androidstudy2/ItemListAdapter.kt new file mode 100644 index 0000000..ed0d37d --- /dev/null +++ b/app/src/main/java/com/alom/androidstudy2/ItemListAdapter.kt @@ -0,0 +1,43 @@ +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.ItemDataBinding +import com.bumptech.glide.Glide + +class ItemListAdapter: ListAdapter( + object: DiffUtil.ItemCallback(){ + override fun areItemsTheSame(oldItem: Data, newItem: Data): Boolean = oldItem.id == newItem.id + + override fun areContentsTheSame(oldItem: Data, newItem: Data): Boolean = oldItem == newItem + } +) { + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): ViewHolder { + val binding = LayoutInflater.from(parent.context).inflate(R.layout.item_data, parent, false) + return ViewHolder(ItemDataBinding.bind(binding)) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val goods = getItem(position) + holder.bind(goods) + } + + inner class ViewHolder(private val binding: ItemDataBinding): RecyclerView.ViewHolder(binding.root) { + fun bind(goods: Data) { + with(binding) { + Glide.with(root.context) + .load(goods.imageUrl) + .into(ivGoodsImageItem) + tvGoodsTitleItem.text = goods.title + tvGoodsPriceItem.text = goods.price + tvGoodsTimeItem.text = goods.time + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/alom/androidstudy2/ItemRepository.kt b/app/src/main/java/com/alom/androidstudy2/ItemRepository.kt new file mode 100644 index 0000000..3044d96 --- /dev/null +++ b/app/src/main/java/com/alom/androidstudy2/ItemRepository.kt @@ -0,0 +1,6 @@ +package com.alom.androidstudy2 + +interface ItemRepository { + suspend fun getItem(): List + suspend fun addItem(item: itemRequest) +} \ No newline at end of file diff --git a/app/src/main/java/com/alom/androidstudy2/ItemRepositoryImpl.kt b/app/src/main/java/com/alom/androidstudy2/ItemRepositoryImpl.kt new file mode 100644 index 0000000..10b9795 --- /dev/null +++ b/app/src/main/java/com/alom/androidstudy2/ItemRepositoryImpl.kt @@ -0,0 +1,14 @@ +package com.alom.androidstudy2 + +import retrofit2.Call + +class ItemRepositoryImpl : ItemRepository { + override suspend fun getItem(): List { + val response = RetrofitClient.itemApi.getItems() + return response.data ?: emptyList() + } + + override suspend fun addItem(item: itemRequest) { + RetrofitClient.itemApi.addItems(item) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/alom/androidstudy2/ItemResponse.kt b/app/src/main/java/com/alom/androidstudy2/ItemResponse.kt new file mode 100644 index 0000000..1e5dc24 --- /dev/null +++ b/app/src/main/java/com/alom/androidstudy2/ItemResponse.kt @@ -0,0 +1,25 @@ +package com.alom.androidstudy2 + +import com.google.gson.annotations.SerializedName + +data class ItemResponse( + @SerializedName("result") + val result: String, + @SerializedName("message") + val message: String, + @SerializedName("data") + val data: List +) + +data class Data( + @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/MainActivity.kt b/app/src/main/java/com/alom/androidstudy2/MainActivity.kt index 54ce3ce..65d0e27 100644 --- a/app/src/main/java/com/alom/androidstudy2/MainActivity.kt +++ b/app/src/main/java/com/alom/androidstudy2/MainActivity.kt @@ -1,20 +1,59 @@ package com.alom.androidstudy2 +import android.content.Intent import android.os.Bundle -import androidx.activity.enableEdgeToEdge +import android.util.Log +import android.widget.Toast +import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity -import androidx.core.view.ViewCompat -import androidx.core.view.WindowInsetsCompat +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope +import androidx.recyclerview.widget.LinearLayoutManager +import com.alom.androidstudy2.databinding.ActivityMainBinding +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response class MainActivity : AppCompatActivity() { + private lateinit var binding: ActivityMainBinding + private lateinit var mainViewModel: MainViewModel + private val itemAdapter by lazy { ItemListAdapter() } + 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 repository = ItemRepositoryImpl() + val factory = ViewModelFactory(repository) + + mainViewModel = ViewModelProvider(this, factory)[MainViewModel::class.java] + + binding.rvItems.apply { + layoutManager = LinearLayoutManager(this@MainActivity) + adapter = itemAdapter + } + + binding.tvAdd.setOnClickListener{ + val intent = Intent(this, AddGoodsActivity::class.java) + startActivity(intent) } + + lifecycleScope.launch { + mainViewModel.currentData.collect { dataList -> + itemAdapter.submitList(dataList) + } + } + + mainViewModel.updateValue() + } + + override fun onResume() { + super.onResume() + + mainViewModel.updateValue() } } \ 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..3555168 --- /dev/null +++ b/app/src/main/java/com/alom/androidstudy2/MainViewModel.kt @@ -0,0 +1,34 @@ +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: ItemRepository) : ViewModel() { + private var _currentItem = MutableStateFlow>(emptyList()) + + val currentData: StateFlow> + get() = _currentItem.asStateFlow() + + init { // 뷰모델이 실행될때 싫행 + updateValue() + } + + fun updateValue() { + viewModelScope.launch { + val item = repository.getItem() + _currentItem.emit(item) // 불러온 데이터 emit + } + } + + fun addValue(item: itemRequest){ + viewModelScope.launch { + repository.addItem(item) + } + } +} \ No newline at end of file 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..2b05225 --- /dev/null +++ b/app/src/main/java/com/alom/androidstudy2/RetrofitClient.kt @@ -0,0 +1,39 @@ +package com.alom.androidstudy2 + +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import java.util.concurrent.TimeUnit + +object RetrofitClient { + private const val BASE_URL = "https://goaplrynweyxovekoezl.supabase.co/rest/v1/" + + private fun createOkHttpClient(): OkHttpClient { + val interceptor = HttpLoggingInterceptor() + interceptor.level = HttpLoggingInterceptor.Level.BODY + + + return OkHttpClient.Builder() + .addInterceptor { chain -> + val original = chain.request() + val requestBuilder = original.newBuilder() + .header("apikey", BuildConfig.API_KEY) + val request = requestBuilder.build() + chain.proceed(request) + } + .connectTimeout(20, TimeUnit.SECONDS) + .readTimeout(20, TimeUnit.SECONDS) + .writeTimeout(20, TimeUnit.SECONDS) + .addNetworkInterceptor(interceptor) + .build() + } + + private val retrofit: Retrofit = Retrofit.Builder() + .baseUrl(BASE_URL) + .addConverterFactory(GsonConverterFactory.create()) + .client(createOkHttpClient()) + .build() + + val itemApi: ItemAPI = retrofit.create(ItemAPI::class.java) +} \ No newline at end of file diff --git a/app/src/main/java/com/alom/androidstudy2/ViewModelFactory.kt b/app/src/main/java/com/alom/androidstudy2/ViewModelFactory.kt new file mode 100644 index 0000000..d7a1719 --- /dev/null +++ b/app/src/main/java/com/alom/androidstudy2/ViewModelFactory.kt @@ -0,0 +1,13 @@ +package com.alom.androidstudy2 + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider + +class ViewModelFactory (private val repository: ItemRepository): 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/itemRequest.kt b/app/src/main/java/com/alom/androidstudy2/itemRequest.kt new file mode 100644 index 0000000..3c51175 --- /dev/null +++ b/app/src/main/java/com/alom/androidstudy2/itemRequest.kt @@ -0,0 +1,12 @@ +package com.alom.androidstudy2 + +import com.google.gson.annotations.SerializedName + +data class itemRequest ( + @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/res/layout/activity_add_goods.xml b/app/src/main/res/layout/activity_add_goods.xml new file mode 100644 index 0000000..cee06ce --- /dev/null +++ b/app/src/main/res/layout/activity_add_goods.xml @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + +