diff --git a/jongyee/Week5/.claude/settings.local.json b/jongyee/Week5/.claude/settings.local.json
new file mode 100644
index 0000000..428eb42
--- /dev/null
+++ b/jongyee/Week5/.claude/settings.local.json
@@ -0,0 +1,7 @@
+{
+ "permissions": {
+ "allow": [
+ "Bash(grep *)"
+ ]
+ }
+}
diff --git a/jongyee/Week5/.gitignore b/jongyee/Week5/.gitignore
new file mode 100644
index 0000000..aa724b7
--- /dev/null
+++ b/jongyee/Week5/.gitignore
@@ -0,0 +1,15 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
+local.properties
diff --git a/jongyee/Week5/app/.gitignore b/jongyee/Week5/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/jongyee/Week5/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/jongyee/Week5/app/build.gradle.kts b/jongyee/Week5/app/build.gradle.kts
new file mode 100644
index 0000000..847fab7
--- /dev/null
+++ b/jongyee/Week5/app/build.gradle.kts
@@ -0,0 +1,62 @@
+plugins {
+ alias(libs.plugins.android.application)
+ alias(libs.plugins.kotlin.android)
+}
+
+android {
+ namespace = "com.example.week5"
+ compileSdk = 36
+
+ defaultConfig {
+ applicationId = "com.example.week5"
+ minSdk = 24
+ targetSdk = 36
+ versionCode = 1
+ versionName = "1.0"
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ isMinifyEnabled = false
+ proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
+ }
+ }
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
+ }
+ kotlinOptions {
+ jvmTarget = "11"
+ }
+ buildFeatures {
+ viewBinding = true
+ }
+}
+
+dependencies {
+ implementation(libs.androidx.core.ktx)
+ implementation(libs.androidx.appcompat)
+ implementation(libs.material)
+ implementation(libs.androidx.activity)
+ implementation(libs.androidx.constraintlayout)
+ testImplementation(libs.junit)
+ androidTestImplementation(libs.androidx.junit)
+ androidTestImplementation(libs.androidx.espresso.core)
+ implementation(libs.androidx.datastore.preferences)
+ implementation(libs.gson)
+ implementation(libs.androidx.fragment.ktx)
+ implementation(libs.androidx.lifecycle.viewmodel.ktx)
+ implementation(libs.androidx.lifecycle.livedata.ktx)
+
+ // Retrofit
+ implementation(libs.retrofit)
+ implementation(libs.retrofit.converter.gson)
+ implementation(libs.okhttp.logging.interceptor)
+
+ // Coroutines
+ implementation(libs.kotlinx.coroutines.core)
+ implementation(libs.kotlinx.coroutines.android)
+
+ // Glide
+ implementation(libs.glide)
+}
diff --git a/jongyee/Week5/app/proguard-rules.pro b/jongyee/Week5/app/proguard-rules.pro
new file mode 100644
index 0000000..f1b4245
--- /dev/null
+++ b/jongyee/Week5/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/jongyee/Week5/app/src/androidTest/java/com/example/week5/ExampleInstrumentedTest.kt b/jongyee/Week5/app/src/androidTest/java/com/example/week5/ExampleInstrumentedTest.kt
new file mode 100644
index 0000000..9873c9f
--- /dev/null
+++ b/jongyee/Week5/app/src/androidTest/java/com/example/week5/ExampleInstrumentedTest.kt
@@ -0,0 +1,16 @@
+package com.example.week5
+
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.Assert.*
+
+@RunWith(AndroidJUnit4::class)
+class ExampleInstrumentedTest {
+ @Test
+ fun useAppContext() {
+ val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+ assertEquals("com.example.week5", appContext.packageName)
+ }
+}
diff --git a/jongyee/Week5/app/src/main/AndroidManifest.xml b/jongyee/Week5/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..2aa574c
--- /dev/null
+++ b/jongyee/Week5/app/src/main/AndroidManifest.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jongyee/Week5/app/src/main/java/com/example/week5/ApiClient.kt b/jongyee/Week5/app/src/main/java/com/example/week5/ApiClient.kt
new file mode 100644
index 0000000..16d56c2
--- /dev/null
+++ b/jongyee/Week5/app/src/main/java/com/example/week5/ApiClient.kt
@@ -0,0 +1,41 @@
+package com.example.week5
+
+import okhttp3.Interceptor
+import okhttp3.OkHttpClient
+import okhttp3.logging.HttpLoggingInterceptor
+import retrofit2.Retrofit
+import retrofit2.converter.gson.GsonConverterFactory
+import java.util.concurrent.TimeUnit
+
+object ApiClient {
+
+ private const val BASE_URL = "https://reqres.in/api/"
+ private const val API_KEY = "reqres_701d6373134941c7ba1e30b84be78104"
+
+ private val logging = HttpLoggingInterceptor().apply {
+ level = HttpLoggingInterceptor.Level.BODY
+ }
+
+ private val authInterceptor = Interceptor { chain ->
+ val request = chain.request().newBuilder()
+ .addHeader("x-api-key", API_KEY)
+ .build()
+ chain.proceed(request)
+ }
+
+ private val client = OkHttpClient.Builder()
+ .addInterceptor(authInterceptor)
+ .addInterceptor(logging)
+ .connectTimeout(30, TimeUnit.SECONDS)
+ .readTimeout(30, TimeUnit.SECONDS)
+ .writeTimeout(30, TimeUnit.SECONDS)
+ .build()
+
+ private val retrofit: Retrofit = Retrofit.Builder()
+ .baseUrl(BASE_URL)
+ .client(client)
+ .addConverterFactory(GsonConverterFactory.create())
+ .build()
+
+ val reqResService: ReqResService = retrofit.create(ReqResService::class.java)
+}
diff --git a/jongyee/Week5/app/src/main/java/com/example/week5/BagFragment.kt b/jongyee/Week5/app/src/main/java/com/example/week5/BagFragment.kt
new file mode 100644
index 0000000..4a7210c
--- /dev/null
+++ b/jongyee/Week5/app/src/main/java/com/example/week5/BagFragment.kt
@@ -0,0 +1,36 @@
+package com.example.week5
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.activityViewModels
+import com.example.week5.databinding.FragmentBagBinding
+
+class BagFragment : Fragment() {
+ private var _binding: FragmentBagBinding? = null
+ private val binding get() = _binding!!
+
+ private val sharedViewModel: SharedViewModel by activityViewModels()
+
+ override fun onCreateView(
+ inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ _binding = FragmentBagBinding.inflate(inflater, container, false)
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ binding.bagBtnOrder.setOnClickListener {
+ sharedViewModel.requestNavigateToBuyTab()
+ }
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ _binding = null
+ }
+}
diff --git a/jongyee/Week5/app/src/main/java/com/example/week5/BuyFragment.kt b/jongyee/Week5/app/src/main/java/com/example/week5/BuyFragment.kt
new file mode 100644
index 0000000..d51e806
--- /dev/null
+++ b/jongyee/Week5/app/src/main/java/com/example/week5/BuyFragment.kt
@@ -0,0 +1,81 @@
+package com.example.week5
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.activityViewModels
+import androidx.lifecycle.lifecycleScope
+import androidx.recyclerview.widget.GridLayoutManager
+import com.example.week5.databinding.FragmentBuyBinding
+import com.google.android.material.tabs.TabLayout
+import kotlinx.coroutines.launch
+
+class TopsFragment : Fragment()
+class SaleFragment : Fragment()
+
+class BuyFragment : Fragment() {
+ private var _binding: FragmentBuyBinding? = null
+ private val binding get() = _binding!!
+
+ private val sharedViewModel: SharedViewModel by activityViewModels()
+
+ private val productAdapter by lazy {
+ ProductAdapter(
+ productList = emptyList(),
+ isGrid = true,
+ onHeartClick = { product -> sharedViewModel.toggleFavorite(product) },
+ onItemClick = { product -> sharedViewModel.requestNavigateToDetail(product) }
+ )
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ _binding = FragmentBuyBinding.inflate(inflater, container, false)
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ binding.buyRvProducts.adapter = productAdapter
+ binding.buyRvProducts.layoutManager = GridLayoutManager(context, 2)
+
+ viewLifecycleOwner.lifecycleScope.launch {
+ sharedViewModel.buyProducts.collect { products ->
+ productAdapter.updateData(products)
+ }
+ }
+
+ binding.buyTabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
+ override fun onTabSelected(tab: TabLayout.Tab?) {
+ when (tab?.position) {
+ 0 -> showRecyclerView()
+ 1 -> showChildFragment(TopsFragment())
+ 2 -> showChildFragment(SaleFragment())
+ }
+ }
+ override fun onTabUnselected(tab: TabLayout.Tab?) {}
+ override fun onTabReselected(tab: TabLayout.Tab?) {}
+ })
+ }
+
+ private fun showRecyclerView() {
+ binding.buyRvProducts.visibility = View.VISIBLE
+ binding.buyChildContainer.visibility = View.GONE
+ }
+
+ private fun showChildFragment(fragment: Fragment) {
+ binding.buyRvProducts.visibility = View.GONE
+ binding.buyChildContainer.visibility = View.VISIBLE
+ childFragmentManager.beginTransaction().replace(R.id.buy_child_container, fragment).commit()
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ _binding = null
+ }
+}
diff --git a/jongyee/Week5/app/src/main/java/com/example/week5/DetailFragment.kt b/jongyee/Week5/app/src/main/java/com/example/week5/DetailFragment.kt
new file mode 100644
index 0000000..8300bb4
--- /dev/null
+++ b/jongyee/Week5/app/src/main/java/com/example/week5/DetailFragment.kt
@@ -0,0 +1,60 @@
+package com.example.week5
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.activityViewModels
+import androidx.lifecycle.lifecycleScope
+import com.example.week5.databinding.FragmentDetailBinding
+import kotlinx.coroutines.launch
+
+class DetailFragment : Fragment() {
+ private var _binding: FragmentDetailBinding? = null
+ private val binding get() = _binding!!
+
+ private val sharedViewModel: SharedViewModel by activityViewModels()
+
+ override fun onCreateView(
+ inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ _binding = FragmentDetailBinding.inflate(inflater, container, false)
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ viewLifecycleOwner.lifecycleScope.launch {
+ sharedViewModel.selectedProduct.collect { product ->
+ product?.let {
+ binding.tvDetailCategory.text = it.category
+ binding.tvDetailName.text = it.name
+ binding.tvDetailPrice.text = it.price
+ binding.tvDetailDescription.text = it.description
+ binding.imgDetailProduct.setImageResource(it.imageRes)
+
+ val heartIcon = if (it.isFavorite) R.drawable.ic_heart_filled else R.drawable.ic_heart_empty
+ binding.btnWishlist.setIconResource(heartIcon)
+ }
+ }
+ }
+
+ binding.btnWishlist.setOnClickListener {
+ sharedViewModel.selectedProduct.value?.let { currentProduct ->
+ sharedViewModel.toggleFavorite(currentProduct)
+ }
+ }
+
+ binding.icBack.setOnClickListener {
+ parentFragmentManager.popBackStack()
+ }
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ _binding = null
+ }
+}
diff --git a/jongyee/Week5/app/src/main/java/com/example/week5/FollowingAdapter.kt b/jongyee/Week5/app/src/main/java/com/example/week5/FollowingAdapter.kt
new file mode 100644
index 0000000..ae1c462
--- /dev/null
+++ b/jongyee/Week5/app/src/main/java/com/example/week5/FollowingAdapter.kt
@@ -0,0 +1,34 @@
+package com.example.week5
+
+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.bumptech.glide.Glide
+import com.example.week5.databinding.ItemFollowingBinding
+
+class FollowingAdapter : ListAdapter(DiffCallback) {
+
+ inner class ViewHolder(val binding: ItemFollowingBinding) : RecyclerView.ViewHolder(binding.root)
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
+ val binding = ItemFollowingBinding.inflate(LayoutInflater.from(parent.context), parent, false)
+ return ViewHolder(binding)
+ }
+
+ override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+ val user = getItem(position)
+ holder.binding.tvFollowingName.text = user.firstName
+ Glide.with(holder.itemView.context)
+ .load(user.avatar)
+ .circleCrop()
+ .placeholder(R.drawable.ic_launcher_background)
+ .into(holder.binding.ivFollowingAvatar)
+ }
+
+ companion object DiffCallback : DiffUtil.ItemCallback() {
+ override fun areItemsTheSame(oldItem: UserDto, newItem: UserDto) = oldItem.id == newItem.id
+ override fun areContentsTheSame(oldItem: UserDto, newItem: UserDto) = oldItem == newItem
+ }
+}
diff --git a/jongyee/Week5/app/src/main/java/com/example/week5/HomeFragment.kt b/jongyee/Week5/app/src/main/java/com/example/week5/HomeFragment.kt
new file mode 100644
index 0000000..5b0b715
--- /dev/null
+++ b/jongyee/Week5/app/src/main/java/com/example/week5/HomeFragment.kt
@@ -0,0 +1,52 @@
+package com.example.week5
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.activityViewModels
+import androidx.lifecycle.lifecycleScope
+import androidx.recyclerview.widget.LinearLayoutManager
+import com.example.week5.databinding.FragmentHomeBinding
+import kotlinx.coroutines.launch
+
+class HomeFragment : Fragment() {
+ private var _binding: FragmentHomeBinding? = null
+ private val binding get() = _binding!!
+
+ private val sharedViewModel: SharedViewModel by activityViewModels()
+
+ private val productAdapter by lazy {
+ ProductAdapter(
+ productList = emptyList(),
+ isGrid = false,
+ onItemClick = { product -> sharedViewModel.requestNavigateToDetail(product) }
+ )
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+ _binding = FragmentHomeBinding.inflate(inflater, container, false)
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ binding.homeRvProducts.apply {
+ adapter = productAdapter
+ layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
+ }
+
+ viewLifecycleOwner.lifecycleScope.launch {
+ sharedViewModel.homeProducts.collect { products ->
+ productAdapter.updateData(products)
+ }
+ }
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ _binding = null
+ }
+}
diff --git a/jongyee/Week5/app/src/main/java/com/example/week5/MainActivity.kt b/jongyee/Week5/app/src/main/java/com/example/week5/MainActivity.kt
new file mode 100644
index 0000000..a1d4e7e
--- /dev/null
+++ b/jongyee/Week5/app/src/main/java/com/example/week5/MainActivity.kt
@@ -0,0 +1,52 @@
+package com.example.week5
+
+import android.os.Bundle
+import androidx.activity.viewModels
+import androidx.appcompat.app.AppCompatActivity
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.lifecycleScope
+import com.example.week5.databinding.ActivityMainBinding
+import kotlinx.coroutines.launch
+
+class MainActivity : AppCompatActivity() {
+ private lateinit var binding: ActivityMainBinding
+ private val sharedViewModel: SharedViewModel by viewModels()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ binding = ActivityMainBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ if (savedInstanceState == null) changeFragment(HomeFragment())
+
+ binding.mainBottomNav.setOnItemSelectedListener { item ->
+ when (item.itemId) {
+ R.id.nav_home -> changeFragment(HomeFragment())
+ R.id.nav_buy -> changeFragment(BuyFragment())
+ R.id.nav_wishlist -> changeFragment(WishlistFragment())
+ R.id.nav_bag -> changeFragment(BagFragment())
+ R.id.nav_profile -> changeFragment(ProfileFragment())
+ }
+ true
+ }
+
+ lifecycleScope.launch {
+ sharedViewModel.navigateToDetail.collect {
+ supportFragmentManager.beginTransaction()
+ .replace(R.id.main_container, DetailFragment())
+ .addToBackStack(null)
+ .commit()
+ }
+ }
+
+ lifecycleScope.launch {
+ sharedViewModel.navigateToBuyTab.collect {
+ binding.mainBottomNav.selectedItemId = R.id.nav_buy
+ }
+ }
+ }
+
+ private fun changeFragment(fragment: Fragment) {
+ supportFragmentManager.beginTransaction().replace(R.id.main_container, fragment).commit()
+ }
+}
diff --git a/jongyee/Week5/app/src/main/java/com/example/week5/Product.kt b/jongyee/Week5/app/src/main/java/com/example/week5/Product.kt
new file mode 100644
index 0000000..72b6af3
--- /dev/null
+++ b/jongyee/Week5/app/src/main/java/com/example/week5/Product.kt
@@ -0,0 +1,13 @@
+package com.example.week5
+
+data class Product(
+ val id: Int,
+ val name: String,
+ val price: String,
+ val description: String,
+ val imageRes: Int,
+ val subTitle: String = "",
+ val category: String = "",
+ val colorsCount: String = "",
+ var isFavorite: Boolean = false
+)
diff --git a/jongyee/Week5/app/src/main/java/com/example/week5/ProductAdapter.kt b/jongyee/Week5/app/src/main/java/com/example/week5/ProductAdapter.kt
new file mode 100644
index 0000000..491bda5
--- /dev/null
+++ b/jongyee/Week5/app/src/main/java/com/example/week5/ProductAdapter.kt
@@ -0,0 +1,78 @@
+package com.example.week5
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import com.example.week5.databinding.ItemGridProductBinding
+import com.example.week5.databinding.ItemHomeProductBinding
+
+class ProductAdapter(
+ private var productList: List = emptyList(),
+ private val isGrid: Boolean = false,
+ private val onHeartClick: ((Product) -> Unit)? = null,
+ private val onItemClick: (Product) -> Unit
+) : RecyclerView.Adapter() {
+
+ fun updateData(newList: List) {
+ productList = newList
+ notifyDataSetChanged()
+ }
+
+ inner class HomeViewHolder(private val binding: ItemHomeProductBinding) :
+ RecyclerView.ViewHolder(binding.root) {
+ fun bind(product: Product) {
+ binding.ivProduct.setImageResource(product.imageRes)
+ binding.tvName.text = product.name
+ binding.tvPrice.text = product.price
+ binding.root.setOnClickListener { onItemClick(product) }
+ }
+ }
+
+ inner class GridViewHolder(private val binding: ItemGridProductBinding) :
+ RecyclerView.ViewHolder(binding.root) {
+ fun bind(product: Product) {
+ binding.ivProductGrid.setImageResource(product.imageRes)
+ binding.tvNameGrid.text = product.name
+ binding.tvPriceGrid.text = product.price
+ binding.tvCategoryGrid.text = product.category
+ binding.tvColorsGrid.text = product.colorsCount
+
+ if (product.subTitle.isEmpty()) {
+ binding.tvSubtitleGrid.visibility = View.GONE
+ } else {
+ binding.tvSubtitleGrid.visibility = View.VISIBLE
+ binding.tvSubtitleGrid.text = product.subTitle
+ }
+
+ updateHeartIcon(product.isFavorite)
+ binding.ivHeartGrid.setOnClickListener {
+ onHeartClick?.invoke(product)
+ }
+
+ binding.root.setOnClickListener { onItemClick(product) }
+ }
+
+ private fun updateHeartIcon(isFavorite: Boolean) {
+ val iconRes = if (isFavorite) R.drawable.ic_heart_filled else R.drawable.ic_heart_empty
+ binding.ivHeartGrid.setImageResource(iconRes)
+ }
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
+ val layoutInflater = LayoutInflater.from(parent.context)
+ return if (isGrid) {
+ GridViewHolder(ItemGridProductBinding.inflate(layoutInflater, parent, false))
+ } else {
+ HomeViewHolder(ItemHomeProductBinding.inflate(layoutInflater, parent, false))
+ }
+ }
+
+ override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
+ val product = productList[position]
+ if (holder is HomeViewHolder) holder.bind(product)
+ else if (holder is GridViewHolder) holder.bind(product)
+ }
+
+ override fun getItemCount(): Int = productList.size
+}
diff --git a/jongyee/Week5/app/src/main/java/com/example/week5/ProfileEditActivity.kt b/jongyee/Week5/app/src/main/java/com/example/week5/ProfileEditActivity.kt
new file mode 100644
index 0000000..38e4420
--- /dev/null
+++ b/jongyee/Week5/app/src/main/java/com/example/week5/ProfileEditActivity.kt
@@ -0,0 +1,15 @@
+package com.example.week5
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import com.example.week5.databinding.ActivityProfileEditBinding
+
+class ProfileEditActivity : AppCompatActivity() {
+ private lateinit var binding: ActivityProfileEditBinding
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ binding = ActivityProfileEditBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+ }
+}
diff --git a/jongyee/Week5/app/src/main/java/com/example/week5/ProfileFragment.kt b/jongyee/Week5/app/src/main/java/com/example/week5/ProfileFragment.kt
new file mode 100644
index 0000000..e5ab63b
--- /dev/null
+++ b/jongyee/Week5/app/src/main/java/com/example/week5/ProfileFragment.kt
@@ -0,0 +1,73 @@
+package com.example.week5
+
+import android.content.Intent
+import android.os.Bundle
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.viewModels
+import androidx.recyclerview.widget.LinearLayoutManager
+import com.bumptech.glide.Glide
+import com.example.week5.databinding.FragmentProfileBinding
+
+class ProfileFragment : Fragment() {
+ private var _binding: FragmentProfileBinding? = null
+ private val binding get() = _binding!!
+
+ private val profileViewModel: ProfileViewModel by viewModels()
+ private lateinit var followingAdapter: FollowingAdapter
+
+ override fun onCreateView(
+ inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ _binding = FragmentProfileBinding.inflate(inflater, container, false)
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ followingAdapter = FollowingAdapter()
+ binding.rvFollowing.apply {
+ adapter = followingAdapter
+ layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
+ }
+
+ profileViewModel.userProfile.observe(viewLifecycleOwner) { result ->
+ result.fold(
+ onSuccess = { user ->
+ binding.tvProfileName.text = "${user.firstName} ${user.lastName}"
+ Glide.with(this)
+ .load(user.avatar)
+ .circleCrop()
+ .placeholder(R.drawable.ic_launcher_background)
+ .into(binding.ivProfileAvatar)
+ },
+ onFailure = { e -> Log.e("ProfileFragment", "유저 로드 실패", e) }
+ )
+ }
+
+ profileViewModel.followingList.observe(viewLifecycleOwner) { result ->
+ result.fold(
+ onSuccess = { users ->
+ followingAdapter.submitList(users)
+ binding.tvFollowingTitle.text = "팔로잉 (${users.size})"
+ },
+ onFailure = { e -> Log.e("ProfileFragment", "팔로잉 로드 실패", e) }
+ )
+ }
+
+ binding.btnEditProfile.setOnClickListener {
+ val intent = Intent(requireActivity(), ProfileEditActivity::class.java)
+ startActivity(intent)
+ }
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ _binding = null
+ }
+}
diff --git a/jongyee/Week5/app/src/main/java/com/example/week5/ProfileRepository.kt b/jongyee/Week5/app/src/main/java/com/example/week5/ProfileRepository.kt
new file mode 100644
index 0000000..960a7b2
--- /dev/null
+++ b/jongyee/Week5/app/src/main/java/com/example/week5/ProfileRepository.kt
@@ -0,0 +1,18 @@
+package com.example.week5
+
+class ProfileRepository(private val service: ReqResService) {
+
+ suspend fun getUser(id: Int): Result = try {
+ val response = service.getUser(id)
+ Result.success(response.data)
+ } catch (e: Exception) {
+ Result.failure(e)
+ }
+
+ suspend fun getUsers(page: Int): Result> = try {
+ val response = service.getUsers(page)
+ Result.success(response.data)
+ } catch (e: Exception) {
+ Result.failure(e)
+ }
+}
diff --git a/jongyee/Week5/app/src/main/java/com/example/week5/ProfileViewModel.kt b/jongyee/Week5/app/src/main/java/com/example/week5/ProfileViewModel.kt
new file mode 100644
index 0000000..019ceb5
--- /dev/null
+++ b/jongyee/Week5/app/src/main/java/com/example/week5/ProfileViewModel.kt
@@ -0,0 +1,37 @@
+package com.example.week5
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import kotlinx.coroutines.launch
+
+class ProfileViewModel : ViewModel() {
+
+ private val repository = ProfileRepository(ApiClient.reqResService)
+
+ private val _userProfile = MutableLiveData>()
+ val userProfile: LiveData> = _userProfile
+
+ private val _followingList = MutableLiveData>>()
+ val followingList: LiveData>> = _followingList
+
+ init {
+ loadUserProfile()
+ loadFollowingList()
+ }
+
+ private fun loadUserProfile() {
+ viewModelScope.launch {
+ val result = repository.getUser(1)
+ _userProfile.postValue(result)
+ }
+ }
+
+ private fun loadFollowingList() {
+ viewModelScope.launch {
+ val result = repository.getUsers(1)
+ _followingList.postValue(result)
+ }
+ }
+}
diff --git a/jongyee/Week5/app/src/main/java/com/example/week5/ReqResService.kt b/jongyee/Week5/app/src/main/java/com/example/week5/ReqResService.kt
new file mode 100644
index 0000000..ab3a494
--- /dev/null
+++ b/jongyee/Week5/app/src/main/java/com/example/week5/ReqResService.kt
@@ -0,0 +1,14 @@
+package com.example.week5
+
+import retrofit2.http.GET
+import retrofit2.http.Path
+import retrofit2.http.Query
+
+interface ReqResService {
+
+ @GET("users/{id}")
+ suspend fun getUser(@Path("id") id: Int): SingleUserResponse
+
+ @GET("users")
+ suspend fun getUsers(@Query("page") page: Int): UserListResponse
+}
diff --git a/jongyee/Week5/app/src/main/java/com/example/week5/SharedViewModel.kt b/jongyee/Week5/app/src/main/java/com/example/week5/SharedViewModel.kt
new file mode 100644
index 0000000..3ae54ff
--- /dev/null
+++ b/jongyee/Week5/app/src/main/java/com/example/week5/SharedViewModel.kt
@@ -0,0 +1,119 @@
+package com.example.week5
+
+import android.app.Application
+import androidx.datastore.preferences.core.edit
+import androidx.datastore.preferences.core.stringPreferencesKey
+import androidx.datastore.preferences.preferencesDataStore
+import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.viewModelScope
+import com.google.gson.Gson
+import com.google.gson.reflect.TypeToken
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asSharedFlow
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.launch
+
+private val Application.dataStore by preferencesDataStore(name = "nike_store")
+
+class SharedViewModel(application: Application) : AndroidViewModel(application) {
+ private val dataStore = application.dataStore
+ private val gson = Gson()
+
+ private val HOME_KEY = stringPreferencesKey("home_products")
+ private val BUY_KEY = stringPreferencesKey("buy_products")
+
+ private val _homeProducts = MutableStateFlow>(emptyList())
+ val homeProducts: StateFlow> get() = _homeProducts
+
+ private val _buyProducts = MutableStateFlow>(emptyList())
+ val buyProducts: StateFlow> get() = _buyProducts
+
+ private val _selectedProduct = MutableStateFlow(null)
+ val selectedProduct: StateFlow get() = _selectedProduct
+
+ private val _navigateToDetail = MutableSharedFlow()
+ val navigateToDetail = _navigateToDetail.asSharedFlow()
+
+ private val _navigateToBuyTab = MutableSharedFlow()
+ val navigateToBuyTab = _navigateToBuyTab.asSharedFlow()
+
+ init {
+ loadProducts()
+ }
+
+ private fun loadProducts() {
+ viewModelScope.launch {
+ val prefs = dataStore.data.first()
+ val type = object : TypeToken>() {}.type
+
+ val homeJson = prefs[HOME_KEY]
+ if (homeJson != null) {
+ _homeProducts.value = gson.fromJson(homeJson, type)
+ } else {
+ val dummyHome = listOf(
+ Product(1, "Air Jordan XXXVI", "US$185", "Latest performance basketball shoe.", R.drawable.jordan),
+ Product(2, "Nike Dunk Low", "US$110", "A hoop icon returns with classic colors.", R.drawable.airforce)
+ )
+ saveToDataStore(HOME_KEY, dummyHome)
+ _homeProducts.value = dummyHome
+ }
+
+ val buyJson = prefs[BUY_KEY]
+ if (buyJson != null) {
+ _buyProducts.value = gson.fromJson(buyJson, type)
+ } else {
+ val dummyBuy = listOf(
+ Product(3, "Nike Everyday Plus Cushioned", "US$10", "Training Ankle Socks", R.drawable.trainingsocks, category = "Training Ankle Socks (6 Pairs)", colorsCount = "5 Colours"),
+ Product(4, "Nike Elite Crew", "US$16", "Performance Socks", R.drawable.jordan, category = "Basketball Socks", colorsCount = "7 Colours"),
+ Product(5, "Nike Air Force 1 '07", "US$115", "The legend lives on", R.drawable.airforce, subTitle = "BestSeller", category = "Women's Shoes", colorsCount = "5 Colours"),
+ Product(6, "Jordan Series .05", "US$115", "Men's Shoes", R.drawable.crewsocks, subTitle = "BestSeller", category = "Men's Shoes", colorsCount = "2 Colours")
+ )
+ saveToDataStore(BUY_KEY, dummyBuy)
+ _buyProducts.value = dummyBuy
+ }
+ }
+ }
+
+ fun toggleFavorite(product: Product) {
+ viewModelScope.launch {
+ val newFavStatus = !product.isFavorite
+
+ val updatedBuyList = _buyProducts.value.map {
+ if (it.id == product.id) it.copy(isFavorite = newFavStatus) else it
+ }
+ _buyProducts.value = updatedBuyList
+ saveToDataStore(BUY_KEY, updatedBuyList)
+
+ val updatedHomeList = _homeProducts.value.map {
+ if (it.id == product.id) it.copy(isFavorite = newFavStatus) else it
+ }
+ _homeProducts.value = updatedHomeList
+ saveToDataStore(HOME_KEY, updatedHomeList)
+
+ if (_selectedProduct.value?.id == product.id) {
+ _selectedProduct.value = _selectedProduct.value?.copy(isFavorite = newFavStatus)
+ }
+ }
+ }
+
+ private suspend fun saveToDataStore(key: androidx.datastore.preferences.core.Preferences.Key, data: List) {
+ dataStore.edit { prefs ->
+ prefs[key] = gson.toJson(data)
+ }
+ }
+
+ fun requestNavigateToDetail(product: Product) {
+ _selectedProduct.value = product
+ viewModelScope.launch {
+ _navigateToDetail.emit(Unit)
+ }
+ }
+
+ fun requestNavigateToBuyTab() {
+ viewModelScope.launch {
+ _navigateToBuyTab.emit(Unit)
+ }
+ }
+}
diff --git a/jongyee/Week5/app/src/main/java/com/example/week5/UserDto.kt b/jongyee/Week5/app/src/main/java/com/example/week5/UserDto.kt
new file mode 100644
index 0000000..598329d
--- /dev/null
+++ b/jongyee/Week5/app/src/main/java/com/example/week5/UserDto.kt
@@ -0,0 +1,23 @@
+package com.example.week5
+
+import com.google.gson.annotations.SerializedName
+
+data class UserDto(
+ val id: Int,
+ val email: String,
+ @SerializedName("first_name") val firstName: String,
+ @SerializedName("last_name") val lastName: String,
+ val avatar: String
+)
+
+data class SingleUserResponse(
+ val data: UserDto
+)
+
+data class UserListResponse(
+ val page: Int,
+ @SerializedName("per_page") val perPage: Int,
+ val total: Int,
+ @SerializedName("total_pages") val totalPages: Int,
+ val data: List
+)
diff --git a/jongyee/Week5/app/src/main/java/com/example/week5/WishlistFragment.kt b/jongyee/Week5/app/src/main/java/com/example/week5/WishlistFragment.kt
new file mode 100644
index 0000000..51ef090
--- /dev/null
+++ b/jongyee/Week5/app/src/main/java/com/example/week5/WishlistFragment.kt
@@ -0,0 +1,54 @@
+package com.example.week5
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.activityViewModels
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import androidx.recyclerview.widget.GridLayoutManager
+import com.example.week5.databinding.FragmentWishlistBinding
+import kotlinx.coroutines.launch
+
+class WishlistFragment : Fragment() {
+ private var _binding: FragmentWishlistBinding? = null
+ private val binding get() = _binding!!
+
+ private val sharedViewModel: SharedViewModel by activityViewModels()
+
+ private val productAdapter by lazy {
+ ProductAdapter(emptyList(), isGrid = true,
+ onItemClick = { product -> sharedViewModel.requestNavigateToDetail(product) },
+ onHeartClick = { product -> sharedViewModel.toggleFavorite(product) }
+ )
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+ _binding = FragmentWishlistBinding.inflate(inflater, container, false)
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ binding.wishlistRvProducts.adapter = productAdapter
+ binding.wishlistRvProducts.layoutManager = GridLayoutManager(context, 2)
+
+ viewLifecycleOwner.lifecycleScope.launch {
+ viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
+ sharedViewModel.buyProducts.collect { products ->
+ val wishItems = products.filter { it.isFavorite }
+ productAdapter.updateData(wishItems)
+ }
+ }
+ }
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ _binding = null
+ }
+}
diff --git a/jongyee/Week5/app/src/main/res/drawable/airforce.png b/jongyee/Week5/app/src/main/res/drawable/airforce.png
new file mode 100644
index 0000000..99fcf6c
Binary files /dev/null and b/jongyee/Week5/app/src/main/res/drawable/airforce.png differ
diff --git a/jongyee/Week5/app/src/main/res/drawable/bag.xml b/jongyee/Week5/app/src/main/res/drawable/bag.xml
new file mode 100644
index 0000000..9e97beb
--- /dev/null
+++ b/jongyee/Week5/app/src/main/res/drawable/bag.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/jongyee/Week5/app/src/main/res/drawable/bagcircle.xml b/jongyee/Week5/app/src/main/res/drawable/bagcircle.xml
new file mode 100644
index 0000000..1af2ce6
--- /dev/null
+++ b/jongyee/Week5/app/src/main/res/drawable/bagcircle.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
diff --git a/jongyee/Week5/app/src/main/res/drawable/buy.xml b/jongyee/Week5/app/src/main/res/drawable/buy.xml
new file mode 100644
index 0000000..483d117
--- /dev/null
+++ b/jongyee/Week5/app/src/main/res/drawable/buy.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
diff --git a/jongyee/Week5/app/src/main/res/drawable/crewsocks.png b/jongyee/Week5/app/src/main/res/drawable/crewsocks.png
new file mode 100644
index 0000000..d7c6ef1
Binary files /dev/null and b/jongyee/Week5/app/src/main/res/drawable/crewsocks.png differ
diff --git a/jongyee/Week5/app/src/main/res/drawable/event.png b/jongyee/Week5/app/src/main/res/drawable/event.png
new file mode 100644
index 0000000..704512e
Binary files /dev/null and b/jongyee/Week5/app/src/main/res/drawable/event.png differ
diff --git a/jongyee/Week5/app/src/main/res/drawable/home.xml b/jongyee/Week5/app/src/main/res/drawable/home.xml
new file mode 100644
index 0000000..a0d86b9
--- /dev/null
+++ b/jongyee/Week5/app/src/main/res/drawable/home.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
diff --git a/jongyee/Week5/app/src/main/res/drawable/ic_arrow_right.xml b/jongyee/Week5/app/src/main/res/drawable/ic_arrow_right.xml
new file mode 100644
index 0000000..19201cb
--- /dev/null
+++ b/jongyee/Week5/app/src/main/res/drawable/ic_arrow_right.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/jongyee/Week5/app/src/main/res/drawable/ic_heart_empty.xml b/jongyee/Week5/app/src/main/res/drawable/ic_heart_empty.xml
new file mode 100644
index 0000000..59b3907
--- /dev/null
+++ b/jongyee/Week5/app/src/main/res/drawable/ic_heart_empty.xml
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/jongyee/Week5/app/src/main/res/drawable/ic_heart_filled.xml b/jongyee/Week5/app/src/main/res/drawable/ic_heart_filled.xml
new file mode 100644
index 0000000..85961df
--- /dev/null
+++ b/jongyee/Week5/app/src/main/res/drawable/ic_heart_filled.xml
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/jongyee/Week5/app/src/main/res/drawable/ic_launcher_background.xml b/jongyee/Week5/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..07d5da9
--- /dev/null
+++ b/jongyee/Week5/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jongyee/Week5/app/src/main/res/drawable/ic_launcher_foreground.xml b/jongyee/Week5/app/src/main/res/drawable/ic_launcher_foreground.xml
new file mode 100644
index 0000000..2b068d1
--- /dev/null
+++ b/jongyee/Week5/app/src/main/res/drawable/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/jongyee/Week5/app/src/main/res/drawable/image.png b/jongyee/Week5/app/src/main/res/drawable/image.png
new file mode 100644
index 0000000..f42aa9d
Binary files /dev/null and b/jongyee/Week5/app/src/main/res/drawable/image.png differ
diff --git a/jongyee/Week5/app/src/main/res/drawable/jordan.png b/jongyee/Week5/app/src/main/res/drawable/jordan.png
new file mode 100644
index 0000000..1bfc3f2
Binary files /dev/null and b/jongyee/Week5/app/src/main/res/drawable/jordan.png differ
diff --git a/jongyee/Week5/app/src/main/res/drawable/order.png b/jongyee/Week5/app/src/main/res/drawable/order.png
new file mode 100644
index 0000000..61200c1
Binary files /dev/null and b/jongyee/Week5/app/src/main/res/drawable/order.png differ
diff --git a/jongyee/Week5/app/src/main/res/drawable/pass.png b/jongyee/Week5/app/src/main/res/drawable/pass.png
new file mode 100644
index 0000000..af82b3b
Binary files /dev/null and b/jongyee/Week5/app/src/main/res/drawable/pass.png differ
diff --git a/jongyee/Week5/app/src/main/res/drawable/profile.xml b/jongyee/Week5/app/src/main/res/drawable/profile.xml
new file mode 100644
index 0000000..1fc97b8
--- /dev/null
+++ b/jongyee/Week5/app/src/main/res/drawable/profile.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
diff --git a/jongyee/Week5/app/src/main/res/drawable/setting.png b/jongyee/Week5/app/src/main/res/drawable/setting.png
new file mode 100644
index 0000000..ed55245
Binary files /dev/null and b/jongyee/Week5/app/src/main/res/drawable/setting.png differ
diff --git a/jongyee/Week5/app/src/main/res/drawable/shape_circle_gray.xml b/jongyee/Week5/app/src/main/res/drawable/shape_circle_gray.xml
new file mode 100644
index 0000000..1f18f34
--- /dev/null
+++ b/jongyee/Week5/app/src/main/res/drawable/shape_circle_gray.xml
@@ -0,0 +1,5 @@
+
+
+
+
\ No newline at end of file
diff --git a/jongyee/Week5/app/src/main/res/drawable/trainingsocks.png b/jongyee/Week5/app/src/main/res/drawable/trainingsocks.png
new file mode 100644
index 0000000..fc41659
Binary files /dev/null and b/jongyee/Week5/app/src/main/res/drawable/trainingsocks.png differ
diff --git a/jongyee/Week5/app/src/main/res/drawable/wishlist.xml b/jongyee/Week5/app/src/main/res/drawable/wishlist.xml
new file mode 100644
index 0000000..6597a4a
--- /dev/null
+++ b/jongyee/Week5/app/src/main/res/drawable/wishlist.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
diff --git a/jongyee/Week5/app/src/main/res/layout/activity_main.xml b/jongyee/Week5/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..51565b5
--- /dev/null
+++ b/jongyee/Week5/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
diff --git a/jongyee/Week5/app/src/main/res/layout/activity_profile_edit.xml b/jongyee/Week5/app/src/main/res/layout/activity_profile_edit.xml
new file mode 100644
index 0000000..77f947b
--- /dev/null
+++ b/jongyee/Week5/app/src/main/res/layout/activity_profile_edit.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
diff --git a/jongyee/Week5/app/src/main/res/layout/fragment_bag.xml b/jongyee/Week5/app/src/main/res/layout/fragment_bag.xml
new file mode 100644
index 0000000..1a5ff2c
--- /dev/null
+++ b/jongyee/Week5/app/src/main/res/layout/fragment_bag.xml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jongyee/Week5/app/src/main/res/layout/fragment_buy.xml b/jongyee/Week5/app/src/main/res/layout/fragment_buy.xml
new file mode 100644
index 0000000..10058eb
--- /dev/null
+++ b/jongyee/Week5/app/src/main/res/layout/fragment_buy.xml
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jongyee/Week5/app/src/main/res/layout/fragment_detail.xml b/jongyee/Week5/app/src/main/res/layout/fragment_detail.xml
new file mode 100644
index 0000000..e7411ef
--- /dev/null
+++ b/jongyee/Week5/app/src/main/res/layout/fragment_detail.xml
@@ -0,0 +1,131 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jongyee/Week5/app/src/main/res/layout/fragment_home.xml b/jongyee/Week5/app/src/main/res/layout/fragment_home.xml
new file mode 100644
index 0000000..d669bc5
--- /dev/null
+++ b/jongyee/Week5/app/src/main/res/layout/fragment_home.xml
@@ -0,0 +1,93 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jongyee/Week5/app/src/main/res/layout/fragment_profile.xml b/jongyee/Week5/app/src/main/res/layout/fragment_profile.xml
new file mode 100644
index 0000000..8daebc9
--- /dev/null
+++ b/jongyee/Week5/app/src/main/res/layout/fragment_profile.xml
@@ -0,0 +1,250 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/jongyee/Week5/app/src/main/res/layout/fragment_wishlist.xml b/jongyee/Week5/app/src/main/res/layout/fragment_wishlist.xml
new file mode 100644
index 0000000..9b4d04c
--- /dev/null
+++ b/jongyee/Week5/app/src/main/res/layout/fragment_wishlist.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
diff --git a/jongyee/Week5/app/src/main/res/layout/item_following.xml b/jongyee/Week5/app/src/main/res/layout/item_following.xml
new file mode 100644
index 0000000..4f8d364
--- /dev/null
+++ b/jongyee/Week5/app/src/main/res/layout/item_following.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
diff --git a/jongyee/Week5/app/src/main/res/layout/item_grid_product.xml b/jongyee/Week5/app/src/main/res/layout/item_grid_product.xml
new file mode 100644
index 0000000..299315b
--- /dev/null
+++ b/jongyee/Week5/app/src/main/res/layout/item_grid_product.xml
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jongyee/Week5/app/src/main/res/layout/item_home_product.xml b/jongyee/Week5/app/src/main/res/layout/item_home_product.xml
new file mode 100644
index 0000000..fd414d9
--- /dev/null
+++ b/jongyee/Week5/app/src/main/res/layout/item_home_product.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
diff --git a/jongyee/Week5/app/src/main/res/menu/bottom_nav_menu.xml b/jongyee/Week5/app/src/main/res/menu/bottom_nav_menu.xml
new file mode 100644
index 0000000..788eb4f
--- /dev/null
+++ b/jongyee/Week5/app/src/main/res/menu/bottom_nav_menu.xml
@@ -0,0 +1,23 @@
+
+
diff --git a/jongyee/Week5/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/jongyee/Week5/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..6f3b755
--- /dev/null
+++ b/jongyee/Week5/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/jongyee/Week5/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/jongyee/Week5/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..6f3b755
--- /dev/null
+++ b/jongyee/Week5/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/jongyee/Week5/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/jongyee/Week5/app/src/main/res/mipmap-hdpi/ic_launcher.webp
new file mode 100644
index 0000000..c209e78
Binary files /dev/null and b/jongyee/Week5/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ
diff --git a/jongyee/Week5/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/jongyee/Week5/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..b2dfe3d
Binary files /dev/null and b/jongyee/Week5/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ
diff --git a/jongyee/Week5/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/jongyee/Week5/app/src/main/res/mipmap-mdpi/ic_launcher.webp
new file mode 100644
index 0000000..4f0f1d6
Binary files /dev/null and b/jongyee/Week5/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ
diff --git a/jongyee/Week5/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/jongyee/Week5/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..62b611d
Binary files /dev/null and b/jongyee/Week5/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ
diff --git a/jongyee/Week5/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/jongyee/Week5/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 0000000..948a307
Binary files /dev/null and b/jongyee/Week5/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ
diff --git a/jongyee/Week5/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/jongyee/Week5/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..1b9a695
Binary files /dev/null and b/jongyee/Week5/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ
diff --git a/jongyee/Week5/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/jongyee/Week5/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..28d4b77
Binary files /dev/null and b/jongyee/Week5/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ
diff --git a/jongyee/Week5/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/jongyee/Week5/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..9287f50
Binary files /dev/null and b/jongyee/Week5/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ
diff --git a/jongyee/Week5/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/jongyee/Week5/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..aa7d642
Binary files /dev/null and b/jongyee/Week5/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ
diff --git a/jongyee/Week5/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/jongyee/Week5/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..9126ae3
Binary files /dev/null and b/jongyee/Week5/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ
diff --git a/jongyee/Week5/app/src/main/res/values-night/themes.xml b/jongyee/Week5/app/src/main/res/values-night/themes.xml
new file mode 100644
index 0000000..f8a16ff
--- /dev/null
+++ b/jongyee/Week5/app/src/main/res/values-night/themes.xml
@@ -0,0 +1,6 @@
+
+
+
+
diff --git a/jongyee/Week5/app/src/main/res/values/colors.xml b/jongyee/Week5/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..8478258
--- /dev/null
+++ b/jongyee/Week5/app/src/main/res/values/colors.xml
@@ -0,0 +1,9 @@
+
+
+ #FFFFFF
+ #000000
+ #F5F5F5
+ #888888
+ #333333
+ #DDDDDD
+
diff --git a/jongyee/Week5/app/src/main/res/values/strings.xml b/jongyee/Week5/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..013d7a7
--- /dev/null
+++ b/jongyee/Week5/app/src/main/res/values/strings.xml
@@ -0,0 +1,30 @@
+
+ Hello blank fragment
+ 홈
+ 구매하기
+ 위시리스트
+ 장바구니
+ 프로필
+ Discover
+ 9월 4일 목요일
+ 전체
+ Tops & T-Shirts
+ Shoes
+ 위시리스트
+ 장바구니가 비어 있습니다.\n제품을 추가하면 여기에 표시됩니다.
+ 주문하기
+ Nike Week5
+ 사이즈 선택
+ 장바구니에 추가
+ 위시리스트
+ 프로필 수정
+ sale
+
+ 전체
+ 나이키 최신 상품
+ What\'s new
+ 프로필 수정
+ 위시리스트
+ BestSeller
+ 팔로잉
+
diff --git a/jongyee/Week5/app/src/main/res/values/themes.xml b/jongyee/Week5/app/src/main/res/values/themes.xml
new file mode 100644
index 0000000..48fda23
--- /dev/null
+++ b/jongyee/Week5/app/src/main/res/values/themes.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
diff --git a/jongyee/Week5/app/src/main/res/xml/backup_rules.xml b/jongyee/Week5/app/src/main/res/xml/backup_rules.xml
new file mode 100644
index 0000000..4df9255
--- /dev/null
+++ b/jongyee/Week5/app/src/main/res/xml/backup_rules.xml
@@ -0,0 +1,13 @@
+
+
+
+
\ No newline at end of file
diff --git a/jongyee/Week5/app/src/main/res/xml/data_extraction_rules.xml b/jongyee/Week5/app/src/main/res/xml/data_extraction_rules.xml
new file mode 100644
index 0000000..9ee9997
--- /dev/null
+++ b/jongyee/Week5/app/src/main/res/xml/data_extraction_rules.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/jongyee/Week5/app/src/test/java/com/example/week5/ExampleUnitTest.kt b/jongyee/Week5/app/src/test/java/com/example/week5/ExampleUnitTest.kt
new file mode 100644
index 0000000..77e9549
--- /dev/null
+++ b/jongyee/Week5/app/src/test/java/com/example/week5/ExampleUnitTest.kt
@@ -0,0 +1,11 @@
+package com.example.week5
+
+import org.junit.Test
+import org.junit.Assert.*
+
+class ExampleUnitTest {
+ @Test
+ fun addition_isCorrect() {
+ assertEquals(4, 2 + 2)
+ }
+}
diff --git a/jongyee/Week5/build.gradle.kts b/jongyee/Week5/build.gradle.kts
new file mode 100644
index 0000000..1d8ba06
--- /dev/null
+++ b/jongyee/Week5/build.gradle.kts
@@ -0,0 +1,6 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+plugins {
+ alias(libs.plugins.android.application) apply false
+ alias(libs.plugins.kotlin.android) apply false
+ alias(libs.plugins.kotlin.parcelize) apply false
+}
diff --git a/jongyee/Week5/gradle.properties b/jongyee/Week5/gradle.properties
new file mode 100644
index 0000000..d621849
--- /dev/null
+++ b/jongyee/Week5/gradle.properties
@@ -0,0 +1,16 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. For more details, visit
+# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
+# org.gradle.parallel=true
+# Kotlin code style for this project: "official" or "obsolete":
+kotlin.code.style=official
+android.useAndroidX=true
diff --git a/jongyee/Week5/gradle/gradle-daemon-jvm.properties b/jongyee/Week5/gradle/gradle-daemon-jvm.properties
new file mode 100644
index 0000000..6c1139e
--- /dev/null
+++ b/jongyee/Week5/gradle/gradle-daemon-jvm.properties
@@ -0,0 +1,12 @@
+#This file is generated by updateDaemonJvm
+toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/ec7520a1e057cd116f9544c42142a16b/redirect
+toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/4c4f879899012ff0a8b2e2117df03b0e/redirect
+toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/ec7520a1e057cd116f9544c42142a16b/redirect
+toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/4c4f879899012ff0a8b2e2117df03b0e/redirect
+toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/73bcfb608d1fde9fb62e462f834a3299/redirect
+toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/846ee0d876d26a26f37aa1ce8de73224/redirect
+toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/ec7520a1e057cd116f9544c42142a16b/redirect
+toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/4c4f879899012ff0a8b2e2117df03b0e/redirect
+toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/9482ddec596298c84656d31d16652665/redirect
+toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/39701d92e1756bb2f141eb67cd4c660e/redirect
+toolchainVersion=21
diff --git a/jongyee/Week5/gradle/libs.versions.toml b/jongyee/Week5/gradle/libs.versions.toml
new file mode 100644
index 0000000..5629240
--- /dev/null
+++ b/jongyee/Week5/gradle/libs.versions.toml
@@ -0,0 +1,48 @@
+[versions]
+agp = "8.9.1"
+kotlin = "2.1.0"
+coreKtx = "1.18.0"
+junit = "4.13.2"
+junitVersion = "1.3.0"
+espressoCore = "3.7.0"
+appcompat = "1.7.1"
+material = "1.13.0"
+activity = "1.13.0"
+constraintlayout = "2.2.1"
+datastorePreferences = "1.2.1"
+gson = "2.13.2"
+fragmentKtx = "1.8.9"
+lifecycle = "2.10.0"
+retrofit = "2.9.0"
+okhttp = "4.12.0"
+coroutines = "1.8.0"
+glide = "4.16.0"
+
+[libraries]
+androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
+junit = { group = "junit", name = "junit", version.ref = "junit" }
+androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
+androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
+androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
+material = { group = "com.google.android.material", name = "material", version.ref = "material" }
+androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
+androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
+
+androidx-datastore-preferences = { group = "androidx.datastore", name = "datastore-preferences", version.ref = "datastorePreferences" }
+gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" }
+
+androidx-fragment-ktx = { group = "androidx.fragment", name = "fragment-ktx", version.ref = "fragmentKtx" }
+androidx-lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycle" }
+androidx-lifecycle-livedata-ktx = { group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version.ref = "lifecycle" }
+
+retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }
+retrofit-converter-gson = { group = "com.squareup.retrofit2", name = "converter-gson", version.ref = "retrofit" }
+okhttp-logging-interceptor = { group = "com.squareup.okhttp3", name = "logging-interceptor", version.ref = "okhttp" }
+kotlinx-coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "coroutines" }
+kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "coroutines" }
+glide = { group = "com.github.bumptech.glide", name = "glide", version.ref = "glide" }
+
+[plugins]
+android-application = { id = "com.android.application", version.ref = "agp" }
+kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
+kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" }
diff --git a/jongyee/Week5/gradle/wrapper/gradle-wrapper.jar b/jongyee/Week5/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..8bdaf60
Binary files /dev/null and b/jongyee/Week5/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/jongyee/Week5/gradle/wrapper/gradle-wrapper.properties b/jongyee/Week5/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..318e0f5
--- /dev/null
+++ b/jongyee/Week5/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,9 @@
+#Sat Mar 28 18:15:36 KST 2026
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionSha256Sum=b266d5ff6b90eada6dc3b20cb090e3731302e553a27c5d3e4df1f0d76beaff06
+distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip
+networkTimeout=10000
+validateDistributionUrl=true
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/jongyee/Week5/settings.gradle.kts b/jongyee/Week5/settings.gradle.kts
new file mode 100644
index 0000000..d554396
--- /dev/null
+++ b/jongyee/Week5/settings.gradle.kts
@@ -0,0 +1,26 @@
+pluginManagement {
+ repositories {
+ google {
+ content {
+ includeGroupByRegex("com\\.android.*")
+ includeGroupByRegex("com\\.google.*")
+ includeGroupByRegex("androidx.*")
+ }
+ }
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
+plugins {
+ id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
+}
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+rootProject.name = "Week5"
+include(":app")
diff --git a/jongyee/Week6/.claude/settings.local.json b/jongyee/Week6/.claude/settings.local.json
new file mode 100644
index 0000000..428eb42
--- /dev/null
+++ b/jongyee/Week6/.claude/settings.local.json
@@ -0,0 +1,7 @@
+{
+ "permissions": {
+ "allow": [
+ "Bash(grep *)"
+ ]
+ }
+}
diff --git a/jongyee/Week6/.gitignore b/jongyee/Week6/.gitignore
new file mode 100644
index 0000000..aa724b7
--- /dev/null
+++ b/jongyee/Week6/.gitignore
@@ -0,0 +1,15 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
+local.properties
diff --git a/jongyee/Week6/app/.gitignore b/jongyee/Week6/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/jongyee/Week6/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/jongyee/Week6/app/build.gradle.kts b/jongyee/Week6/app/build.gradle.kts
new file mode 100644
index 0000000..a9ee921
--- /dev/null
+++ b/jongyee/Week6/app/build.gradle.kts
@@ -0,0 +1,71 @@
+plugins {
+ alias(libs.plugins.android.application)
+ alias(libs.plugins.kotlin.android)
+ alias(libs.plugins.hilt.android)
+ kotlin("kapt")
+}
+
+android {
+ namespace = "com.example.week6"
+ compileSdk = 36
+
+ defaultConfig {
+ applicationId = "com.example.week6"
+ minSdk = 24
+ targetSdk = 36
+ versionCode = 1
+ versionName = "1.0"
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ isMinifyEnabled = false
+ proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
+ }
+ }
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
+ }
+ kotlinOptions {
+ jvmTarget = "11"
+ }
+ buildFeatures {
+ viewBinding = true
+ }
+ kapt {
+ correctErrorTypes = true
+ }
+}
+
+dependencies {
+ implementation(libs.androidx.core.ktx)
+ implementation(libs.androidx.appcompat)
+ implementation(libs.material)
+ implementation(libs.androidx.activity)
+ implementation(libs.androidx.constraintlayout)
+ testImplementation(libs.junit)
+ androidTestImplementation(libs.androidx.junit)
+ androidTestImplementation(libs.androidx.espresso.core)
+ implementation(libs.androidx.datastore.preferences)
+ implementation(libs.gson)
+ implementation(libs.androidx.fragment.ktx)
+ implementation(libs.androidx.lifecycle.viewmodel.ktx)
+ implementation(libs.androidx.lifecycle.livedata.ktx)
+
+ // Retrofit
+ implementation(libs.retrofit)
+ implementation(libs.retrofit.converter.gson)
+ implementation(libs.okhttp.logging.interceptor)
+
+ // Coroutines
+ implementation(libs.kotlinx.coroutines.core)
+ implementation(libs.kotlinx.coroutines.android)
+
+ // Glide
+ implementation(libs.glide)
+
+ // Hilt
+ implementation(libs.hilt.android)
+ kapt(libs.hilt.compiler)
+}
diff --git a/jongyee/Week6/app/proguard-rules.pro b/jongyee/Week6/app/proguard-rules.pro
new file mode 100644
index 0000000..f1b4245
--- /dev/null
+++ b/jongyee/Week6/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/jongyee/Week6/app/src/androidTest/java/com/example/week6/ExampleInstrumentedTest.kt b/jongyee/Week6/app/src/androidTest/java/com/example/week6/ExampleInstrumentedTest.kt
new file mode 100644
index 0000000..17eda76
--- /dev/null
+++ b/jongyee/Week6/app/src/androidTest/java/com/example/week6/ExampleInstrumentedTest.kt
@@ -0,0 +1,16 @@
+package com.example.week6
+
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.Assert.*
+
+@RunWith(AndroidJUnit4::class)
+class ExampleInstrumentedTest {
+ @Test
+ fun useAppContext() {
+ val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+ assertEquals("com.example.week6", appContext.packageName)
+ }
+}
diff --git a/jongyee/Week6/app/src/main/AndroidManifest.xml b/jongyee/Week6/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..233ff0b
--- /dev/null
+++ b/jongyee/Week6/app/src/main/AndroidManifest.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jongyee/Week6/app/src/main/java/com/example/week6/BagFragment.kt b/jongyee/Week6/app/src/main/java/com/example/week6/BagFragment.kt
new file mode 100644
index 0000000..f724e53
--- /dev/null
+++ b/jongyee/Week6/app/src/main/java/com/example/week6/BagFragment.kt
@@ -0,0 +1,38 @@
+package com.example.week6
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.activityViewModels
+import com.example.week6.databinding.FragmentBagBinding
+import dagger.hilt.android.AndroidEntryPoint
+
+@AndroidEntryPoint
+class BagFragment : Fragment() {
+ private var _binding: FragmentBagBinding? = null
+ private val binding get() = _binding!!
+
+ private val sharedViewModel: SharedViewModel by activityViewModels()
+
+ override fun onCreateView(
+ inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ _binding = FragmentBagBinding.inflate(inflater, container, false)
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ binding.bagBtnOrder.setOnClickListener {
+ sharedViewModel.requestNavigateToBuyTab()
+ }
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ _binding = null
+ }
+}
diff --git a/jongyee/Week6/app/src/main/java/com/example/week6/BuyFragment.kt b/jongyee/Week6/app/src/main/java/com/example/week6/BuyFragment.kt
new file mode 100644
index 0000000..a7adeec
--- /dev/null
+++ b/jongyee/Week6/app/src/main/java/com/example/week6/BuyFragment.kt
@@ -0,0 +1,86 @@
+package com.example.week6
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.activityViewModels
+import androidx.lifecycle.lifecycleScope
+import androidx.recyclerview.widget.GridLayoutManager
+import com.example.week6.databinding.FragmentBuyBinding
+import com.google.android.material.tabs.TabLayout
+import dagger.hilt.android.AndroidEntryPoint
+import kotlinx.coroutines.launch
+
+@AndroidEntryPoint
+class TopsFragment : Fragment()
+
+@AndroidEntryPoint
+class SaleFragment : Fragment()
+
+@AndroidEntryPoint
+class BuyFragment : Fragment() {
+ private var _binding: FragmentBuyBinding? = null
+ private val binding get() = _binding!!
+
+ private val sharedViewModel: SharedViewModel by activityViewModels()
+
+ private val productAdapter by lazy {
+ ProductAdapter(
+ productList = emptyList(),
+ isGrid = true,
+ onHeartClick = { product -> sharedViewModel.toggleFavorite(product) },
+ onItemClick = { product -> sharedViewModel.requestNavigateToDetail(product) }
+ )
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ _binding = FragmentBuyBinding.inflate(inflater, container, false)
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ binding.buyRvProducts.adapter = productAdapter
+ binding.buyRvProducts.layoutManager = GridLayoutManager(context, 2)
+
+ viewLifecycleOwner.lifecycleScope.launch {
+ sharedViewModel.buyProducts.collect { products ->
+ productAdapter.updateData(products)
+ }
+ }
+
+ binding.buyTabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
+ override fun onTabSelected(tab: TabLayout.Tab?) {
+ when (tab?.position) {
+ 0 -> showRecyclerView()
+ 1 -> showChildFragment(TopsFragment())
+ 2 -> showChildFragment(SaleFragment())
+ }
+ }
+ override fun onTabUnselected(tab: TabLayout.Tab?) {}
+ override fun onTabReselected(tab: TabLayout.Tab?) {}
+ })
+ }
+
+ private fun showRecyclerView() {
+ binding.buyRvProducts.visibility = View.VISIBLE
+ binding.buyChildContainer.visibility = View.GONE
+ }
+
+ private fun showChildFragment(fragment: Fragment) {
+ binding.buyRvProducts.visibility = View.GONE
+ binding.buyChildContainer.visibility = View.VISIBLE
+ childFragmentManager.beginTransaction().replace(R.id.buy_child_container, fragment).commit()
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ _binding = null
+ }
+}
diff --git a/jongyee/Week6/app/src/main/java/com/example/week6/DetailFragment.kt b/jongyee/Week6/app/src/main/java/com/example/week6/DetailFragment.kt
new file mode 100644
index 0000000..2feecd7
--- /dev/null
+++ b/jongyee/Week6/app/src/main/java/com/example/week6/DetailFragment.kt
@@ -0,0 +1,62 @@
+package com.example.week6
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.activityViewModels
+import androidx.lifecycle.lifecycleScope
+import com.example.week6.databinding.FragmentDetailBinding
+import dagger.hilt.android.AndroidEntryPoint
+import kotlinx.coroutines.launch
+
+@AndroidEntryPoint
+class DetailFragment : Fragment() {
+ private var _binding: FragmentDetailBinding? = null
+ private val binding get() = _binding!!
+
+ private val sharedViewModel: SharedViewModel by activityViewModels()
+
+ override fun onCreateView(
+ inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ _binding = FragmentDetailBinding.inflate(inflater, container, false)
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ viewLifecycleOwner.lifecycleScope.launch {
+ sharedViewModel.selectedProduct.collect { product ->
+ product?.let {
+ binding.tvDetailCategory.text = it.category
+ binding.tvDetailName.text = it.name
+ binding.tvDetailPrice.text = it.price
+ binding.tvDetailDescription.text = it.description
+ binding.imgDetailProduct.setImageResource(it.imageRes)
+
+ val heartIcon = if (it.isFavorite) R.drawable.ic_heart_filled else R.drawable.ic_heart_empty
+ binding.btnWishlist.setIconResource(heartIcon)
+ }
+ }
+ }
+
+ binding.btnWishlist.setOnClickListener {
+ sharedViewModel.selectedProduct.value?.let { currentProduct ->
+ sharedViewModel.toggleFavorite(currentProduct)
+ }
+ }
+
+ binding.icBack.setOnClickListener {
+ parentFragmentManager.popBackStack()
+ }
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ _binding = null
+ }
+}
diff --git a/jongyee/Week6/app/src/main/java/com/example/week6/FollowingAdapter.kt b/jongyee/Week6/app/src/main/java/com/example/week6/FollowingAdapter.kt
new file mode 100644
index 0000000..2f57a35
--- /dev/null
+++ b/jongyee/Week6/app/src/main/java/com/example/week6/FollowingAdapter.kt
@@ -0,0 +1,34 @@
+package com.example.week6
+
+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.bumptech.glide.Glide
+import com.example.week6.databinding.ItemFollowingBinding
+
+class FollowingAdapter : ListAdapter(DiffCallback) {
+
+ inner class ViewHolder(val binding: ItemFollowingBinding) : RecyclerView.ViewHolder(binding.root)
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
+ val binding = ItemFollowingBinding.inflate(LayoutInflater.from(parent.context), parent, false)
+ return ViewHolder(binding)
+ }
+
+ override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+ val user = getItem(position)
+ holder.binding.tvFollowingName.text = user.firstName
+ Glide.with(holder.itemView.context)
+ .load(user.avatar)
+ .circleCrop()
+ .placeholder(R.drawable.ic_launcher_background)
+ .into(holder.binding.ivFollowingAvatar)
+ }
+
+ companion object DiffCallback : DiffUtil.ItemCallback() {
+ override fun areItemsTheSame(oldItem: UserDto, newItem: UserDto) = oldItem.id == newItem.id
+ override fun areContentsTheSame(oldItem: UserDto, newItem: UserDto) = oldItem == newItem
+ }
+}
diff --git a/jongyee/Week6/app/src/main/java/com/example/week6/HomeFragment.kt b/jongyee/Week6/app/src/main/java/com/example/week6/HomeFragment.kt
new file mode 100644
index 0000000..0004da3
--- /dev/null
+++ b/jongyee/Week6/app/src/main/java/com/example/week6/HomeFragment.kt
@@ -0,0 +1,54 @@
+package com.example.week6
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.activityViewModels
+import androidx.lifecycle.lifecycleScope
+import androidx.recyclerview.widget.LinearLayoutManager
+import com.example.week6.databinding.FragmentHomeBinding
+import dagger.hilt.android.AndroidEntryPoint
+import kotlinx.coroutines.launch
+
+@AndroidEntryPoint
+class HomeFragment : Fragment() {
+ private var _binding: FragmentHomeBinding? = null
+ private val binding get() = _binding!!
+
+ private val sharedViewModel: SharedViewModel by activityViewModels()
+
+ private val productAdapter by lazy {
+ ProductAdapter(
+ productList = emptyList(),
+ isGrid = false,
+ onItemClick = { product -> sharedViewModel.requestNavigateToDetail(product) }
+ )
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+ _binding = FragmentHomeBinding.inflate(inflater, container, false)
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ binding.homeRvProducts.apply {
+ adapter = productAdapter
+ layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
+ }
+
+ viewLifecycleOwner.lifecycleScope.launch {
+ sharedViewModel.homeProducts.collect { products ->
+ productAdapter.updateData(products)
+ }
+ }
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ _binding = null
+ }
+}
diff --git a/jongyee/Week6/app/src/main/java/com/example/week6/MainActivity.kt b/jongyee/Week6/app/src/main/java/com/example/week6/MainActivity.kt
new file mode 100644
index 0000000..a9e6530
--- /dev/null
+++ b/jongyee/Week6/app/src/main/java/com/example/week6/MainActivity.kt
@@ -0,0 +1,54 @@
+package com.example.week6
+
+import android.os.Bundle
+import androidx.activity.viewModels
+import androidx.appcompat.app.AppCompatActivity
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.lifecycleScope
+import com.example.week6.databinding.ActivityMainBinding
+import dagger.hilt.android.AndroidEntryPoint
+import kotlinx.coroutines.launch
+
+@AndroidEntryPoint
+class MainActivity : AppCompatActivity() {
+ private lateinit var binding: ActivityMainBinding
+ private val sharedViewModel: SharedViewModel by viewModels()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ binding = ActivityMainBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ if (savedInstanceState == null) changeFragment(HomeFragment())
+
+ binding.mainBottomNav.setOnItemSelectedListener { item ->
+ when (item.itemId) {
+ R.id.nav_home -> changeFragment(HomeFragment())
+ R.id.nav_buy -> changeFragment(BuyFragment())
+ R.id.nav_wishlist -> changeFragment(WishlistFragment())
+ R.id.nav_bag -> changeFragment(BagFragment())
+ R.id.nav_profile -> changeFragment(ProfileFragment())
+ }
+ true
+ }
+
+ lifecycleScope.launch {
+ sharedViewModel.navigateToDetail.collect {
+ supportFragmentManager.beginTransaction()
+ .replace(R.id.main_container, DetailFragment())
+ .addToBackStack(null)
+ .commit()
+ }
+ }
+
+ lifecycleScope.launch {
+ sharedViewModel.navigateToBuyTab.collect {
+ binding.mainBottomNav.selectedItemId = R.id.nav_buy
+ }
+ }
+ }
+
+ private fun changeFragment(fragment: Fragment) {
+ supportFragmentManager.beginTransaction().replace(R.id.main_container, fragment).commit()
+ }
+}
diff --git a/jongyee/Week6/app/src/main/java/com/example/week6/MyApplication.kt b/jongyee/Week6/app/src/main/java/com/example/week6/MyApplication.kt
new file mode 100644
index 0000000..ab99afd
--- /dev/null
+++ b/jongyee/Week6/app/src/main/java/com/example/week6/MyApplication.kt
@@ -0,0 +1,7 @@
+package com.example.week6
+
+import android.app.Application
+import dagger.hilt.android.HiltAndroidApp
+
+@HiltAndroidApp
+class MyApplication : Application()
diff --git a/jongyee/Week6/app/src/main/java/com/example/week6/Product.kt b/jongyee/Week6/app/src/main/java/com/example/week6/Product.kt
new file mode 100644
index 0000000..479f1e8
--- /dev/null
+++ b/jongyee/Week6/app/src/main/java/com/example/week6/Product.kt
@@ -0,0 +1,13 @@
+package com.example.week6
+
+data class Product(
+ val id: Int,
+ val name: String,
+ val price: String,
+ val description: String,
+ val imageRes: Int,
+ val subTitle: String = "",
+ val category: String = "",
+ val colorsCount: String = "",
+ var isFavorite: Boolean = false
+)
diff --git a/jongyee/Week6/app/src/main/java/com/example/week6/ProductAdapter.kt b/jongyee/Week6/app/src/main/java/com/example/week6/ProductAdapter.kt
new file mode 100644
index 0000000..0b11520
--- /dev/null
+++ b/jongyee/Week6/app/src/main/java/com/example/week6/ProductAdapter.kt
@@ -0,0 +1,78 @@
+package com.example.week6
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import com.example.week6.databinding.ItemGridProductBinding
+import com.example.week6.databinding.ItemHomeProductBinding
+
+class ProductAdapter(
+ private var productList: List = emptyList(),
+ private val isGrid: Boolean = false,
+ private val onHeartClick: ((Product) -> Unit)? = null,
+ private val onItemClick: (Product) -> Unit
+) : RecyclerView.Adapter() {
+
+ fun updateData(newList: List) {
+ productList = newList
+ notifyDataSetChanged()
+ }
+
+ inner class HomeViewHolder(private val binding: ItemHomeProductBinding) :
+ RecyclerView.ViewHolder(binding.root) {
+ fun bind(product: Product) {
+ binding.ivProduct.setImageResource(product.imageRes)
+ binding.tvName.text = product.name
+ binding.tvPrice.text = product.price
+ binding.root.setOnClickListener { onItemClick(product) }
+ }
+ }
+
+ inner class GridViewHolder(private val binding: ItemGridProductBinding) :
+ RecyclerView.ViewHolder(binding.root) {
+ fun bind(product: Product) {
+ binding.ivProductGrid.setImageResource(product.imageRes)
+ binding.tvNameGrid.text = product.name
+ binding.tvPriceGrid.text = product.price
+ binding.tvCategoryGrid.text = product.category
+ binding.tvColorsGrid.text = product.colorsCount
+
+ if (product.subTitle.isEmpty()) {
+ binding.tvSubtitleGrid.visibility = View.GONE
+ } else {
+ binding.tvSubtitleGrid.visibility = View.VISIBLE
+ binding.tvSubtitleGrid.text = product.subTitle
+ }
+
+ updateHeartIcon(product.isFavorite)
+ binding.ivHeartGrid.setOnClickListener {
+ onHeartClick?.invoke(product)
+ }
+
+ binding.root.setOnClickListener { onItemClick(product) }
+ }
+
+ private fun updateHeartIcon(isFavorite: Boolean) {
+ val iconRes = if (isFavorite) R.drawable.ic_heart_filled else R.drawable.ic_heart_empty
+ binding.ivHeartGrid.setImageResource(iconRes)
+ }
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
+ val layoutInflater = LayoutInflater.from(parent.context)
+ return if (isGrid) {
+ GridViewHolder(ItemGridProductBinding.inflate(layoutInflater, parent, false))
+ } else {
+ HomeViewHolder(ItemHomeProductBinding.inflate(layoutInflater, parent, false))
+ }
+ }
+
+ override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
+ val product = productList[position]
+ if (holder is HomeViewHolder) holder.bind(product)
+ else if (holder is GridViewHolder) holder.bind(product)
+ }
+
+ override fun getItemCount(): Int = productList.size
+}
diff --git a/jongyee/Week6/app/src/main/java/com/example/week6/ProfileEditActivity.kt b/jongyee/Week6/app/src/main/java/com/example/week6/ProfileEditActivity.kt
new file mode 100644
index 0000000..9607f0e
--- /dev/null
+++ b/jongyee/Week6/app/src/main/java/com/example/week6/ProfileEditActivity.kt
@@ -0,0 +1,17 @@
+package com.example.week6
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import com.example.week6.databinding.ActivityProfileEditBinding
+import dagger.hilt.android.AndroidEntryPoint
+
+@AndroidEntryPoint
+class ProfileEditActivity : AppCompatActivity() {
+ private lateinit var binding: ActivityProfileEditBinding
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ binding = ActivityProfileEditBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+ }
+}
diff --git a/jongyee/Week6/app/src/main/java/com/example/week6/ProfileFragment.kt b/jongyee/Week6/app/src/main/java/com/example/week6/ProfileFragment.kt
new file mode 100644
index 0000000..a15e4c3
--- /dev/null
+++ b/jongyee/Week6/app/src/main/java/com/example/week6/ProfileFragment.kt
@@ -0,0 +1,75 @@
+package com.example.week6
+
+import android.content.Intent
+import android.os.Bundle
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.viewModels
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import androidx.recyclerview.widget.LinearLayoutManager
+import com.bumptech.glide.Glide
+import com.example.week6.databinding.FragmentProfileBinding
+import dagger.hilt.android.AndroidEntryPoint
+import kotlinx.coroutines.launch
+
+@AndroidEntryPoint
+class ProfileFragment : Fragment() {
+ private var _binding: FragmentProfileBinding? = null
+ private val binding get() = _binding!!
+
+ private val profileViewModel: ProfileViewModel by viewModels()
+ private lateinit var followingAdapter: FollowingAdapter
+
+ override fun onCreateView(
+ inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ _binding = FragmentProfileBinding.inflate(inflater, container, false)
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ followingAdapter = FollowingAdapter()
+ binding.rvFollowing.apply {
+ adapter = followingAdapter
+ layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
+ }
+
+ viewLifecycleOwner.lifecycleScope.launch {
+ viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
+ profileViewModel.uiState.collect { state ->
+ state.userProfile?.let { user ->
+ binding.tvProfileName.text = "${user.firstName} ${user.lastName}"
+ Glide.with(this@ProfileFragment)
+ .load(user.avatar)
+ .circleCrop()
+ .placeholder(R.drawable.ic_launcher_background)
+ .into(binding.ivProfileAvatar)
+ }
+ if (state.followingList.isNotEmpty()) {
+ followingAdapter.submitList(state.followingList)
+ binding.tvFollowingTitle.text = "팔로잉 (${state.followingList.size})"
+ }
+ state.error?.let { Log.e("ProfileFragment", "로드 실패: $it") }
+ }
+ }
+ }
+
+ binding.btnEditProfile.setOnClickListener {
+ val intent = Intent(requireActivity(), ProfileEditActivity::class.java)
+ startActivity(intent)
+ }
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ _binding = null
+ }
+}
diff --git a/jongyee/Week6/app/src/main/java/com/example/week6/ProfileViewModel.kt b/jongyee/Week6/app/src/main/java/com/example/week6/ProfileViewModel.kt
new file mode 100644
index 0000000..b85076d
--- /dev/null
+++ b/jongyee/Week6/app/src/main/java/com/example/week6/ProfileViewModel.kt
@@ -0,0 +1,49 @@
+package com.example.week6
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.example.week6.domain.repository.ProfileRepository
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.update
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+data class ProfileUiState(
+ val userProfile: UserDto? = null,
+ val followingList: List = emptyList(),
+ val error: String? = null
+)
+
+@HiltViewModel
+class ProfileViewModel @Inject constructor(
+ private val profileRepository: ProfileRepository
+) : ViewModel() {
+
+ private val _uiState = MutableStateFlow(ProfileUiState())
+ val uiState: StateFlow = _uiState
+
+ init {
+ loadUserProfile()
+ loadFollowingList()
+ }
+
+ private fun loadUserProfile() {
+ viewModelScope.launch {
+ profileRepository.getUser(1).fold(
+ onSuccess = { user -> _uiState.update { it.copy(userProfile = user) } },
+ onFailure = { e -> _uiState.update { it.copy(error = e.message) } }
+ )
+ }
+ }
+
+ private fun loadFollowingList() {
+ viewModelScope.launch {
+ profileRepository.getUsers(1).fold(
+ onSuccess = { users -> _uiState.update { it.copy(followingList = users) } },
+ onFailure = { e -> _uiState.update { it.copy(error = e.message) } }
+ )
+ }
+ }
+}
diff --git a/jongyee/Week6/app/src/main/java/com/example/week6/SharedViewModel.kt b/jongyee/Week6/app/src/main/java/com/example/week6/SharedViewModel.kt
new file mode 100644
index 0000000..5f38503
--- /dev/null
+++ b/jongyee/Week6/app/src/main/java/com/example/week6/SharedViewModel.kt
@@ -0,0 +1,73 @@
+package com.example.week6
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.example.week6.domain.repository.ProductRepository
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asSharedFlow
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+@HiltViewModel
+class SharedViewModel @Inject constructor(
+ private val productRepository: ProductRepository
+) : ViewModel() {
+
+ private val _homeProducts = MutableStateFlow>(emptyList())
+ val homeProducts: StateFlow> get() = _homeProducts
+
+ private val _buyProducts = MutableStateFlow>(emptyList())
+ val buyProducts: StateFlow> get() = _buyProducts
+
+ private val _selectedProduct = MutableStateFlow(null)
+ val selectedProduct: StateFlow get() = _selectedProduct
+
+ private val _navigateToDetail = MutableSharedFlow()
+ val navigateToDetail = _navigateToDetail.asSharedFlow()
+
+ private val _navigateToBuyTab = MutableSharedFlow()
+ val navigateToBuyTab = _navigateToBuyTab.asSharedFlow()
+
+ init {
+ viewModelScope.launch {
+ productRepository.homeProducts.collect { _homeProducts.value = it }
+ }
+ viewModelScope.launch {
+ productRepository.buyProducts.collect { _buyProducts.value = it }
+ }
+ }
+
+ fun toggleFavorite(product: Product) {
+ viewModelScope.launch {
+ val newFavStatus = !product.isFavorite
+
+ val updatedBuyList = _buyProducts.value.map {
+ if (it.id == product.id) it.copy(isFavorite = newFavStatus) else it
+ }
+ _buyProducts.value = updatedBuyList
+ productRepository.saveBuyProducts(updatedBuyList)
+
+ val updatedHomeList = _homeProducts.value.map {
+ if (it.id == product.id) it.copy(isFavorite = newFavStatus) else it
+ }
+ _homeProducts.value = updatedHomeList
+ productRepository.saveHomeProducts(updatedHomeList)
+
+ if (_selectedProduct.value?.id == product.id) {
+ _selectedProduct.value = _selectedProduct.value?.copy(isFavorite = newFavStatus)
+ }
+ }
+ }
+
+ fun requestNavigateToDetail(product: Product) {
+ _selectedProduct.value = product
+ viewModelScope.launch { _navigateToDetail.emit(Unit) }
+ }
+
+ fun requestNavigateToBuyTab() {
+ viewModelScope.launch { _navigateToBuyTab.emit(Unit) }
+ }
+}
diff --git a/jongyee/Week6/app/src/main/java/com/example/week6/UserDto.kt b/jongyee/Week6/app/src/main/java/com/example/week6/UserDto.kt
new file mode 100644
index 0000000..ec96b6e
--- /dev/null
+++ b/jongyee/Week6/app/src/main/java/com/example/week6/UserDto.kt
@@ -0,0 +1,23 @@
+package com.example.week6
+
+import com.google.gson.annotations.SerializedName
+
+data class UserDto(
+ val id: Int,
+ val email: String,
+ @SerializedName("first_name") val firstName: String,
+ @SerializedName("last_name") val lastName: String,
+ val avatar: String
+)
+
+data class SingleUserResponse(
+ val data: UserDto
+)
+
+data class UserListResponse(
+ val page: Int,
+ @SerializedName("per_page") val perPage: Int,
+ val total: Int,
+ @SerializedName("total_pages") val totalPages: Int,
+ val data: List
+)
diff --git a/jongyee/Week6/app/src/main/java/com/example/week6/WishlistFragment.kt b/jongyee/Week6/app/src/main/java/com/example/week6/WishlistFragment.kt
new file mode 100644
index 0000000..8e0460e
--- /dev/null
+++ b/jongyee/Week6/app/src/main/java/com/example/week6/WishlistFragment.kt
@@ -0,0 +1,56 @@
+package com.example.week6
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.activityViewModels
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import androidx.recyclerview.widget.GridLayoutManager
+import com.example.week6.databinding.FragmentWishlistBinding
+import dagger.hilt.android.AndroidEntryPoint
+import kotlinx.coroutines.launch
+
+@AndroidEntryPoint
+class WishlistFragment : Fragment() {
+ private var _binding: FragmentWishlistBinding? = null
+ private val binding get() = _binding!!
+
+ private val sharedViewModel: SharedViewModel by activityViewModels()
+
+ private val productAdapter by lazy {
+ ProductAdapter(emptyList(), isGrid = true,
+ onItemClick = { product -> sharedViewModel.requestNavigateToDetail(product) },
+ onHeartClick = { product -> sharedViewModel.toggleFavorite(product) }
+ )
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+ _binding = FragmentWishlistBinding.inflate(inflater, container, false)
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ binding.wishlistRvProducts.adapter = productAdapter
+ binding.wishlistRvProducts.layoutManager = GridLayoutManager(context, 2)
+
+ viewLifecycleOwner.lifecycleScope.launch {
+ viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
+ sharedViewModel.buyProducts.collect { products ->
+ val wishItems = products.filter { it.isFavorite }
+ productAdapter.updateData(wishItems)
+ }
+ }
+ }
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ _binding = null
+ }
+}
diff --git a/jongyee/Week6/app/src/main/java/com/example/week6/data/local/ProductLocalDataSource.kt b/jongyee/Week6/app/src/main/java/com/example/week6/data/local/ProductLocalDataSource.kt
new file mode 100644
index 0000000..88508a1
--- /dev/null
+++ b/jongyee/Week6/app/src/main/java/com/example/week6/data/local/ProductLocalDataSource.kt
@@ -0,0 +1,45 @@
+package com.example.week6.data.local
+
+import android.content.Context
+import androidx.datastore.preferences.core.edit
+import androidx.datastore.preferences.core.stringPreferencesKey
+import androidx.datastore.preferences.preferencesDataStore
+import com.example.week6.Product
+import com.google.gson.Gson
+import com.google.gson.reflect.TypeToken
+import dagger.hilt.android.qualifiers.ApplicationContext
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+import javax.inject.Inject
+import javax.inject.Singleton
+
+private val Context.dataStore by preferencesDataStore(name = "nike_store")
+
+@Singleton
+class ProductLocalDataSource @Inject constructor(
+ @ApplicationContext private val context: Context
+) {
+ private val gson = Gson()
+ private val HOME_KEY = stringPreferencesKey("home_products")
+ private val BUY_KEY = stringPreferencesKey("buy_products")
+
+ val homeProductsFlow: Flow?> = context.dataStore.data.map { prefs ->
+ prefs[HOME_KEY]?.let { json ->
+ gson.fromJson(json, object : TypeToken>() {}.type)
+ }
+ }
+
+ val buyProductsFlow: Flow?> = context.dataStore.data.map { prefs ->
+ prefs[BUY_KEY]?.let { json ->
+ gson.fromJson(json, object : TypeToken>() {}.type)
+ }
+ }
+
+ suspend fun saveHomeProducts(products: List) {
+ context.dataStore.edit { it[HOME_KEY] = gson.toJson(products) }
+ }
+
+ suspend fun saveBuyProducts(products: List) {
+ context.dataStore.edit { it[BUY_KEY] = gson.toJson(products) }
+ }
+}
diff --git a/jongyee/Week6/app/src/main/java/com/example/week6/data/remote/ReqResService.kt b/jongyee/Week6/app/src/main/java/com/example/week6/data/remote/ReqResService.kt
new file mode 100644
index 0000000..fe7c54c
--- /dev/null
+++ b/jongyee/Week6/app/src/main/java/com/example/week6/data/remote/ReqResService.kt
@@ -0,0 +1,16 @@
+package com.example.week6.data.remote
+
+import com.example.week6.SingleUserResponse
+import com.example.week6.UserListResponse
+import retrofit2.http.GET
+import retrofit2.http.Path
+import retrofit2.http.Query
+
+interface ReqResService {
+
+ @GET("users/{id}")
+ suspend fun getUser(@Path("id") id: Int): SingleUserResponse
+
+ @GET("users")
+ suspend fun getUsers(@Query("page") page: Int): UserListResponse
+}
diff --git a/jongyee/Week6/app/src/main/java/com/example/week6/data/repository/ProductRepositoryImpl.kt b/jongyee/Week6/app/src/main/java/com/example/week6/data/repository/ProductRepositoryImpl.kt
new file mode 100644
index 0000000..c1f7c7b
--- /dev/null
+++ b/jongyee/Week6/app/src/main/java/com/example/week6/data/repository/ProductRepositoryImpl.kt
@@ -0,0 +1,40 @@
+package com.example.week6.data.repository
+
+import com.example.week6.Product
+import com.example.week6.R
+import com.example.week6.data.local.ProductLocalDataSource
+import com.example.week6.domain.repository.ProductRepository
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class ProductRepositoryImpl @Inject constructor(
+ private val localDataSource: ProductLocalDataSource
+) : ProductRepository {
+
+ private val defaultHomeProducts = listOf(
+ Product(1, "Air Jordan XXXVI", "US$185", "Latest performance basketball shoe.", R.drawable.jordan),
+ Product(2, "Nike Dunk Low", "US$110", "A hoop icon returns with classic colors.", R.drawable.airforce)
+ )
+
+ private val defaultBuyProducts = listOf(
+ Product(3, "Nike Everyday Plus Cushioned", "US$10", "Training Ankle Socks", R.drawable.trainingsocks, category = "Training Ankle Socks (6 Pairs)", colorsCount = "5 Colours"),
+ Product(4, "Nike Elite Crew", "US$16", "Performance Socks", R.drawable.jordan, category = "Basketball Socks", colorsCount = "7 Colours"),
+ Product(5, "Nike Air Force 1 '07", "US$115", "The legend lives on", R.drawable.airforce, subTitle = "BestSeller", category = "Women's Shoes", colorsCount = "5 Colours"),
+ Product(6, "Jordan Series .05", "US$115", "Men's Shoes", R.drawable.crewsocks, subTitle = "BestSeller", category = "Men's Shoes", colorsCount = "2 Colours")
+ )
+
+ override val homeProducts: Flow> =
+ localDataSource.homeProductsFlow.map { it ?: defaultHomeProducts }
+
+ override val buyProducts: Flow> =
+ localDataSource.buyProductsFlow.map { it ?: defaultBuyProducts }
+
+ override suspend fun saveHomeProducts(products: List) =
+ localDataSource.saveHomeProducts(products)
+
+ override suspend fun saveBuyProducts(products: List) =
+ localDataSource.saveBuyProducts(products)
+}
diff --git a/jongyee/Week6/app/src/main/java/com/example/week6/data/repository/ProfileRepositoryImpl.kt b/jongyee/Week6/app/src/main/java/com/example/week6/data/repository/ProfileRepositoryImpl.kt
new file mode 100644
index 0000000..8ee0391
--- /dev/null
+++ b/jongyee/Week6/app/src/main/java/com/example/week6/data/repository/ProfileRepositoryImpl.kt
@@ -0,0 +1,25 @@
+package com.example.week6.data.repository
+
+import com.example.week6.UserDto
+import com.example.week6.data.remote.ReqResService
+import com.example.week6.domain.repository.ProfileRepository
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class ProfileRepositoryImpl @Inject constructor(
+ private val service: ReqResService
+) : ProfileRepository {
+
+ override suspend fun getUser(id: Int): Result = try {
+ Result.success(service.getUser(id).data)
+ } catch (e: Exception) {
+ Result.failure(e)
+ }
+
+ override suspend fun getUsers(page: Int): Result> = try {
+ Result.success(service.getUsers(page).data)
+ } catch (e: Exception) {
+ Result.failure(e)
+ }
+}
diff --git a/jongyee/Week6/app/src/main/java/com/example/week6/di/NetworkModule.kt b/jongyee/Week6/app/src/main/java/com/example/week6/di/NetworkModule.kt
new file mode 100644
index 0000000..c6094de
--- /dev/null
+++ b/jongyee/Week6/app/src/main/java/com/example/week6/di/NetworkModule.kt
@@ -0,0 +1,56 @@
+package com.example.week6.di
+
+import com.example.week6.data.remote.ReqResService
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import okhttp3.Interceptor
+import okhttp3.OkHttpClient
+import okhttp3.logging.HttpLoggingInterceptor
+import retrofit2.Retrofit
+import retrofit2.converter.gson.GsonConverterFactory
+import java.util.concurrent.TimeUnit
+import javax.inject.Singleton
+
+@Module
+@InstallIn(SingletonComponent::class)
+object NetworkModule {
+
+ private const val BASE_URL = "https://reqres.in/api/"
+ private const val API_KEY = "reqres_701d6373134941c7ba1e30b84be78104"
+
+ @Provides
+ @Singleton
+ fun provideOkHttpClient(): OkHttpClient {
+ val logging = HttpLoggingInterceptor().apply {
+ level = HttpLoggingInterceptor.Level.BODY
+ }
+ val authInterceptor = Interceptor { chain ->
+ val request = chain.request().newBuilder()
+ .addHeader("x-api-key", API_KEY)
+ .build()
+ chain.proceed(request)
+ }
+ return OkHttpClient.Builder()
+ .addInterceptor(authInterceptor)
+ .addInterceptor(logging)
+ .connectTimeout(30, TimeUnit.SECONDS)
+ .readTimeout(30, TimeUnit.SECONDS)
+ .writeTimeout(30, TimeUnit.SECONDS)
+ .build()
+ }
+
+ @Provides
+ @Singleton
+ fun provideRetrofit(client: OkHttpClient): Retrofit = Retrofit.Builder()
+ .baseUrl(BASE_URL)
+ .client(client)
+ .addConverterFactory(GsonConverterFactory.create())
+ .build()
+
+ @Provides
+ @Singleton
+ fun provideReqResService(retrofit: Retrofit): ReqResService =
+ retrofit.create(ReqResService::class.java)
+}
diff --git a/jongyee/Week6/app/src/main/java/com/example/week6/di/RepositoryModule.kt b/jongyee/Week6/app/src/main/java/com/example/week6/di/RepositoryModule.kt
new file mode 100644
index 0000000..091295c
--- /dev/null
+++ b/jongyee/Week6/app/src/main/java/com/example/week6/di/RepositoryModule.kt
@@ -0,0 +1,24 @@
+package com.example.week6.di
+
+import com.example.week6.data.repository.ProductRepositoryImpl
+import com.example.week6.data.repository.ProfileRepositoryImpl
+import com.example.week6.domain.repository.ProductRepository
+import com.example.week6.domain.repository.ProfileRepository
+import dagger.Binds
+import dagger.Module
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import javax.inject.Singleton
+
+@Module
+@InstallIn(SingletonComponent::class)
+abstract class RepositoryModule {
+
+ @Binds
+ @Singleton
+ abstract fun bindProfileRepository(impl: ProfileRepositoryImpl): ProfileRepository
+
+ @Binds
+ @Singleton
+ abstract fun bindProductRepository(impl: ProductRepositoryImpl): ProductRepository
+}
diff --git a/jongyee/Week6/app/src/main/java/com/example/week6/domain/repository/ProductRepository.kt b/jongyee/Week6/app/src/main/java/com/example/week6/domain/repository/ProductRepository.kt
new file mode 100644
index 0000000..9867d3d
--- /dev/null
+++ b/jongyee/Week6/app/src/main/java/com/example/week6/domain/repository/ProductRepository.kt
@@ -0,0 +1,11 @@
+package com.example.week6.domain.repository
+
+import com.example.week6.Product
+import kotlinx.coroutines.flow.Flow
+
+interface ProductRepository {
+ val homeProducts: Flow>
+ val buyProducts: Flow>
+ suspend fun saveHomeProducts(products: List)
+ suspend fun saveBuyProducts(products: List)
+}
diff --git a/jongyee/Week6/app/src/main/java/com/example/week6/domain/repository/ProfileRepository.kt b/jongyee/Week6/app/src/main/java/com/example/week6/domain/repository/ProfileRepository.kt
new file mode 100644
index 0000000..e9b26ad
--- /dev/null
+++ b/jongyee/Week6/app/src/main/java/com/example/week6/domain/repository/ProfileRepository.kt
@@ -0,0 +1,8 @@
+package com.example.week6.domain.repository
+
+import com.example.week6.UserDto
+
+interface ProfileRepository {
+ suspend fun getUser(id: Int): Result
+ suspend fun getUsers(page: Int): Result>
+}
diff --git a/jongyee/Week6/app/src/main/res/drawable/airforce.png b/jongyee/Week6/app/src/main/res/drawable/airforce.png
new file mode 100644
index 0000000..99fcf6c
Binary files /dev/null and b/jongyee/Week6/app/src/main/res/drawable/airforce.png differ
diff --git a/jongyee/Week6/app/src/main/res/drawable/bag.xml b/jongyee/Week6/app/src/main/res/drawable/bag.xml
new file mode 100644
index 0000000..9e97beb
--- /dev/null
+++ b/jongyee/Week6/app/src/main/res/drawable/bag.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/jongyee/Week6/app/src/main/res/drawable/bagcircle.xml b/jongyee/Week6/app/src/main/res/drawable/bagcircle.xml
new file mode 100644
index 0000000..1af2ce6
--- /dev/null
+++ b/jongyee/Week6/app/src/main/res/drawable/bagcircle.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
diff --git a/jongyee/Week6/app/src/main/res/drawable/buy.xml b/jongyee/Week6/app/src/main/res/drawable/buy.xml
new file mode 100644
index 0000000..483d117
--- /dev/null
+++ b/jongyee/Week6/app/src/main/res/drawable/buy.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
diff --git a/jongyee/Week6/app/src/main/res/drawable/crewsocks.png b/jongyee/Week6/app/src/main/res/drawable/crewsocks.png
new file mode 100644
index 0000000..d7c6ef1
Binary files /dev/null and b/jongyee/Week6/app/src/main/res/drawable/crewsocks.png differ
diff --git a/jongyee/Week6/app/src/main/res/drawable/event.png b/jongyee/Week6/app/src/main/res/drawable/event.png
new file mode 100644
index 0000000..704512e
Binary files /dev/null and b/jongyee/Week6/app/src/main/res/drawable/event.png differ
diff --git a/jongyee/Week6/app/src/main/res/drawable/home.xml b/jongyee/Week6/app/src/main/res/drawable/home.xml
new file mode 100644
index 0000000..a0d86b9
--- /dev/null
+++ b/jongyee/Week6/app/src/main/res/drawable/home.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
diff --git a/jongyee/Week6/app/src/main/res/drawable/ic_arrow_right.xml b/jongyee/Week6/app/src/main/res/drawable/ic_arrow_right.xml
new file mode 100644
index 0000000..19201cb
--- /dev/null
+++ b/jongyee/Week6/app/src/main/res/drawable/ic_arrow_right.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/jongyee/Week6/app/src/main/res/drawable/ic_heart_empty.xml b/jongyee/Week6/app/src/main/res/drawable/ic_heart_empty.xml
new file mode 100644
index 0000000..59b3907
--- /dev/null
+++ b/jongyee/Week6/app/src/main/res/drawable/ic_heart_empty.xml
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/jongyee/Week6/app/src/main/res/drawable/ic_heart_filled.xml b/jongyee/Week6/app/src/main/res/drawable/ic_heart_filled.xml
new file mode 100644
index 0000000..85961df
--- /dev/null
+++ b/jongyee/Week6/app/src/main/res/drawable/ic_heart_filled.xml
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/jongyee/Week6/app/src/main/res/drawable/ic_launcher_background.xml b/jongyee/Week6/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..07d5da9
--- /dev/null
+++ b/jongyee/Week6/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jongyee/Week6/app/src/main/res/drawable/ic_launcher_foreground.xml b/jongyee/Week6/app/src/main/res/drawable/ic_launcher_foreground.xml
new file mode 100644
index 0000000..2b068d1
--- /dev/null
+++ b/jongyee/Week6/app/src/main/res/drawable/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/jongyee/Week6/app/src/main/res/drawable/image.png b/jongyee/Week6/app/src/main/res/drawable/image.png
new file mode 100644
index 0000000..f42aa9d
Binary files /dev/null and b/jongyee/Week6/app/src/main/res/drawable/image.png differ
diff --git a/jongyee/Week6/app/src/main/res/drawable/jordan.png b/jongyee/Week6/app/src/main/res/drawable/jordan.png
new file mode 100644
index 0000000..1bfc3f2
Binary files /dev/null and b/jongyee/Week6/app/src/main/res/drawable/jordan.png differ
diff --git a/jongyee/Week6/app/src/main/res/drawable/order.png b/jongyee/Week6/app/src/main/res/drawable/order.png
new file mode 100644
index 0000000..61200c1
Binary files /dev/null and b/jongyee/Week6/app/src/main/res/drawable/order.png differ
diff --git a/jongyee/Week6/app/src/main/res/drawable/pass.png b/jongyee/Week6/app/src/main/res/drawable/pass.png
new file mode 100644
index 0000000..af82b3b
Binary files /dev/null and b/jongyee/Week6/app/src/main/res/drawable/pass.png differ
diff --git a/jongyee/Week6/app/src/main/res/drawable/profile.xml b/jongyee/Week6/app/src/main/res/drawable/profile.xml
new file mode 100644
index 0000000..1fc97b8
--- /dev/null
+++ b/jongyee/Week6/app/src/main/res/drawable/profile.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
diff --git a/jongyee/Week6/app/src/main/res/drawable/setting.png b/jongyee/Week6/app/src/main/res/drawable/setting.png
new file mode 100644
index 0000000..ed55245
Binary files /dev/null and b/jongyee/Week6/app/src/main/res/drawable/setting.png differ
diff --git a/jongyee/Week6/app/src/main/res/drawable/shape_circle_gray.xml b/jongyee/Week6/app/src/main/res/drawable/shape_circle_gray.xml
new file mode 100644
index 0000000..1f18f34
--- /dev/null
+++ b/jongyee/Week6/app/src/main/res/drawable/shape_circle_gray.xml
@@ -0,0 +1,5 @@
+
+
+
+
\ No newline at end of file
diff --git a/jongyee/Week6/app/src/main/res/drawable/trainingsocks.png b/jongyee/Week6/app/src/main/res/drawable/trainingsocks.png
new file mode 100644
index 0000000..fc41659
Binary files /dev/null and b/jongyee/Week6/app/src/main/res/drawable/trainingsocks.png differ
diff --git a/jongyee/Week6/app/src/main/res/drawable/wishlist.xml b/jongyee/Week6/app/src/main/res/drawable/wishlist.xml
new file mode 100644
index 0000000..6597a4a
--- /dev/null
+++ b/jongyee/Week6/app/src/main/res/drawable/wishlist.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
diff --git a/jongyee/Week6/app/src/main/res/layout/activity_main.xml b/jongyee/Week6/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..51565b5
--- /dev/null
+++ b/jongyee/Week6/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
diff --git a/jongyee/Week6/app/src/main/res/layout/activity_profile_edit.xml b/jongyee/Week6/app/src/main/res/layout/activity_profile_edit.xml
new file mode 100644
index 0000000..77f947b
--- /dev/null
+++ b/jongyee/Week6/app/src/main/res/layout/activity_profile_edit.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
diff --git a/jongyee/Week6/app/src/main/res/layout/fragment_bag.xml b/jongyee/Week6/app/src/main/res/layout/fragment_bag.xml
new file mode 100644
index 0000000..1a5ff2c
--- /dev/null
+++ b/jongyee/Week6/app/src/main/res/layout/fragment_bag.xml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jongyee/Week6/app/src/main/res/layout/fragment_buy.xml b/jongyee/Week6/app/src/main/res/layout/fragment_buy.xml
new file mode 100644
index 0000000..10058eb
--- /dev/null
+++ b/jongyee/Week6/app/src/main/res/layout/fragment_buy.xml
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jongyee/Week6/app/src/main/res/layout/fragment_detail.xml b/jongyee/Week6/app/src/main/res/layout/fragment_detail.xml
new file mode 100644
index 0000000..e7411ef
--- /dev/null
+++ b/jongyee/Week6/app/src/main/res/layout/fragment_detail.xml
@@ -0,0 +1,131 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jongyee/Week6/app/src/main/res/layout/fragment_home.xml b/jongyee/Week6/app/src/main/res/layout/fragment_home.xml
new file mode 100644
index 0000000..d669bc5
--- /dev/null
+++ b/jongyee/Week6/app/src/main/res/layout/fragment_home.xml
@@ -0,0 +1,93 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jongyee/Week6/app/src/main/res/layout/fragment_profile.xml b/jongyee/Week6/app/src/main/res/layout/fragment_profile.xml
new file mode 100644
index 0000000..8daebc9
--- /dev/null
+++ b/jongyee/Week6/app/src/main/res/layout/fragment_profile.xml
@@ -0,0 +1,250 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/jongyee/Week6/app/src/main/res/layout/fragment_wishlist.xml b/jongyee/Week6/app/src/main/res/layout/fragment_wishlist.xml
new file mode 100644
index 0000000..9b4d04c
--- /dev/null
+++ b/jongyee/Week6/app/src/main/res/layout/fragment_wishlist.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
diff --git a/jongyee/Week6/app/src/main/res/layout/item_following.xml b/jongyee/Week6/app/src/main/res/layout/item_following.xml
new file mode 100644
index 0000000..4f8d364
--- /dev/null
+++ b/jongyee/Week6/app/src/main/res/layout/item_following.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
diff --git a/jongyee/Week6/app/src/main/res/layout/item_grid_product.xml b/jongyee/Week6/app/src/main/res/layout/item_grid_product.xml
new file mode 100644
index 0000000..299315b
--- /dev/null
+++ b/jongyee/Week6/app/src/main/res/layout/item_grid_product.xml
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jongyee/Week6/app/src/main/res/layout/item_home_product.xml b/jongyee/Week6/app/src/main/res/layout/item_home_product.xml
new file mode 100644
index 0000000..fd414d9
--- /dev/null
+++ b/jongyee/Week6/app/src/main/res/layout/item_home_product.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
diff --git a/jongyee/Week6/app/src/main/res/menu/bottom_nav_menu.xml b/jongyee/Week6/app/src/main/res/menu/bottom_nav_menu.xml
new file mode 100644
index 0000000..788eb4f
--- /dev/null
+++ b/jongyee/Week6/app/src/main/res/menu/bottom_nav_menu.xml
@@ -0,0 +1,23 @@
+
+
diff --git a/jongyee/Week6/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/jongyee/Week6/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..6f3b755
--- /dev/null
+++ b/jongyee/Week6/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/jongyee/Week6/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/jongyee/Week6/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..6f3b755
--- /dev/null
+++ b/jongyee/Week6/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/jongyee/Week6/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/jongyee/Week6/app/src/main/res/mipmap-hdpi/ic_launcher.webp
new file mode 100644
index 0000000..c209e78
Binary files /dev/null and b/jongyee/Week6/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ
diff --git a/jongyee/Week6/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/jongyee/Week6/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..b2dfe3d
Binary files /dev/null and b/jongyee/Week6/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ
diff --git a/jongyee/Week6/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/jongyee/Week6/app/src/main/res/mipmap-mdpi/ic_launcher.webp
new file mode 100644
index 0000000..4f0f1d6
Binary files /dev/null and b/jongyee/Week6/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ
diff --git a/jongyee/Week6/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/jongyee/Week6/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..62b611d
Binary files /dev/null and b/jongyee/Week6/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ
diff --git a/jongyee/Week6/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/jongyee/Week6/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 0000000..948a307
Binary files /dev/null and b/jongyee/Week6/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ
diff --git a/jongyee/Week6/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/jongyee/Week6/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..1b9a695
Binary files /dev/null and b/jongyee/Week6/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ
diff --git a/jongyee/Week6/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/jongyee/Week6/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..28d4b77
Binary files /dev/null and b/jongyee/Week6/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ
diff --git a/jongyee/Week6/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/jongyee/Week6/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..9287f50
Binary files /dev/null and b/jongyee/Week6/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ
diff --git a/jongyee/Week6/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/jongyee/Week6/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..aa7d642
Binary files /dev/null and b/jongyee/Week6/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ
diff --git a/jongyee/Week6/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/jongyee/Week6/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..9126ae3
Binary files /dev/null and b/jongyee/Week6/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ
diff --git a/jongyee/Week6/app/src/main/res/values-night/themes.xml b/jongyee/Week6/app/src/main/res/values-night/themes.xml
new file mode 100644
index 0000000..f8a16ff
--- /dev/null
+++ b/jongyee/Week6/app/src/main/res/values-night/themes.xml
@@ -0,0 +1,6 @@
+
+
+
+
diff --git a/jongyee/Week6/app/src/main/res/values/colors.xml b/jongyee/Week6/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..8478258
--- /dev/null
+++ b/jongyee/Week6/app/src/main/res/values/colors.xml
@@ -0,0 +1,9 @@
+
+
+ #FFFFFF
+ #000000
+ #F5F5F5
+ #888888
+ #333333
+ #DDDDDD
+
diff --git a/jongyee/Week6/app/src/main/res/values/strings.xml b/jongyee/Week6/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..013d7a7
--- /dev/null
+++ b/jongyee/Week6/app/src/main/res/values/strings.xml
@@ -0,0 +1,30 @@
+
+ Hello blank fragment
+ 홈
+ 구매하기
+ 위시리스트
+ 장바구니
+ 프로필
+ Discover
+ 9월 4일 목요일
+ 전체
+ Tops & T-Shirts
+ Shoes
+ 위시리스트
+ 장바구니가 비어 있습니다.\n제품을 추가하면 여기에 표시됩니다.
+ 주문하기
+ Nike Week5
+ 사이즈 선택
+ 장바구니에 추가
+ 위시리스트
+ 프로필 수정
+ sale
+
+ 전체
+ 나이키 최신 상품
+ What\'s new
+ 프로필 수정
+ 위시리스트
+ BestSeller
+ 팔로잉
+
diff --git a/jongyee/Week6/app/src/main/res/values/themes.xml b/jongyee/Week6/app/src/main/res/values/themes.xml
new file mode 100644
index 0000000..48fda23
--- /dev/null
+++ b/jongyee/Week6/app/src/main/res/values/themes.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
diff --git a/jongyee/Week6/app/src/main/res/xml/backup_rules.xml b/jongyee/Week6/app/src/main/res/xml/backup_rules.xml
new file mode 100644
index 0000000..4df9255
--- /dev/null
+++ b/jongyee/Week6/app/src/main/res/xml/backup_rules.xml
@@ -0,0 +1,13 @@
+
+
+
+
\ No newline at end of file
diff --git a/jongyee/Week6/app/src/main/res/xml/data_extraction_rules.xml b/jongyee/Week6/app/src/main/res/xml/data_extraction_rules.xml
new file mode 100644
index 0000000..9ee9997
--- /dev/null
+++ b/jongyee/Week6/app/src/main/res/xml/data_extraction_rules.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/jongyee/Week6/app/src/test/java/com/example/week6/ExampleUnitTest.kt b/jongyee/Week6/app/src/test/java/com/example/week6/ExampleUnitTest.kt
new file mode 100644
index 0000000..25a37a7
--- /dev/null
+++ b/jongyee/Week6/app/src/test/java/com/example/week6/ExampleUnitTest.kt
@@ -0,0 +1,11 @@
+package com.example.week6
+
+import org.junit.Test
+import org.junit.Assert.*
+
+class ExampleUnitTest {
+ @Test
+ fun addition_isCorrect() {
+ assertEquals(4, 2 + 2)
+ }
+}
diff --git a/jongyee/Week6/build.gradle.kts b/jongyee/Week6/build.gradle.kts
new file mode 100644
index 0000000..f7cc58e
--- /dev/null
+++ b/jongyee/Week6/build.gradle.kts
@@ -0,0 +1,6 @@
+plugins {
+ alias(libs.plugins.android.application) apply false
+ alias(libs.plugins.kotlin.android) apply false
+ alias(libs.plugins.kotlin.parcelize) apply false
+ alias(libs.plugins.hilt.android) apply false
+}
diff --git a/jongyee/Week6/gradle.properties b/jongyee/Week6/gradle.properties
new file mode 100644
index 0000000..d621849
--- /dev/null
+++ b/jongyee/Week6/gradle.properties
@@ -0,0 +1,16 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. For more details, visit
+# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
+# org.gradle.parallel=true
+# Kotlin code style for this project: "official" or "obsolete":
+kotlin.code.style=official
+android.useAndroidX=true
diff --git a/jongyee/Week6/gradle/gradle-daemon-jvm.properties b/jongyee/Week6/gradle/gradle-daemon-jvm.properties
new file mode 100644
index 0000000..6c1139e
--- /dev/null
+++ b/jongyee/Week6/gradle/gradle-daemon-jvm.properties
@@ -0,0 +1,12 @@
+#This file is generated by updateDaemonJvm
+toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/ec7520a1e057cd116f9544c42142a16b/redirect
+toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/4c4f879899012ff0a8b2e2117df03b0e/redirect
+toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/ec7520a1e057cd116f9544c42142a16b/redirect
+toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/4c4f879899012ff0a8b2e2117df03b0e/redirect
+toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/73bcfb608d1fde9fb62e462f834a3299/redirect
+toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/846ee0d876d26a26f37aa1ce8de73224/redirect
+toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/ec7520a1e057cd116f9544c42142a16b/redirect
+toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/4c4f879899012ff0a8b2e2117df03b0e/redirect
+toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/9482ddec596298c84656d31d16652665/redirect
+toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/39701d92e1756bb2f141eb67cd4c660e/redirect
+toolchainVersion=21
diff --git a/jongyee/Week6/gradle/libs.versions.toml b/jongyee/Week6/gradle/libs.versions.toml
new file mode 100644
index 0000000..3695e59
--- /dev/null
+++ b/jongyee/Week6/gradle/libs.versions.toml
@@ -0,0 +1,53 @@
+[versions]
+agp = "8.9.1"
+kotlin = "2.1.0"
+coreKtx = "1.18.0"
+junit = "4.13.2"
+junitVersion = "1.3.0"
+espressoCore = "3.7.0"
+appcompat = "1.7.1"
+material = "1.13.0"
+activity = "1.13.0"
+constraintlayout = "2.2.1"
+datastorePreferences = "1.2.1"
+gson = "2.13.2"
+fragmentKtx = "1.8.9"
+lifecycle = "2.10.0"
+retrofit = "2.9.0"
+okhttp = "4.12.0"
+coroutines = "1.8.0"
+glide = "4.16.0"
+hilt = "2.51.1"
+
+[libraries]
+androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
+junit = { group = "junit", name = "junit", version.ref = "junit" }
+androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
+androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
+androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
+material = { group = "com.google.android.material", name = "material", version.ref = "material" }
+androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
+androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
+
+androidx-datastore-preferences = { group = "androidx.datastore", name = "datastore-preferences", version.ref = "datastorePreferences" }
+gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" }
+
+androidx-fragment-ktx = { group = "androidx.fragment", name = "fragment-ktx", version.ref = "fragmentKtx" }
+androidx-lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycle" }
+androidx-lifecycle-livedata-ktx = { group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version.ref = "lifecycle" }
+
+retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }
+retrofit-converter-gson = { group = "com.squareup.retrofit2", name = "converter-gson", version.ref = "retrofit" }
+okhttp-logging-interceptor = { group = "com.squareup.okhttp3", name = "logging-interceptor", version.ref = "okhttp" }
+kotlinx-coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "coroutines" }
+kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "coroutines" }
+glide = { group = "com.github.bumptech.glide", name = "glide", version.ref = "glide" }
+
+hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" }
+hilt-compiler = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hilt" }
+
+[plugins]
+android-application = { id = "com.android.application", version.ref = "agp" }
+kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
+kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" }
+hilt-android = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
diff --git a/jongyee/Week6/gradle/wrapper/gradle-wrapper.jar b/jongyee/Week6/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..8bdaf60
Binary files /dev/null and b/jongyee/Week6/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/jongyee/Week6/gradle/wrapper/gradle-wrapper.properties b/jongyee/Week6/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..318e0f5
--- /dev/null
+++ b/jongyee/Week6/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,9 @@
+#Sat Mar 28 18:15:36 KST 2026
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionSha256Sum=b266d5ff6b90eada6dc3b20cb090e3731302e553a27c5d3e4df1f0d76beaff06
+distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip
+networkTimeout=10000
+validateDistributionUrl=true
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/jongyee/Week6/settings.gradle.kts b/jongyee/Week6/settings.gradle.kts
new file mode 100644
index 0000000..0dc3cb0
--- /dev/null
+++ b/jongyee/Week6/settings.gradle.kts
@@ -0,0 +1,26 @@
+pluginManagement {
+ repositories {
+ google {
+ content {
+ includeGroupByRegex("com\\.android.*")
+ includeGroupByRegex("com\\.google.*")
+ includeGroupByRegex("androidx.*")
+ }
+ }
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
+plugins {
+ id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
+}
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+rootProject.name = "Week6"
+include(":app")
diff --git a/jongyee/Week7/.gitignore b/jongyee/Week7/.gitignore
new file mode 100644
index 0000000..aa724b7
--- /dev/null
+++ b/jongyee/Week7/.gitignore
@@ -0,0 +1,15 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
+local.properties
diff --git a/jongyee/Week7/app/.gitignore b/jongyee/Week7/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/jongyee/Week7/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/jongyee/Week7/app/build.gradle.kts b/jongyee/Week7/app/build.gradle.kts
new file mode 100644
index 0000000..3a5b10d
--- /dev/null
+++ b/jongyee/Week7/app/build.gradle.kts
@@ -0,0 +1,62 @@
+plugins {
+ alias(libs.plugins.android.application)
+ alias(libs.plugins.kotlin.android)
+ alias(libs.plugins.kotlin.compose)
+ alias(libs.plugins.kotlin.serialization)
+}
+
+android {
+ namespace = "com.example.week7"
+ compileSdk = 36
+
+ defaultConfig {
+ applicationId = "com.example.week7"
+ minSdk = 24
+ targetSdk = 36
+ versionCode = 1
+ versionName = "1.0"
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ isMinifyEnabled = false
+ proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
+ }
+ }
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
+ }
+ kotlinOptions {
+ jvmTarget = "11"
+ }
+ buildFeatures {
+ compose = true
+ }
+}
+
+dependencies {
+ implementation(libs.androidx.core.ktx)
+ implementation(libs.androidx.appcompat)
+ implementation(libs.androidx.activity.compose)
+
+ // Compose BOM
+ implementation(platform(libs.androidx.compose.bom))
+ implementation(libs.androidx.compose.ui)
+ implementation(libs.androidx.compose.ui.graphics)
+ implementation(libs.androidx.compose.ui.tooling.preview)
+ implementation(libs.androidx.compose.material3)
+ implementation(libs.androidx.compose.material.icons.extended)
+
+ // Navigation
+ implementation(libs.androidx.navigation.compose)
+ implementation(libs.kotlinx.serialization.json)
+
+ testImplementation(libs.junit)
+ androidTestImplementation(libs.androidx.junit)
+ androidTestImplementation(libs.androidx.espresso.core)
+ androidTestImplementation(platform(libs.androidx.compose.bom))
+ androidTestImplementation(libs.androidx.compose.ui.test.junit4)
+ debugImplementation(libs.androidx.compose.ui.tooling)
+ debugImplementation(libs.androidx.compose.ui.test.manifest)
+}
diff --git a/jongyee/Week7/app/proguard-rules.pro b/jongyee/Week7/app/proguard-rules.pro
new file mode 100644
index 0000000..7e9d12c
--- /dev/null
+++ b/jongyee/Week7/app/proguard-rules.pro
@@ -0,0 +1,2 @@
+# Add project specific ProGuard rules here.
+-keep class com.example.week7.navigation.** { *; }
diff --git a/jongyee/Week7/app/src/androidTest/java/com/example/week7/ExampleInstrumentedTest.kt b/jongyee/Week7/app/src/androidTest/java/com/example/week7/ExampleInstrumentedTest.kt
new file mode 100644
index 0000000..b9cd473
--- /dev/null
+++ b/jongyee/Week7/app/src/androidTest/java/com/example/week7/ExampleInstrumentedTest.kt
@@ -0,0 +1,16 @@
+package com.example.week7
+
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.Assert.*
+
+@RunWith(AndroidJUnit4::class)
+class ExampleInstrumentedTest {
+ @Test
+ fun useAppContext() {
+ val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+ assertEquals("com.example.week7", appContext.packageName)
+ }
+}
diff --git a/jongyee/Week7/app/src/main/AndroidManifest.xml b/jongyee/Week7/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..981a0ce
--- /dev/null
+++ b/jongyee/Week7/app/src/main/AndroidManifest.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jongyee/Week7/app/src/main/java/com/example/week7/MainActivity.kt b/jongyee/Week7/app/src/main/java/com/example/week7/MainActivity.kt
new file mode 100644
index 0000000..6326a83
--- /dev/null
+++ b/jongyee/Week7/app/src/main/java/com/example/week7/MainActivity.kt
@@ -0,0 +1,20 @@
+package com.example.week7
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.activity.enableEdgeToEdge
+import com.example.week7.ui.MainScreen
+import com.example.week7.ui.theme.Week7Theme
+
+class MainActivity : ComponentActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ enableEdgeToEdge()
+ setContent {
+ Week7Theme {
+ MainScreen()
+ }
+ }
+ }
+}
diff --git a/jongyee/Week7/app/src/main/java/com/example/week7/navigation/AppDestination.kt b/jongyee/Week7/app/src/main/java/com/example/week7/navigation/AppDestination.kt
new file mode 100644
index 0000000..780bf88
--- /dev/null
+++ b/jongyee/Week7/app/src/main/java/com/example/week7/navigation/AppDestination.kt
@@ -0,0 +1,11 @@
+package com.example.week7.navigation
+
+import kotlinx.serialization.Serializable
+
+sealed interface AppDestination {
+ @Serializable data object Home : AppDestination
+ @Serializable data object Buy : AppDestination
+ @Serializable data object Wishlist : AppDestination
+ @Serializable data object Bag : AppDestination
+ @Serializable data object Profile : AppDestination
+}
diff --git a/jongyee/Week7/app/src/main/java/com/example/week7/ui/MainScreen.kt b/jongyee/Week7/app/src/main/java/com/example/week7/ui/MainScreen.kt
new file mode 100644
index 0000000..f4a9c52
--- /dev/null
+++ b/jongyee/Week7/app/src/main/java/com/example/week7/ui/MainScreen.kt
@@ -0,0 +1,107 @@
+package com.example.week7.ui
+
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Favorite
+import androidx.compose.material.icons.filled.Home
+import androidx.compose.material.icons.filled.Person
+import androidx.compose.material.icons.filled.ShoppingBag
+import androidx.compose.material.icons.filled.ShoppingCart
+import androidx.compose.material3.Icon
+import androidx.compose.material3.NavigationBar
+import androidx.compose.material3.NavigationBarItem
+import androidx.compose.material3.NavigationBarItemDefaults
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.navigation.NavDestination.Companion.hasRoute
+import androidx.navigation.compose.NavHost
+import androidx.navigation.compose.composable
+import androidx.navigation.compose.currentBackStackEntryAsState
+import androidx.navigation.compose.rememberNavController
+import com.example.week7.navigation.AppDestination
+import com.example.week7.ui.bag.BagScreen
+import com.example.week7.ui.buy.BuyScreen
+import com.example.week7.ui.home.HomeScreen
+import com.example.week7.ui.profile.ProfileScreen
+import com.example.week7.ui.theme.NikeBlack
+import com.example.week7.ui.theme.NikeGray
+import com.example.week7.ui.theme.NikeWhite
+import com.example.week7.ui.wishlist.WishlistScreen
+
+private data class BottomNavItem(
+ val label: String,
+ val icon: ImageVector,
+ val destination: AppDestination,
+)
+
+private val bottomNavItems = listOf(
+ BottomNavItem("홈", Icons.Default.Home, AppDestination.Home),
+ BottomNavItem("구매하기", Icons.Default.ShoppingCart, AppDestination.Buy),
+ BottomNavItem("위시리스트", Icons.Default.Favorite, AppDestination.Wishlist),
+ BottomNavItem("장바구니", Icons.Default.ShoppingBag, AppDestination.Bag),
+ BottomNavItem("프로필", Icons.Default.Person, AppDestination.Profile),
+)
+
+@Composable
+fun MainScreen() {
+ val navController = rememberNavController()
+ val navBackStackEntry by navController.currentBackStackEntryAsState()
+ val currentDestination = navBackStackEntry?.destination
+
+ Scaffold(
+ bottomBar = {
+ NavigationBar(containerColor = NikeWhite) {
+ bottomNavItems.forEach { item ->
+ val selected = currentDestination?.hasRoute(item.destination::class) == true
+ NavigationBarItem(
+ selected = selected,
+ onClick = {
+ navController.navigate(item.destination) {
+ popUpTo(AppDestination.Home) { saveState = true }
+ launchSingleTop = true
+ restoreState = true
+ }
+ },
+ icon = {
+ Icon(imageVector = item.icon, contentDescription = item.label)
+ },
+ label = { Text(item.label) },
+ colors = NavigationBarItemDefaults.colors(
+ selectedIconColor = NikeBlack,
+ selectedTextColor = NikeBlack,
+ unselectedIconColor = NikeGray,
+ unselectedTextColor = NikeGray,
+ indicatorColor = NikeWhite,
+ )
+ )
+ }
+ }
+ }
+ ) { innerPadding ->
+ NavHost(
+ navController = navController,
+ startDestination = AppDestination.Home,
+ modifier = Modifier.padding(innerPadding)
+ ) {
+ composable { HomeScreen() }
+ composable { BuyScreen() }
+ composable { WishlistScreen() }
+ composable {
+ BagScreen(
+ onNavigateToBuy = {
+ navController.navigate(AppDestination.Buy) {
+ popUpTo(AppDestination.Home) { saveState = true }
+ launchSingleTop = true
+ restoreState = true
+ }
+ }
+ )
+ }
+ composable { ProfileScreen() }
+ }
+ }
+}
diff --git a/jongyee/Week7/app/src/main/java/com/example/week7/ui/bag/BagScreen.kt b/jongyee/Week7/app/src/main/java/com/example/week7/ui/bag/BagScreen.kt
new file mode 100644
index 0000000..4368aef
--- /dev/null
+++ b/jongyee/Week7/app/src/main/java/com/example/week7/ui/bag/BagScreen.kt
@@ -0,0 +1,72 @@
+package com.example.week7.ui.bag
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.ShoppingBag
+import androidx.compose.material3.Button
+import androidx.compose.material3.ButtonDefaults
+import androidx.compose.material3.Icon
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import com.example.week7.R
+import com.example.week7.ui.theme.NikeBlack
+import com.example.week7.ui.theme.NikeWhite
+
+@Composable
+fun BagScreen(onNavigateToBuy: () -> Unit) {
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .background(NikeWhite)
+ ) {
+ Column(
+ modifier = Modifier.align(Alignment.Center),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Icon(
+ imageVector = Icons.Default.ShoppingBag,
+ contentDescription = null,
+ modifier = Modifier.size(60.dp),
+ tint = NikeBlack
+ )
+ Spacer(Modifier.height(27.dp))
+ Text(
+ text = stringResource(R.string.bag_empty_message),
+ fontSize = 16.sp,
+ fontWeight = FontWeight.Bold,
+ textAlign = TextAlign.Center
+ )
+ }
+ Button(
+ onClick = onNavigateToBuy,
+ modifier = Modifier
+ .align(Alignment.BottomCenter)
+ .fillMaxWidth()
+ .padding(horizontal = 24.dp, vertical = 32.dp)
+ .height(60.dp),
+ colors = ButtonDefaults.buttonColors(containerColor = NikeBlack)
+ ) {
+ Text(
+ text = stringResource(R.string.bag_order_button),
+ color = NikeWhite,
+ fontSize = 18.sp,
+ fontWeight = FontWeight.Bold
+ )
+ }
+ }
+}
diff --git a/jongyee/Week7/app/src/main/java/com/example/week7/ui/buy/BuyScreen.kt b/jongyee/Week7/app/src/main/java/com/example/week7/ui/buy/BuyScreen.kt
new file mode 100644
index 0000000..2614497
--- /dev/null
+++ b/jongyee/Week7/app/src/main/java/com/example/week7/ui/buy/BuyScreen.kt
@@ -0,0 +1,68 @@
+package com.example.week7.ui.buy
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.height
+import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.ScrollableTabRow
+import androidx.compose.material3.Tab
+import androidx.compose.material3.TabRowDefaults.SecondaryIndicator
+import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import com.example.week7.ui.theme.NikeBlack
+import com.example.week7.ui.theme.NikeGray
+import com.example.week7.ui.theme.NikeWhite
+
+private val tabs = listOf("전체", "Tops & T-Shirts", "Shoes")
+
+@Composable
+fun BuyScreen() {
+ var selectedTab by remember { mutableIntStateOf(0) }
+
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .background(NikeWhite)
+ ) {
+ Spacer(Modifier.height(65.dp))
+ ScrollableTabRow(
+ selectedTabIndex = selectedTab,
+ containerColor = NikeWhite,
+ contentColor = NikeBlack,
+ edgePadding = 0.dp,
+ indicator = { tabPositions ->
+ SecondaryIndicator(
+ modifier = Modifier.tabIndicatorOffset(tabPositions[selectedTab]),
+ color = NikeBlack,
+ height = 3.dp
+ )
+ },
+ divider = {}
+ ) {
+ tabs.forEachIndexed { index, title ->
+ Tab(
+ selected = selectedTab == index,
+ onClick = { selectedTab = index },
+ text = {
+ Text(
+ text = title,
+ color = if (selectedTab == index) NikeBlack else NikeGray,
+ fontWeight = if (selectedTab == index) FontWeight.Bold else FontWeight.Normal
+ )
+ }
+ )
+ }
+ }
+ HorizontalDivider(color = NikeGray, thickness = 1.dp)
+ }
+}
diff --git a/jongyee/Week7/app/src/main/java/com/example/week7/ui/home/HomeScreen.kt b/jongyee/Week7/app/src/main/java/com/example/week7/ui/home/HomeScreen.kt
new file mode 100644
index 0000000..7b95ad6
--- /dev/null
+++ b/jongyee/Week7/app/src/main/java/com/example/week7/ui/home/HomeScreen.kt
@@ -0,0 +1,55 @@
+package com.example.week7.ui.home
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import com.example.week7.R
+import com.example.week7.ui.theme.NikeBlack
+import com.example.week7.ui.theme.NikeGray
+import com.example.week7.ui.theme.NikeWhite
+
+@Composable
+fun HomeScreen() {
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .background(NikeWhite)
+ ) {
+ Column(
+ modifier = Modifier.padding(horizontal = 24.dp, vertical = 50.dp)
+ ) {
+ Text(
+ text = stringResource(R.string.home_title),
+ color = NikeBlack,
+ fontSize = 28.sp,
+ fontWeight = FontWeight.Bold
+ )
+ Spacer(Modifier.height(8.dp))
+ Text(
+ text = stringResource(R.string.home_date),
+ color = NikeGray,
+ fontSize = 16.sp
+ )
+ }
+ Image(
+ painter = painterResource(id = R.drawable.image),
+ contentDescription = stringResource(R.string.home_image_description),
+ modifier = Modifier.fillMaxWidth(),
+ contentScale = ContentScale.FillWidth
+ )
+ }
+}
diff --git a/jongyee/Week7/app/src/main/java/com/example/week7/ui/profile/ProfileScreen.kt b/jongyee/Week7/app/src/main/java/com/example/week7/ui/profile/ProfileScreen.kt
new file mode 100644
index 0000000..b9729e7
--- /dev/null
+++ b/jongyee/Week7/app/src/main/java/com/example/week7/ui/profile/ProfileScreen.kt
@@ -0,0 +1,43 @@
+package com.example.week7.ui.profile
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.height
+import androidx.compose.material3.Button
+import androidx.compose.material3.ButtonDefaults
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import com.example.week7.ui.theme.NikeBlack
+import com.example.week7.ui.theme.NikeWhite
+
+@Composable
+fun ProfileScreen() {
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .background(NikeWhite),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Spacer(Modifier.height(100.dp))
+ Text(
+ text = "프로필",
+ color = NikeBlack,
+ fontSize = 20.sp,
+ fontWeight = FontWeight.Bold
+ )
+ Spacer(Modifier.height(30.dp))
+ Button(
+ onClick = {},
+ colors = ButtonDefaults.buttonColors(containerColor = NikeBlack)
+ ) {
+ Text(text = "프로필 수정", color = NikeWhite)
+ }
+ }
+}
diff --git a/jongyee/Week7/app/src/main/java/com/example/week7/ui/theme/Color.kt b/jongyee/Week7/app/src/main/java/com/example/week7/ui/theme/Color.kt
new file mode 100644
index 0000000..7147aed
--- /dev/null
+++ b/jongyee/Week7/app/src/main/java/com/example/week7/ui/theme/Color.kt
@@ -0,0 +1,7 @@
+package com.example.week7.ui.theme
+
+import androidx.compose.ui.graphics.Color
+
+val NikeBlack = Color(0xFF000000)
+val NikeWhite = Color(0xFFFFFFFF)
+val NikeGray = Color(0xFF888888)
diff --git a/jongyee/Week7/app/src/main/java/com/example/week7/ui/theme/Theme.kt b/jongyee/Week7/app/src/main/java/com/example/week7/ui/theme/Theme.kt
new file mode 100644
index 0000000..732beae
--- /dev/null
+++ b/jongyee/Week7/app/src/main/java/com/example/week7/ui/theme/Theme.kt
@@ -0,0 +1,22 @@
+package com.example.week7.ui.theme
+
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.lightColorScheme
+import androidx.compose.runtime.Composable
+
+private val LightColorScheme = lightColorScheme(
+ primary = NikeBlack,
+ onPrimary = NikeWhite,
+ background = NikeWhite,
+ onBackground = NikeBlack,
+ surface = NikeWhite,
+ onSurface = NikeBlack,
+)
+
+@Composable
+fun Week7Theme(content: @Composable () -> Unit) {
+ MaterialTheme(
+ colorScheme = LightColorScheme,
+ content = content
+ )
+}
diff --git a/jongyee/Week7/app/src/main/java/com/example/week7/ui/wishlist/WishlistScreen.kt b/jongyee/Week7/app/src/main/java/com/example/week7/ui/wishlist/WishlistScreen.kt
new file mode 100644
index 0000000..395aa10
--- /dev/null
+++ b/jongyee/Week7/app/src/main/java/com/example/week7/ui/wishlist/WishlistScreen.kt
@@ -0,0 +1,31 @@
+package com.example.week7.ui.wishlist
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import com.example.week7.ui.theme.NikeBlack
+import com.example.week7.ui.theme.NikeWhite
+
+@Composable
+fun WishlistScreen() {
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .background(NikeWhite)
+ .padding(start = 24.dp, top = 78.dp, end = 24.dp)
+ ) {
+ Text(
+ text = "위시리스트",
+ color = NikeBlack,
+ fontSize = 24.sp,
+ fontWeight = FontWeight.Bold
+ )
+ }
+}
diff --git a/jongyee/Week7/app/src/main/res/drawable/ic_launcher_background.xml b/jongyee/Week7/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..4a213bb
--- /dev/null
+++ b/jongyee/Week7/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/jongyee/Week7/app/src/main/res/drawable/ic_launcher_foreground.xml b/jongyee/Week7/app/src/main/res/drawable/ic_launcher_foreground.xml
new file mode 100644
index 0000000..0a122fd
--- /dev/null
+++ b/jongyee/Week7/app/src/main/res/drawable/ic_launcher_foreground.xml
@@ -0,0 +1,177 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jongyee/Week7/app/src/main/res/drawable/image.png b/jongyee/Week7/app/src/main/res/drawable/image.png
new file mode 100644
index 0000000..f42aa9d
Binary files /dev/null and b/jongyee/Week7/app/src/main/res/drawable/image.png differ
diff --git a/jongyee/Week7/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/jongyee/Week7/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..6f3b755
--- /dev/null
+++ b/jongyee/Week7/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/jongyee/Week7/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/jongyee/Week7/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..6f3b755
--- /dev/null
+++ b/jongyee/Week7/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/jongyee/Week7/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/jongyee/Week7/app/src/main/res/mipmap-hdpi/ic_launcher.webp
new file mode 100644
index 0000000..c209e78
Binary files /dev/null and b/jongyee/Week7/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ
diff --git a/jongyee/Week7/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/jongyee/Week7/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..b2dfe3d
Binary files /dev/null and b/jongyee/Week7/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ
diff --git a/jongyee/Week7/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/jongyee/Week7/app/src/main/res/mipmap-mdpi/ic_launcher.webp
new file mode 100644
index 0000000..4f0f1d6
Binary files /dev/null and b/jongyee/Week7/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ
diff --git a/jongyee/Week7/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/jongyee/Week7/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..62b611d
Binary files /dev/null and b/jongyee/Week7/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ
diff --git a/jongyee/Week7/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/jongyee/Week7/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 0000000..948a307
Binary files /dev/null and b/jongyee/Week7/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ
diff --git a/jongyee/Week7/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/jongyee/Week7/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..1b9a695
Binary files /dev/null and b/jongyee/Week7/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ
diff --git a/jongyee/Week7/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/jongyee/Week7/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..28d4b77
Binary files /dev/null and b/jongyee/Week7/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ
diff --git a/jongyee/Week7/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/jongyee/Week7/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..9287f50
Binary files /dev/null and b/jongyee/Week7/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ
diff --git a/jongyee/Week7/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/jongyee/Week7/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..aa7d642
Binary files /dev/null and b/jongyee/Week7/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ
diff --git a/jongyee/Week7/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/jongyee/Week7/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..9126ae3
Binary files /dev/null and b/jongyee/Week7/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ
diff --git a/jongyee/Week7/app/src/main/res/values-night/themes.xml b/jongyee/Week7/app/src/main/res/values-night/themes.xml
new file mode 100644
index 0000000..3772963
--- /dev/null
+++ b/jongyee/Week7/app/src/main/res/values-night/themes.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/jongyee/Week7/app/src/main/res/values/colors.xml b/jongyee/Week7/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..768b058
--- /dev/null
+++ b/jongyee/Week7/app/src/main/res/values/colors.xml
@@ -0,0 +1,5 @@
+
+
+ #FF000000
+ #FFFFFFFF
+
diff --git a/jongyee/Week7/app/src/main/res/values/strings.xml b/jongyee/Week7/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..60db8bf
--- /dev/null
+++ b/jongyee/Week7/app/src/main/res/values/strings.xml
@@ -0,0 +1,9 @@
+
+
+ Week7
+ Discover
+ 9월 4일 목요일
+ Nike 추천 상품 이미지
+ 장바구니가 비어 있습니다.\n제품을 추가하면 여기에 표시됩니다.
+ 주문하기
+
diff --git a/jongyee/Week7/app/src/main/res/values/themes.xml b/jongyee/Week7/app/src/main/res/values/themes.xml
new file mode 100644
index 0000000..3772963
--- /dev/null
+++ b/jongyee/Week7/app/src/main/res/values/themes.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/jongyee/Week7/app/src/main/res/xml/backup_rules.xml b/jongyee/Week7/app/src/main/res/xml/backup_rules.xml
new file mode 100644
index 0000000..4df9255
--- /dev/null
+++ b/jongyee/Week7/app/src/main/res/xml/backup_rules.xml
@@ -0,0 +1,13 @@
+
+
+
+
\ No newline at end of file
diff --git a/jongyee/Week7/app/src/main/res/xml/data_extraction_rules.xml b/jongyee/Week7/app/src/main/res/xml/data_extraction_rules.xml
new file mode 100644
index 0000000..9ee9997
--- /dev/null
+++ b/jongyee/Week7/app/src/main/res/xml/data_extraction_rules.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/jongyee/Week7/app/src/test/java/com/example/week7/ExampleUnitTest.kt b/jongyee/Week7/app/src/test/java/com/example/week7/ExampleUnitTest.kt
new file mode 100644
index 0000000..9939568
--- /dev/null
+++ b/jongyee/Week7/app/src/test/java/com/example/week7/ExampleUnitTest.kt
@@ -0,0 +1,11 @@
+package com.example.week7
+
+import org.junit.Test
+import org.junit.Assert.*
+
+class ExampleUnitTest {
+ @Test
+ fun addition_isCorrect() {
+ assertEquals(4, 2 + 2)
+ }
+}
diff --git a/jongyee/Week7/build.gradle.kts b/jongyee/Week7/build.gradle.kts
new file mode 100644
index 0000000..132ad8d
--- /dev/null
+++ b/jongyee/Week7/build.gradle.kts
@@ -0,0 +1,6 @@
+plugins {
+ alias(libs.plugins.android.application) apply false
+ alias(libs.plugins.kotlin.android) apply false
+ alias(libs.plugins.kotlin.compose) apply false
+ alias(libs.plugins.kotlin.serialization) apply false
+}
diff --git a/jongyee/Week7/gradle.properties b/jongyee/Week7/gradle.properties
new file mode 100644
index 0000000..5190ee8
--- /dev/null
+++ b/jongyee/Week7/gradle.properties
@@ -0,0 +1,3 @@
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+kotlin.code.style=official
+android.useAndroidX=true
diff --git a/jongyee/Week7/gradle/libs.versions.toml b/jongyee/Week7/gradle/libs.versions.toml
new file mode 100644
index 0000000..1c70847
--- /dev/null
+++ b/jongyee/Week7/gradle/libs.versions.toml
@@ -0,0 +1,41 @@
+[versions]
+agp = "8.9.1"
+kotlin = "2.1.0"
+coreKtx = "1.18.0"
+junit = "4.13.2"
+junitVersion = "1.3.0"
+espressoCore = "3.7.0"
+appcompat = "1.7.1"
+activity = "1.10.1"
+composeBom = "2024.12.01"
+navigationCompose = "2.8.9"
+kotlinxSerializationJson = "1.7.3"
+
+[libraries]
+androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
+junit = { group = "junit", name = "junit", version.ref = "junit" }
+androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
+androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
+androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
+androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activity" }
+
+# Compose BOM
+androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
+androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" }
+androidx-compose-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
+androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
+androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }
+androidx-compose-material-icons-extended = { group = "androidx.compose.material", name = "material-icons-extended" }
+androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
+androidx-compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
+androidx-compose-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
+
+# Navigation
+androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" }
+kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }
+
+[plugins]
+android-application = { id = "com.android.application", version.ref = "agp" }
+kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
+kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
+kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
diff --git a/jongyee/Week7/gradle/wrapper/gradle-wrapper.jar b/jongyee/Week7/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..8bdaf60
Binary files /dev/null and b/jongyee/Week7/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/jongyee/Week7/gradle/wrapper/gradle-wrapper.properties b/jongyee/Week7/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..318e0f5
--- /dev/null
+++ b/jongyee/Week7/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,9 @@
+#Sat Mar 28 18:15:36 KST 2026
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionSha256Sum=b266d5ff6b90eada6dc3b20cb090e3731302e553a27c5d3e4df1f0d76beaff06
+distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip
+networkTimeout=10000
+validateDistributionUrl=true
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/jongyee/Week7/gradlew b/jongyee/Week7/gradlew
new file mode 100644
index 0000000..ef07e01
--- /dev/null
+++ b/jongyee/Week7/gradlew
@@ -0,0 +1,251 @@
+#!/bin/sh
+
+#
+# Copyright © 2015 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+##############################################################################
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
+done
+
+# This is normally unused
+# shellcheck disable=SC2034
+APP_BASE_NAME=${0##*/}
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+ echo "$*"
+} >&2
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
+esac
+
+CLASSPATH="\\\"\\\""
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD=$JAVA_HOME/jre/sh/java
+ else
+ JAVACMD=$JAVA_HOME/bin/java
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD=java
+ if ! command -v java >/dev/null 2>&1
+ then
+ die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
+ fi
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
+ done
+fi
+
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Collect all arguments for the java command:
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+# and any embedded shellness will be escaped.
+# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
+# treated as '${Hostname}' itself on the command line.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
+ "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+ die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/jongyee/Week7/gradlew.bat b/jongyee/Week7/gradlew.bat
new file mode 100644
index 0000000..db3a6ac
--- /dev/null
+++ b/jongyee/Week7/gradlew.bat
@@ -0,0 +1,94 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+@rem SPDX-License-Identifier: Apache-2.0
+@rem
+
+@if "%DEBUG%"=="" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if %ERRORLEVEL% equ 0 goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if %ERRORLEVEL% equ 0 goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/jongyee/Week7/settings.gradle.kts b/jongyee/Week7/settings.gradle.kts
new file mode 100644
index 0000000..ec381b5
--- /dev/null
+++ b/jongyee/Week7/settings.gradle.kts
@@ -0,0 +1,26 @@
+pluginManagement {
+ repositories {
+ google {
+ content {
+ includeGroupByRegex("com\\.android.*")
+ includeGroupByRegex("com\\.google.*")
+ includeGroupByRegex("androidx.*")
+ }
+ }
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
+plugins {
+ id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
+}
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+rootProject.name = "Week7"
+include(":app")