From f6b3e909fe399466b7a43ee3ae5de28339d6e4a3 Mon Sep 17 00:00:00 2001 From: comst19 Date: Sun, 17 Mar 2024 23:51:14 +0900 Subject: [PATCH 001/301] =?UTF-8?q?feat:=20FCM=20=ED=99=95=EC=9E=A5,?= =?UTF-8?q?=EC=B6=95=EC=86=8C=EC=8B=9C=EC=97=90=20=EC=9D=B4=EB=AF=B8?= =?UTF-8?q?=EC=A7=80=20=EC=A1=B0=EC=A0=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../droidblossom/archive/util/MyFirebaseMessagingService.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/MyFirebaseMessagingService.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/MyFirebaseMessagingService.kt index c8cba0ea9..d301b0cb6 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/MyFirebaseMessagingService.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/MyFirebaseMessagingService.kt @@ -134,7 +134,11 @@ class MyFirebaseMessagingService : FirebaseMessagingService() { setContentIntent(pendingIntent) bigPicture?.let { - setStyle(NotificationCompat.BigPictureStyle().bigPicture(it)) + setStyle(NotificationCompat.BigPictureStyle() + .bigPicture(it) + .bigLargeIcon(null as Bitmap?)) + + setLargeIcon(it) } } From f29b9bab01215e94c1ec5367092a6e54e2a9e002 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Mon, 18 Mar 2024 11:38:37 +0900 Subject: [PATCH 002/301] =?UTF-8?q?feat:=20init=20=EC=84=B8=ED=8C=85?= =?UTF-8?q?=EC=97=91=ED=8B=B0=EB=B9=84=ED=8B=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ARchive/app/src/main/AndroidManifest.xml | 16 +++++++++------- .../ui/mypage/setting/SettingActivity.kt | 12 ++++++++++++ .../src/main/res/layout/activity_setting.xml | 18 ++++++++++++++++++ 3 files changed, 39 insertions(+), 7 deletions(-) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingActivity.kt create mode 100644 frontend/ARchive/app/src/main/res/layout/activity_setting.xml diff --git a/frontend/ARchive/app/src/main/AndroidManifest.xml b/frontend/ARchive/app/src/main/AndroidManifest.xml index 0522b7c76..73361fe78 100644 --- a/frontend/ARchive/app/src/main/AndroidManifest.xml +++ b/frontend/ARchive/app/src/main/AndroidManifest.xml @@ -32,6 +32,9 @@ android:theme="@style/Theme.ARchive" android:usesCleartextTraffic="true" tools:targetApi="31"> + @@ -82,10 +85,9 @@ - + tools:ignore="LockedOrientationActivity"> - - + @@ -138,7 +141,6 @@ - \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingActivity.kt new file mode 100644 index 000000000..f093673fc --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingActivity.kt @@ -0,0 +1,12 @@ +package com.droidblossom.archive.presentation.ui.mypage.setting + +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import com.droidblossom.archive.R + +class SettingActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_setting) + } +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/activity_setting.xml b/frontend/ARchive/app/src/main/res/layout/activity_setting.xml new file mode 100644 index 000000000..02fd02af3 --- /dev/null +++ b/frontend/ARchive/app/src/main/res/layout/activity_setting.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file From cd4ea74d5530642f45d6bd3603e79fcbbcc4a3b0 Mon Sep 17 00:00:00 2001 From: comst19 Date: Mon, 18 Mar 2024 11:59:11 +0900 Subject: [PATCH 003/301] =?UTF-8?q?chore:=20snack=20->=20customview=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/presentation/base/BaseActivity.kt | 4 +--- .../presentation/{snack => customview}/CallSnackBar.kt | 2 +- .../{snack => customview}/HomeSnackBarBig.kt | 2 +- .../{snack => customview}/HomeSnackBarSmall.kt | 2 +- .../archive/presentation/ui/home/HomeFragment.kt | 10 ++-------- .../archive/util/MyFirebaseMessagingService.kt | 1 - 6 files changed, 6 insertions(+), 15 deletions(-) rename frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/{snack => customview}/CallSnackBar.kt (60%) rename frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/{snack => customview}/HomeSnackBarBig.kt (96%) rename frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/{snack => customview}/HomeSnackBarSmall.kt (96%) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseActivity.kt index 48c9b33ba..a38df1744 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseActivity.kt @@ -1,6 +1,5 @@ package com.droidblossom.archive.presentation.base -import android.content.Intent import android.graphics.Color import android.os.Bundle import android.util.Log @@ -11,8 +10,7 @@ import androidx.annotation.LayoutRes import androidx.appcompat.app.AppCompatActivity import androidx.databinding.DataBindingUtil import androidx.databinding.ViewDataBinding -import com.droidblossom.archive.presentation.snack.CallSnackBar -import com.droidblossom.archive.presentation.snack.HomeSnackBarSmall +import com.droidblossom.archive.presentation.customview.HomeSnackBarSmall import kotlinx.coroutines.Job import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/snack/CallSnackBar.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/CallSnackBar.kt similarity index 60% rename from frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/snack/CallSnackBar.kt rename to frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/CallSnackBar.kt index bc2ce7aed..90cc3a8d3 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/snack/CallSnackBar.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/CallSnackBar.kt @@ -1,4 +1,4 @@ -package com.droidblossom.archive.presentation.snack +package com.droidblossom.archive.presentation.customview data class CallSnackBar( // 타입, 아이디 등등 추가 diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/snack/HomeSnackBarBig.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/HomeSnackBarBig.kt similarity index 96% rename from frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/snack/HomeSnackBarBig.kt rename to frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/HomeSnackBarBig.kt index 8160f3c98..b756f77a5 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/snack/HomeSnackBarBig.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/HomeSnackBarBig.kt @@ -1,4 +1,4 @@ -package com.droidblossom.archive.presentation.snack +package com.droidblossom.archive.presentation.customview import android.view.LayoutInflater import android.view.View diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/snack/HomeSnackBarSmall.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/HomeSnackBarSmall.kt similarity index 96% rename from frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/snack/HomeSnackBarSmall.kt rename to frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/HomeSnackBarSmall.kt index e3d76c0f9..01033771c 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/snack/HomeSnackBarSmall.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/HomeSnackBarSmall.kt @@ -1,4 +1,4 @@ -package com.droidblossom.archive.presentation.snack +package com.droidblossom.archive.presentation.customview import android.view.LayoutInflater diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt index bb3a57d0a..d93fe65eb 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt @@ -3,10 +3,8 @@ package com.droidblossom.archive.presentation.ui.home import android.graphics.Point import android.location.Location import android.os.Bundle -import android.util.Log import android.view.View import android.view.ViewGroup -import androidx.core.content.ContentProviderCompat.requireContext import androidx.core.content.ContextCompat import androidx.core.graphics.toPointF import androidx.fragment.app.viewModels @@ -17,8 +15,8 @@ import com.droidblossom.archive.R import com.droidblossom.archive.databinding.FragmentHomeBinding import com.droidblossom.archive.domain.model.common.CapsuleMarker import com.droidblossom.archive.presentation.base.BaseFragment -import com.droidblossom.archive.presentation.snack.HomeSnackBarBig -import com.droidblossom.archive.presentation.snack.HomeSnackBarSmall +import com.droidblossom.archive.presentation.customview.HomeSnackBarBig +import com.droidblossom.archive.presentation.customview.HomeSnackBarSmall import com.droidblossom.archive.presentation.ui.home.createcapsule.CreateCapsuleActivity import com.droidblossom.archive.presentation.ui.home.dialog.CapsulePreviewDialogFragment import com.droidblossom.archive.util.CapsuleTypeUtils @@ -35,11 +33,7 @@ import com.naver.maps.map.overlay.Overlay import com.naver.maps.map.overlay.OverlayImage import com.naver.maps.map.util.FusedLocationSource import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import kotlin.random.Random @AndroidEntryPoint class HomeFragment : BaseFragment(R.layout.fragment_home), diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/MyFirebaseMessagingService.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/MyFirebaseMessagingService.kt index d301b0cb6..926f35fc9 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/MyFirebaseMessagingService.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/MyFirebaseMessagingService.kt @@ -12,7 +12,6 @@ import android.os.Build import android.util.Log import androidx.core.app.NotificationCompat import com.droidblossom.archive.R -import com.droidblossom.archive.presentation.snack.CallSnackBar import com.droidblossom.archive.presentation.ui.MainActivity import com.google.firebase.messaging.FirebaseMessaging import com.google.firebase.messaging.FirebaseMessagingService From 8f19a2b6b239b662f87d20bb242d22cf0d77854e Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Mon, 18 Mar 2024 12:00:13 +0900 Subject: [PATCH 004/301] =?UTF-8?q?feat:=20=EC=84=B8=ED=8C=85=20=EC=97=91?= =?UTF-8?q?=ED=8B=B0=EB=B9=84=ED=8B=B0=20viewModel.=20databinding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/mypage/setting/SettingActivity.kt | 20 +++++++++-- .../ui/mypage/setting/SettingViewModel.kt | 4 +++ .../ui/mypage/setting/SettingViewModelImpl.kt | 12 +++++++ .../src/main/res/layout/activity_setting.xml | 35 +++++++++++-------- 4 files changed, 55 insertions(+), 16 deletions(-) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingViewModel.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingViewModelImpl.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingActivity.kt index f093673fc..260e64284 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingActivity.kt @@ -2,11 +2,27 @@ package com.droidblossom.archive.presentation.ui.mypage.setting import androidx.appcompat.app.AppCompatActivity import android.os.Bundle +import androidx.activity.viewModels +import androidx.databinding.DataBindingUtil.setContentView import com.droidblossom.archive.R +import com.droidblossom.archive.databinding.ActivitySettingBinding +import com.droidblossom.archive.databinding.FragmentMyPageBinding +import com.droidblossom.archive.presentation.base.BaseActivity +import com.droidblossom.archive.presentation.base.BaseFragment +import com.droidblossom.archive.presentation.ui.mypage.MyPageViewModelImpl +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class SettingActivity : BaseActivity(R.layout.activity_setting) { + override val viewModel: SettingViewModelImpl by viewModels() + override fun observeData() {} -class SettingActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_setting) + initView() + } + + private fun initView(){ + } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingViewModel.kt new file mode 100644 index 000000000..f04ba6a31 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingViewModel.kt @@ -0,0 +1,4 @@ +package com.droidblossom.archive.presentation.ui.mypage.setting + +interface SettingViewModel { +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingViewModelImpl.kt new file mode 100644 index 000000000..2e84b0682 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingViewModelImpl.kt @@ -0,0 +1,12 @@ +package com.droidblossom.archive.presentation.ui.mypage.setting + +import com.droidblossom.archive.presentation.base.BaseViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject + +@HiltViewModel +class SettingViewModelImpl @Inject constructor( + +) : BaseViewModel(), SettingViewModel { + +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/activity_setting.xml b/frontend/ARchive/app/src/main/res/layout/activity_setting.xml index 02fd02af3..619524a08 100644 --- a/frontend/ARchive/app/src/main/res/layout/activity_setting.xml +++ b/frontend/ARchive/app/src/main/res/layout/activity_setting.xml @@ -1,18 +1,25 @@ - + xmlns:tools="http://schemas.android.com/tools"> - + - \ No newline at end of file + + + + + + + + \ No newline at end of file From dfbdf7bba264c753cce1ced9d1fb23ca62bf24f3 Mon Sep 17 00:00:00 2001 From: comst19 Date: Mon, 18 Mar 2024 15:06:51 +0900 Subject: [PATCH 005/301] =?UTF-8?q?feat:=20=EB=A1=9C=EB=94=A9=20=EB=8B=A4?= =?UTF-8?q?=EC=9D=B4=EC=96=BC=EB=A1=9C=EA=B7=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/customview/LoadingDialog.kt | 29 +++++++++ .../main/res/drawable/app_symbol_loading.xml | 60 +++++++++++++++++++ .../src/main/res/layout/dialog_loading.xml | 31 ++++++++++ 3 files changed, 120 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/LoadingDialog.kt create mode 100644 frontend/ARchive/app/src/main/res/drawable/app_symbol_loading.xml create mode 100644 frontend/ARchive/app/src/main/res/layout/dialog_loading.xml diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/LoadingDialog.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/LoadingDialog.kt new file mode 100644 index 000000000..621c8297f --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/LoadingDialog.kt @@ -0,0 +1,29 @@ +package com.droidblossom.archive.presentation.customview + +import android.app.Dialog +import android.content.Context +import android.graphics.drawable.ColorDrawable +import android.os.Bundle +import android.view.Window +import com.droidblossom.archive.databinding.DialogLoadingBinding + +class LoadingDialog(context : Context) : Dialog(context) { + + private lateinit var binding : DialogLoadingBinding + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + requestWindowFeature(Window.FEATURE_NO_TITLE) + binding = DialogLoadingBinding.inflate(layoutInflater) + setContentView(binding.root) + setCanceledOnTouchOutside(false) + setCancelable(false) + window!!.setBackgroundDrawable(ColorDrawable()) + window!!.setDimAmount(0.5f) + + } + + override fun show(){ + if(!this.isShowing) super.show() + } +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/drawable/app_symbol_loading.xml b/frontend/ARchive/app/src/main/res/drawable/app_symbol_loading.xml new file mode 100644 index 000000000..4da037232 --- /dev/null +++ b/frontend/ARchive/app/src/main/res/drawable/app_symbol_loading.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/ARchive/app/src/main/res/layout/dialog_loading.xml b/frontend/ARchive/app/src/main/res/layout/dialog_loading.xml new file mode 100644 index 000000000..4eebd6534 --- /dev/null +++ b/frontend/ARchive/app/src/main/res/layout/dialog_loading.xml @@ -0,0 +1,31 @@ + + + + + + + + \ No newline at end of file From f7f13211d004a3bb3324df7a8de8b663646976b2 Mon Sep 17 00:00:00 2001 From: comst19 Date: Mon, 18 Mar 2024 15:07:24 +0900 Subject: [PATCH 006/301] =?UTF-8?q?feat:=20BaseActivity,=20BaseFragment?= =?UTF-8?q?=EC=97=90=20=EB=A1=9C=EB=94=A9=20=EB=8B=A4=EC=9D=B4=EC=96=BC?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=20on/off=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/presentation/base/BaseActivity.kt | 24 +++++++++++++++++++ .../archive/presentation/base/BaseFragment.kt | 21 ++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseActivity.kt index a38df1744..811664803 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseActivity.kt @@ -1,5 +1,6 @@ package com.droidblossom.archive.presentation.base +import android.content.Context import android.graphics.Color import android.os.Bundle import android.util.Log @@ -11,6 +12,7 @@ import androidx.appcompat.app.AppCompatActivity import androidx.databinding.DataBindingUtil import androidx.databinding.ViewDataBinding import com.droidblossom.archive.presentation.customview.HomeSnackBarSmall +import com.droidblossom.archive.presentation.customview.LoadingDialog import kotlinx.coroutines.Job import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe @@ -24,6 +26,9 @@ abstract class BaseActivity(@LayoutRes v private var _binding: V? = null protected val binding: V get() = _binding!! + private lateinit var loadingDialog: LoadingDialog + private var loadingState = false + abstract fun observeData() protected fun getStatusBarHeight(): Int { @@ -64,11 +69,30 @@ abstract class BaseActivity(@LayoutRes v } } + + + fun showLoading(context: Context) { + if (!loadingState) { + loadingDialog = LoadingDialog(context) + loadingDialog.show() + loadingState = true + } + } + + + fun dismissLoading() { + if (loadingState) { + loadingDialog.dismiss() + loadingState = false + } + } + override fun onDestroy() { fetchJob?.let { if (it.isActive) { it.cancel() } + dismissLoading() } super.onDestroy() } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseFragment.kt index 04aaa7a91..c84e25b88 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseFragment.kt @@ -1,5 +1,6 @@ package com.droidblossom.archive.presentation.base +import android.content.Context import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -9,6 +10,7 @@ import androidx.annotation.LayoutRes import androidx.databinding.DataBindingUtil import androidx.databinding.ViewDataBinding import androidx.fragment.app.Fragment +import com.droidblossom.archive.presentation.customview.LoadingDialog import kotlinx.coroutines.Job abstract class BaseFragment(@LayoutRes val layoutResource :Int): Fragment() { @@ -19,6 +21,9 @@ abstract class BaseFragment(@LayoutRes va private var _binding: V? = null protected val binding : V get() = _binding!! + private lateinit var loadingDialog: LoadingDialog + private var loadingState = false + protected fun getStatusBarHeight(): Int { val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android") return if (resourceId > 0) resources.getDimensionPixelSize(resourceId) else 0 @@ -53,11 +58,27 @@ abstract class BaseFragment(@LayoutRes va toast.show() } + fun showLoading(context: Context) { + if (!loadingState) { + loadingDialog = LoadingDialog(context) + loadingDialog.show() + loadingState = true + } + } + + fun dismissLoading() { + if (loadingState) { + loadingDialog.dismiss() + loadingState = false + } + } + override fun onDestroyView() { super.onDestroyView() if (fetchJob.isActive) { fetchJob.cancel() } + dismissLoading() _binding = null } } \ No newline at end of file From 4d015e44dc89cff439393dff9343cbe870891cda Mon Sep 17 00:00:00 2001 From: comst19 Date: Mon, 18 Mar 2024 15:07:51 +0900 Subject: [PATCH 007/301] =?UTF-8?q?feat:=20=EC=BA=A1=EC=8A=90=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=EC=97=90=20=EB=A1=9C=EB=94=A9=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/home/createcapsule/CreateCapsule3Fragment.kt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsule3Fragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsule3Fragment.kt index 38575aa28..b5c348140 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsule3Fragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsule3Fragment.kt @@ -120,6 +120,15 @@ class CreateCapsule3Fragment : nextBtn.setOnClickListener { + if (viewModel.isSelectTimeCapsule.value && (viewModel.capsuleLatitude.value == 0.0 || viewModel.capsuleTitle.value.isEmpty() || viewModel.capsuleContent.value.isEmpty() || viewModel.dueTime.value.isEmpty())) { + showToastMessage("타임캡슐은 시간, 제목, 내용이 필수 입니다.") + return@setOnClickListener + } + if (!viewModel.isSelectTimeCapsule.value && (viewModel.capsuleLatitude.value == 0.0 || viewModel.capsuleTitle.value.isEmpty() || viewModel.capsuleContent.value.isEmpty())) { + showToastMessage("캡슐은 제목, 내용이 필수 입니다.") + return@setOnClickListener + } + showLoading(requireContext()) val dateFormat = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()) val currentTime = dateFormat.format(Date()) CoroutineScope(Dispatchers.IO).launch { From 647aeb7c0c0284053d5d712834b0d0887d341327 Mon Sep 17 00:00:00 2001 From: comst19 Date: Mon, 18 Mar 2024 15:08:05 +0900 Subject: [PATCH 008/301] =?UTF-8?q?feat:=20=EC=8A=A4=ED=82=A8=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=EC=97=90=20=EB=A1=9C=EB=94=A9=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/presentation/ui/skin/skinmake/SkinMakeFragment.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeFragment.kt index 53edee794..990f46953 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeFragment.kt @@ -101,6 +101,8 @@ class SkinMakeFragment : BaseFragment Date: Tue, 19 Mar 2024 00:00:56 +0900 Subject: [PATCH 009/301] =?UTF-8?q?feat=20:=20=EC=84=B8=ED=8C=85=20?= =?UTF-8?q?=EB=A9=94=EC=9D=B8=20=ED=94=84=EB=A0=88=EA=B7=B8=EB=A8=BC?= =?UTF-8?q?=ED=8A=B8=20init?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/mypage/setting/SettingMainFragment.kt | 12 ++++++++++++ .../src/main/res/layout/fragment_setting_main.xml | 13 +++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt create mode 100644 frontend/ARchive/app/src/main/res/layout/fragment_setting_main.xml diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt new file mode 100644 index 000000000..f9778b778 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt @@ -0,0 +1,12 @@ +package com.droidblossom.archive.presentation.ui.mypage.setting + +import androidx.fragment.app.viewModels +import com.droidblossom.archive.R +import com.droidblossom.archive.databinding.FragmentSettingMainBinding +import com.droidblossom.archive.presentation.base.BaseFragment + +class SettingMainFragment : BaseFragment(R.layout.fragment_setting_main) { + override val viewModel: SettingViewModelImpl by viewModels() + + override fun observeData() {} +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_setting_main.xml b/frontend/ARchive/app/src/main/res/layout/fragment_setting_main.xml new file mode 100644 index 000000000..67ed5d4c8 --- /dev/null +++ b/frontend/ARchive/app/src/main/res/layout/fragment_setting_main.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + \ No newline at end of file From 71393c76143560b86241788ea485a5e2e2fe2b77 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Tue, 19 Mar 2024 02:17:56 +0900 Subject: [PATCH 010/301] =?UTF-8?q?design:=20=EC=84=B8=ED=8C=85=EB=A9=94?= =?UTF-8?q?=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/res/layout/fragment_setting_main.xml | 390 +++++++++++++++++- 1 file changed, 389 insertions(+), 1 deletion(-) diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_setting_main.xml b/frontend/ARchive/app/src/main/res/layout/fragment_setting_main.xml index 67ed5d4c8..2dfb9c9cc 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_setting_main.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_setting_main.xml @@ -1,5 +1,6 @@ - + @@ -9,5 +10,392 @@ android:layout_width="match_parent" android:layout_height="match_parent"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From a2848c7ffa6d27af02f685702067b526604ff12b Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Tue, 19 Mar 2024 02:41:33 +0900 Subject: [PATCH 011/301] =?UTF-8?q?faet=20:=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EB=84=A4=EB=B9=84=20&=20=EC=84=A4=EC=A0=95=20=EC=97=B0?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../createcapsule/CreateCapsuleActivity.kt | 3 -- .../presentation/ui/mypage/MyPageFragment.kt | 10 +++++-- .../presentation/ui/mypage/MyPageViewModel.kt | 8 +++--- .../ui/mypage/MyPageViewModelImpl.kt | 7 +++++ .../ui/mypage/setting/SettingActivity.kt | 19 +++++++++++-- .../ui/mypage/setting/SettingMainFragment.kt | 3 +- .../util/MyFirebaseMessagingService.kt | 4 ++- .../main/res/drawable/ic_left_arrow_24.xml | 18 ++++++++++++ .../src/main/res/layout/activity_setting.xml | 6 +++- .../src/main/res/layout/fragment_my_page.xml | 17 +++++------ .../main/res/layout/fragment_setting_main.xml | 28 +++++++++++++------ .../main/res/navigation/nav_setting_graph.xml | 13 +++++++++ 12 files changed, 104 insertions(+), 32 deletions(-) create mode 100644 frontend/ARchive/app/src/main/res/drawable/ic_left_arrow_24.xml create mode 100644 frontend/ARchive/app/src/main/res/navigation/nav_setting_graph.xml diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleActivity.kt index 35b368f2a..83608cf76 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleActivity.kt @@ -6,9 +6,6 @@ import android.os.Bundle import android.util.Log import android.view.ViewGroup import androidx.activity.viewModels -import androidx.appcompat.app.AppCompatActivity -import androidx.core.content.ContentProviderCompat.requireContext -import androidx.navigation.findNavController import com.droidblossom.archive.R import com.droidblossom.archive.databinding.ActivityCreateCapsuleBinding import com.droidblossom.archive.presentation.base.BaseActivity diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt index 78a6d665c..b76ba07c3 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt @@ -25,6 +25,7 @@ import com.droidblossom.archive.presentation.ui.home.HomeFragment import com.droidblossom.archive.presentation.ui.home.createcapsule.adapter.SkinRVA import com.droidblossom.archive.presentation.ui.home.dialog.CapsulePreviewDialogFragment import com.droidblossom.archive.presentation.ui.mypage.adapter.MyCapsuleRVA +import com.droidblossom.archive.presentation.ui.mypage.setting.SettingActivity import com.droidblossom.archive.presentation.ui.skin.SkinFragment import com.droidblossom.archive.util.DateUtils import dagger.hilt.android.AndroidEntryPoint @@ -67,9 +68,9 @@ class MyPageFragment : initRVA() - binding.settingBtn.setOnClickListener { - throw RuntimeException("Test Crash") - } +// binding.settingBtn.setOnClickListener { +// throw RuntimeException("Test Crash") +// } val layoutParams = binding.profileImg.layoutParams as ViewGroup.MarginLayoutParams layoutParams.topMargin += getStatusBarHeight() @@ -121,6 +122,9 @@ class MyPageFragment : is MyPageViewModel.MyPageEvent.ShowToastMessage -> { showToastMessage(event.message) } + is MyPageViewModel.MyPageEvent.ClickSetting -> { + startActivity(SettingActivity.newIntent(requireContext())) + } else -> {} } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModel.kt index b459cd493..fb7dfbc6b 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModel.kt @@ -2,8 +2,6 @@ package com.droidblossom.archive.presentation.ui.mypage import com.droidblossom.archive.domain.model.common.MyCapsule import com.droidblossom.archive.domain.model.member.MemberDetail -import com.droidblossom.archive.presentation.base.BaseViewModel -import com.droidblossom.archive.presentation.ui.home.createcapsule.CreateCapsuleViewModel import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow @@ -19,12 +17,14 @@ interface MyPageViewModel { fun getMe() fun getSecretCapsulePage() fun clearCapsules() - fun updateMyCapsulesUI() - fun updateCapsuleOpenState(capsuleId: Long, isOpened: Boolean) + fun clickSetting() sealed class MyPageEvent { data class ShowToastMessage(val message : String) : MyPageEvent() + + object ClickSetting : MyPageEvent() + } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt index 0bddd9a64..8d17150fe 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt @@ -88,6 +88,7 @@ class MyPageViewModelImpl @Inject constructor( } } + override fun updateMyCapsulesUI() { viewModelScope.launch { _myCapsulesUI.emit(myCapsules.value) @@ -117,4 +118,10 @@ class MyPageViewModelImpl @Inject constructor( _myCapsules.emit(updatedCapsules) } } + + override fun clickSetting() { + viewModelScope.launch { + _myPageEvents.emit(MyPageViewModel.MyPageEvent.ClickSetting) + } + } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingActivity.kt index 260e64284..64d6b3ea7 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingActivity.kt @@ -1,7 +1,10 @@ package com.droidblossom.archive.presentation.ui.mypage.setting +import android.content.Context +import android.content.Intent import androidx.appcompat.app.AppCompatActivity import android.os.Bundle +import android.view.ViewGroup import androidx.activity.viewModels import androidx.databinding.DataBindingUtil.setContentView import com.droidblossom.archive.R @@ -9,11 +12,14 @@ import com.droidblossom.archive.databinding.ActivitySettingBinding import com.droidblossom.archive.databinding.FragmentMyPageBinding import com.droidblossom.archive.presentation.base.BaseActivity import com.droidblossom.archive.presentation.base.BaseFragment +import com.droidblossom.archive.presentation.ui.home.createcapsule.CreateCapsuleActivity +import com.droidblossom.archive.presentation.ui.home.createcapsule.CreateCapsuleActivity.Companion.CREATE_CAPSULE import com.droidblossom.archive.presentation.ui.mypage.MyPageViewModelImpl import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint -class SettingActivity : BaseActivity(R.layout.activity_setting) { +class SettingActivity : + BaseActivity(R.layout.activity_setting) { override val viewModel: SettingViewModelImpl by viewModels() override fun observeData() {} @@ -22,7 +28,16 @@ class SettingActivity : BaseActivity(R.layout.fragment_setting_main) { +class SettingMainFragment : + BaseFragment(R.layout.fragment_setting_main) { override val viewModel: SettingViewModelImpl by viewModels() override fun observeData() {} diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/MyFirebaseMessagingService.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/MyFirebaseMessagingService.kt index d301b0cb6..e456926b5 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/MyFirebaseMessagingService.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/MyFirebaseMessagingService.kt @@ -124,7 +124,9 @@ class MyFirebaseMessagingService : FirebaseMessagingService() { PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE ) - val notificationBuilder = NotificationCompat.Builder(this@MyFirebaseMessagingService, channelId).apply { + val notificationBuilder = NotificationCompat.Builder( + this@MyFirebaseMessagingService, channelId + ).apply { priority = NotificationCompat.PRIORITY_HIGH setSmallIcon(R.drawable.app_symbol) setContentTitle(remoteMessage.data["title"]) diff --git a/frontend/ARchive/app/src/main/res/drawable/ic_left_arrow_24.xml b/frontend/ARchive/app/src/main/res/drawable/ic_left_arrow_24.xml new file mode 100644 index 000000000..0fc9dba20 --- /dev/null +++ b/frontend/ARchive/app/src/main/res/drawable/ic_left_arrow_24.xml @@ -0,0 +1,18 @@ + + + + diff --git a/frontend/ARchive/app/src/main/res/layout/activity_setting.xml b/frontend/ARchive/app/src/main/res/layout/activity_setting.xml index 619524a08..6030ab5d5 100644 --- a/frontend/ARchive/app/src/main/res/layout/activity_setting.xml +++ b/frontend/ARchive/app/src/main/res/layout/activity_setting.xml @@ -10,16 +10,20 @@ + app:layout_constraintTop_toTopOf="parent" + app:navGraph="@navigation/nav_setting_graph" /> \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_my_page.xml b/frontend/ARchive/app/src/main/res/layout/fragment_my_page.xml index bbbcfefad..615d74ae2 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_my_page.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_my_page.xml @@ -1,8 +1,8 @@ + xmlns:bind="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools"> @@ -34,12 +34,12 @@ android:id="@+id/profileImg" android:layout_width="44dp" android:layout_height="44dp" - bind:baseImg="@{@drawable/base_use_img}" - bind:url="@{vm.myInfo.profileUrl}" android:layout_marginStart="16dp" android:layout_marginTop="8dp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" + bind:baseImg="@{@drawable/base_use_img}" + bind:url="@{vm.myInfo.profileUrl}" tools:src="@color/main_2" /> + app:layout_constraintTop_toBottomOf="@id/settingBtn" + tools:listitem="@layout/item_story" /> + + + app:layout_constraintTop_toBottomOf="@id/backBtn"> @@ -137,7 +148,7 @@ android:id="@+id/noticeLayout" android:layout_width="0dp" android:layout_height="60dp" - android:layout_marginHorizontal="16dp" + android:layout_marginHorizontal="8dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/notiLayout"> @@ -191,7 +202,7 @@ android:id="@+id/agreeLayout" android:layout_width="0dp" android:layout_height="60dp" - android:layout_marginHorizontal="16dp" + android:layout_marginHorizontal="8dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/noticeLayout"> @@ -245,7 +256,7 @@ android:id="@+id/inquiryLayout" android:layout_width="0dp" android:layout_height="60dp" - android:layout_marginHorizontal="16dp" + android:layout_marginHorizontal="8dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/agreeLayout"> @@ -299,7 +310,7 @@ android:id="@+id/licenseLayout" android:layout_width="0dp" android:layout_height="60dp" - android:layout_marginHorizontal="16dp" + android:layout_marginHorizontal="8dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/inquiryLayout"> @@ -353,7 +364,7 @@ android:id="@+id/logoutLayout" android:layout_width="0dp" android:layout_height="60dp" - android:layout_marginHorizontal="16dp" + android:layout_marginHorizontal="8dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/licenseLayout"> @@ -385,7 +396,6 @@ - + + + + \ No newline at end of file From d78d81d7cd9dbdc0d1cc0840cd305f67a8a2399a Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Tue, 19 Mar 2024 14:14:02 +0900 Subject: [PATCH 012/301] =?UTF-8?q?chore:=20oss-licenses=20=ED=94=8C?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/ARchive/app/build.gradle | 4 ++++ frontend/ARchive/build.gradle | 1 + 2 files changed, 5 insertions(+) diff --git a/frontend/ARchive/app/build.gradle b/frontend/ARchive/app/build.gradle index c0bc7028d..29f6b1fe8 100644 --- a/frontend/ARchive/app/build.gradle +++ b/frontend/ARchive/app/build.gradle @@ -6,6 +6,7 @@ plugins { id 'dagger.hilt.android.plugin' id 'com.google.gms.google-services' id 'com.google.firebase.crashlytics' + id 'com.google.android.gms.oss-licenses-plugin' } Properties properties = new Properties() @@ -178,6 +179,9 @@ dependencies { // Event Bus : https://github.com/greenrobot/EventBus implementation("org.greenrobot:eventbus:3.3.1") + + // oss-licenses + implementation 'com.google.android.gms:play-services-oss-licenses:17.0.1' } kapt { correctErrorTypes true diff --git a/frontend/ARchive/build.gradle b/frontend/ARchive/build.gradle index 641cb29ab..b2ad3fa8f 100644 --- a/frontend/ARchive/build.gradle +++ b/frontend/ARchive/build.gradle @@ -1,6 +1,7 @@ buildscript { dependencies { classpath 'com.google.gms:google-services:4.3.14' + classpath('com.google.android.gms:oss-licenses-plugin:0.10.4') } }// Top-level build file where you can add configuration options common to all sub-projects/modules. From 7b35dc7d0fd6194aa2becdf3cf3aabec9c786a63 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Tue, 19 Mar 2024 14:14:22 +0900 Subject: [PATCH 013/301] =?UTF-8?q?feat:=20=EC=84=B8=ED=8C=85=20=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20=EC=9D=B4=EB=B2=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../createcapsule/CreateCapsule3Fragment.kt | 2 + .../ui/mypage/setting/SettingViewModel.kt | 26 +++++++++ .../ui/mypage/setting/SettingViewModelImpl.kt | 58 +++++++++++++++++++ 3 files changed, 86 insertions(+) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsule3Fragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsule3Fragment.kt index 38575aa28..2ba146171 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsule3Fragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsule3Fragment.kt @@ -210,6 +210,8 @@ class CreateCapsule3Fragment : } CreateCapsuleViewModel.Create3Event.ClickVideoUpLoad -> {} + + else -> {} } } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingViewModel.kt index f04ba6a31..3af8ceeef 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingViewModel.kt @@ -1,4 +1,30 @@ package com.droidblossom.archive.presentation.ui.mypage.setting +import com.droidblossom.archive.presentation.ui.skin.skinmake.SkinMakeViewModel +import kotlinx.coroutines.flow.SharedFlow + interface SettingViewModel { + + val settingMainEvents: SharedFlow + + fun back() + fun goUser() + fun goNotification() + fun goNotice() + fun goAgree() + fun goInquire() + fun goLicenses() + fun goLogout() + + sealed class SettingMainEvent { + object Back : SettingMainEvent() + object GoUser : SettingMainEvent() + object GoNotification : SettingMainEvent() + object GoNotice : SettingMainEvent() + object GoAgree : SettingMainEvent() + object GoInquire : SettingMainEvent() + object GoLicenses : SettingMainEvent() + object GoLogout : SettingMainEvent() + data class ShowToastMessage(val message : String) : SettingMainEvent() + } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingViewModelImpl.kt index 2e84b0682..6b481dbc2 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingViewModelImpl.kt @@ -1,7 +1,13 @@ package com.droidblossom.archive.presentation.ui.mypage.setting +import androidx.lifecycle.viewModelScope import com.droidblossom.archive.presentation.base.BaseViewModel +import com.droidblossom.archive.presentation.ui.skin.skinmake.SkinMakeViewModel import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel @@ -9,4 +15,56 @@ class SettingViewModelImpl @Inject constructor( ) : BaseViewModel(), SettingViewModel { + //main + private val _settingMainEvents = MutableSharedFlow() + override val settingMainEvents: SharedFlow + get() = _settingMainEvents.asSharedFlow() + + override fun back() { + viewModelScope.launch { + _settingMainEvents.emit(SettingViewModel.SettingMainEvent.Back) + } + } + + override fun goUser() { + viewModelScope.launch { + _settingMainEvents.emit(SettingViewModel.SettingMainEvent.GoUser) + } + } + + override fun goNotification() { + viewModelScope.launch { + _settingMainEvents.emit(SettingViewModel.SettingMainEvent.GoNotification) + } + } + + override fun goNotice() { + viewModelScope.launch { + _settingMainEvents.emit(SettingViewModel.SettingMainEvent.GoNotice) + } + } + + override fun goAgree() { + viewModelScope.launch { + _settingMainEvents.emit(SettingViewModel.SettingMainEvent.GoAgree) + } + } + + override fun goInquire() { + viewModelScope.launch { + _settingMainEvents.emit(SettingViewModel.SettingMainEvent.GoInquire) + } + } + + override fun goLicenses() { + viewModelScope.launch { + _settingMainEvents.emit(SettingViewModel.SettingMainEvent.GoLicenses) + } + } + + override fun goLogout() { + viewModelScope.launch { + _settingMainEvents.emit(SettingViewModel.SettingMainEvent.GoLogout) + } + } } \ No newline at end of file From 92b25095e583b0bddab1fa0f9fd852c67762b13a Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Tue, 19 Mar 2024 14:18:48 +0900 Subject: [PATCH 014/301] =?UTF-8?q?feat:=20=EC=98=A4=ED=94=88=EC=86=8C?= =?UTF-8?q?=EC=8A=A4=20=EB=9D=BC=EC=9D=B4=EC=84=BC=EC=8A=A4=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ARchive/app/src/main/AndroidManifest.xml | 9 +++ .../ui/mypage/setting/SettingMainFragment.kt | 61 ++++++++++++++++++- .../main/res/layout/fragment_setting_main.xml | 23 ++++--- 3 files changed, 83 insertions(+), 10 deletions(-) diff --git a/frontend/ARchive/app/src/main/AndroidManifest.xml b/frontend/ARchive/app/src/main/AndroidManifest.xml index 73361fe78..ea4e7c61f 100644 --- a/frontend/ARchive/app/src/main/AndroidManifest.xml +++ b/frontend/ARchive/app/src/main/AndroidManifest.xml @@ -105,6 +105,15 @@ + + + diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt index aaf0942cf..0a96b0464 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt @@ -1,13 +1,72 @@ package com.droidblossom.archive.presentation.ui.mypage.setting +import android.content.Intent +import android.os.Bundle +import android.view.View import androidx.fragment.app.viewModels +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import com.droidblossom.archive.R import com.droidblossom.archive.databinding.FragmentSettingMainBinding import com.droidblossom.archive.presentation.base.BaseFragment +import com.google.android.gms.oss.licenses.OssLicensesMenuActivity +import kotlinx.coroutines.launch class SettingMainFragment : BaseFragment(R.layout.fragment_setting_main) { override val viewModel: SettingViewModelImpl by viewModels() - override fun observeData() {} + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.vm = viewModel + } + override fun observeData() { + viewLifecycleOwner.lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED){ + viewModel.settingMainEvents.collect{ event-> + when(event){ + SettingViewModel.SettingMainEvent.Back -> { + (activity as SettingActivity).finish() + } + + SettingViewModel.SettingMainEvent.GoAgree -> { + + } + + SettingViewModel.SettingMainEvent.GoInquire -> { + + } + + SettingViewModel.SettingMainEvent.GoLicenses -> { + startActivity(Intent(requireContext(), OssLicensesMenuActivity::class.java)) + OssLicensesMenuActivity.setActivityTitle("오픈소스 라이선스") + } + + SettingViewModel.SettingMainEvent.GoLogout -> { + + } + + SettingViewModel.SettingMainEvent.GoNotice -> { + + } + + SettingViewModel.SettingMainEvent.GoNotification -> { + + } + + SettingViewModel.SettingMainEvent.GoUser -> { + + } + + is SettingViewModel.SettingMainEvent.ShowToastMessage -> { + + } + + else -> {} + } + } + } + } + } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_setting_main.xml b/frontend/ARchive/app/src/main/res/layout/fragment_setting_main.xml index 700f60210..e0f7eac9f 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_setting_main.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_setting_main.xml @@ -4,6 +4,9 @@ + + android:src="@drawable/ic_left_arrow_24" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> @@ -397,11 +402,11 @@ + android:layout_height="24dp" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/logoutLayout" /> From da318ed45975760e6f2d12d99ee35822ac91bd1d Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Tue, 19 Mar 2024 14:59:16 +0900 Subject: [PATCH 015/301] =?UTF-8?q?fix=20:=20=EC=8B=9C=EA=B0=84=20?= =?UTF-8?q?=EB=8B=A4=EC=9D=B4=EC=96=B4=EB=A6=AC=20=EB=84=93=EC=9D=B4=20?= =?UTF-8?q?=EB=86=92=EC=9D=B4=20=EC=A1=B0=EC=A0=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/ARchive/app/build.gradle | 2 +- .../createcapsule/dialog/DatePickerDialogFragment.kt | 11 +++++++++++ .../ui/mypage/setting/SettingMainFragment.kt | 3 ++- .../ARchive/app/src/main/res/layout/common_dialog.xml | 6 ++++++ .../main/res/layout/fragment_date_picker_dialog.xml | 5 +++-- 5 files changed, 23 insertions(+), 4 deletions(-) create mode 100644 frontend/ARchive/app/src/main/res/layout/common_dialog.xml diff --git a/frontend/ARchive/app/build.gradle b/frontend/ARchive/app/build.gradle index 29f6b1fe8..340099232 100644 --- a/frontend/ARchive/app/build.gradle +++ b/frontend/ARchive/app/build.gradle @@ -180,7 +180,7 @@ dependencies { // Event Bus : https://github.com/greenrobot/EventBus implementation("org.greenrobot:eventbus:3.3.1") - // oss-licenses + // oss-licenses : https://github.com/google/play-services-plugins/tree/master/oss-licenses-plugin implementation 'com.google.android.gms:play-services-oss-licenses:17.0.1' } kapt { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/dialog/DatePickerDialogFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/dialog/DatePickerDialogFragment.kt index 5e671e5c4..c0e0d0c3e 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/dialog/DatePickerDialogFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/dialog/DatePickerDialogFragment.kt @@ -3,6 +3,7 @@ package com.droidblossom.archive.presentation.ui.home.createcapsule.dialog import android.os.Bundle import android.util.Log import android.view.View +import android.view.ViewGroup import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope @@ -31,6 +32,16 @@ class DatePickerDialogFragment(private val onClick: (String, String) -> Unit) : private val currentHour = DateUtils.getCurrentHour() private val currentMin = DateUtils.getCurrentMin() + override fun onStart() { + super.onStart() + val dialog = dialog + if (dialog != null) { + val width = ViewGroup.LayoutParams.MATCH_PARENT + val height = ViewGroup.LayoutParams.WRAP_CONTENT + dialog.window?.setLayout(width, height) + } + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding.vm = viewModel diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt index 0a96b0464..b5e25241a 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt @@ -21,6 +21,7 @@ class SettingMainFragment : super.onViewCreated(view, savedInstanceState) binding.vm = viewModel } + override fun observeData() { viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED){ @@ -60,7 +61,7 @@ class SettingMainFragment : } is SettingViewModel.SettingMainEvent.ShowToastMessage -> { - + showToastMessage(event.message) } else -> {} diff --git a/frontend/ARchive/app/src/main/res/layout/common_dialog.xml b/frontend/ARchive/app/src/main/res/layout/common_dialog.xml new file mode 100644 index 000000000..77d9ef65f --- /dev/null +++ b/frontend/ARchive/app/src/main/res/layout/common_dialog.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_date_picker_dialog.xml b/frontend/ARchive/app/src/main/res/layout/fragment_date_picker_dialog.xml index ad6bd5c77..dc4c0b30b 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_date_picker_dialog.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_date_picker_dialog.xml @@ -18,11 +18,12 @@ From 3f566b95052b04c0b5c03ee66675e4ce0d3286f0 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Tue, 19 Mar 2024 23:16:29 +0900 Subject: [PATCH 016/301] faet : commont fragment --- .../dialog/CommonDialogFragment.kt | 56 ++++++++++++++ .../app/src/main/res/layout/common_dialog.xml | 6 -- .../res/layout/fragment_common_dialog.xml | 76 +++++++++++++++++++ 3 files changed, 132 insertions(+), 6 deletions(-) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/dialog/CommonDialogFragment.kt delete mode 100644 frontend/ARchive/app/src/main/res/layout/common_dialog.xml create mode 100644 frontend/ARchive/app/src/main/res/layout/fragment_common_dialog.xml diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/dialog/CommonDialogFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/dialog/CommonDialogFragment.kt new file mode 100644 index 000000000..5c085b92a --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/dialog/CommonDialogFragment.kt @@ -0,0 +1,56 @@ +package com.droidblossom.archive.presentation.dialog + +import android.os.Bundle +import android.view.View +import android.view.ViewGroup +import com.droidblossom.archive.R +import com.droidblossom.archive.databinding.FragmentCommonDialogBinding +import com.droidblossom.archive.presentation.base.BaseDialogFragment + +class CommonDialogFragment( + private val onClick: () -> Unit +) : BaseDialogFragment(R.layout.fragment_common_dialog) { + + override fun onStart() { + super.onStart() + val dialog = dialog + if (dialog != null) { + val width = ViewGroup.LayoutParams.MATCH_PARENT + val height = ViewGroup.LayoutParams.WRAP_CONTENT + dialog.window?.setLayout(width, height) + } + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + binding.messageT.text = arguments?.getString("title") ?: "" + binding.rightBtn.text = arguments?.getString("rightBtnT") ?: "확인" + + binding.leftBtn.setOnClickListener { + this.dismiss() + } + + binding.rightBtn.setOnClickListener { + onClick() + this.dismiss() + } + } + + companion object { + + fun newIntent( + title: String, + rightBtnT: String, + onRightClick: () -> Unit + ): CommonDialogFragment { + val args = Bundle().apply { + putString("title", title) + putString("rightBtnT", rightBtnT) + } + return CommonDialogFragment(onRightClick).apply { + arguments = args + } + } + } +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/common_dialog.xml b/frontend/ARchive/app/src/main/res/layout/common_dialog.xml deleted file mode 100644 index 77d9ef65f..000000000 --- a/frontend/ARchive/app/src/main/res/layout/common_dialog.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_common_dialog.xml b/frontend/ARchive/app/src/main/res/layout/fragment_common_dialog.xml new file mode 100644 index 000000000..5947ab20d --- /dev/null +++ b/frontend/ARchive/app/src/main/res/layout/fragment_common_dialog.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From fdc4d68642c5071fa64505b173f811bc8fcdce34 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Tue, 19 Mar 2024 23:16:45 +0900 Subject: [PATCH 017/301] =?UTF-8?q?feat=20:=20=EB=A1=9C=EA=B7=B8=EC=95=84?= =?UTF-8?q?=EC=9B=83=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/mypage/setting/SettingMainFragment.kt | 22 ++++++++++++++++++- .../main/res/layout/fragment_setting_main.xml | 3 ++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt index b5e25241a..39690b427 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt @@ -2,6 +2,7 @@ package com.droidblossom.archive.presentation.ui.mypage.setting import android.content.Intent import android.os.Bundle +import android.util.Log import android.view.View import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle @@ -10,13 +11,26 @@ import androidx.lifecycle.repeatOnLifecycle import com.droidblossom.archive.R import com.droidblossom.archive.databinding.FragmentSettingMainBinding import com.droidblossom.archive.presentation.base.BaseFragment +import com.droidblossom.archive.presentation.dialog.CommonDialogFragment +import com.droidblossom.archive.presentation.ui.auth.AuthActivity +import com.droidblossom.archive.presentation.ui.home.dialog.CapsulePreviewDialogFragment +import com.droidblossom.archive.util.DataStoreUtils import com.google.android.gms.oss.licenses.OssLicensesMenuActivity +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import javax.inject.Inject +@AndroidEntryPoint class SettingMainFragment : BaseFragment(R.layout.fragment_setting_main) { + override val viewModel: SettingViewModelImpl by viewModels() + @Inject + lateinit var dataStoreUtils: DataStoreUtils + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding.vm = viewModel @@ -45,7 +59,13 @@ class SettingMainFragment : } SettingViewModel.SettingMainEvent.GoLogout -> { - + val sheet = CommonDialogFragment.newIntent("정말 로그아웃 하시겠습니까?","로그아웃") { + CoroutineScope(Dispatchers.IO).launch { + dataStoreUtils.resetTokenData() + } + AuthActivity.goAuth(requireContext()) + } + sheet.show(parentFragmentManager, "logoutDialog") } SettingViewModel.SettingMainEvent.GoNotice -> { diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_setting_main.xml b/frontend/ARchive/app/src/main/res/layout/fragment_setting_main.xml index e0f7eac9f..0958291e9 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_setting_main.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_setting_main.xml @@ -32,8 +32,8 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="4dp" - android:paddingHorizontal="16dp" android:onClick="@{()->vm.back()}" + android:paddingHorizontal="16dp" android:paddingVertical="8dp" android:src="@drawable/ic_left_arrow_24" app:layout_constraintStart_toStartOf="parent" @@ -370,6 +370,7 @@ android:layout_width="0dp" android:layout_height="60dp" android:layout_marginHorizontal="8dp" + android:onClick="@{()->vm.goLogout()}" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/licenseLayout"> From 1ff2c76d9e86a9db3c36934ae9e01c6a10b489c6 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Tue, 19 Mar 2024 23:24:45 +0900 Subject: [PATCH 018/301] =?UTF-8?q?chore=20:=20home=20text=20=EC=A7=80?= =?UTF-8?q?=EC=9A=B0=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../res/layout/fragment_common_dialog.xml | 3 +- .../app/src/main/res/layout/fragment_home.xml | 47 +++++++++---------- 2 files changed, 24 insertions(+), 26 deletions(-) diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_common_dialog.xml b/frontend/ARchive/app/src/main/res/layout/fragment_common_dialog.xml index 5947ab20d..a41d613bf 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_common_dialog.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_common_dialog.xml @@ -14,11 +14,10 @@ diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_home.xml b/frontend/ARchive/app/src/main/res/layout/fragment_home.xml index 1cadcd4cd..243cdcde3 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_home.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_home.xml @@ -35,30 +35,29 @@ android:layout_height="0dp" android:name="com.naver.maps.map.MapFragment" /> - - - - + + + + + + + + + + + + + + + + + + + + + + + Date: Wed, 20 Mar 2024 00:13:43 +0900 Subject: [PATCH 019/301] chore --- .../presentation/ui/home/HomeFragment.kt | 6 ----- .../app/src/main/res/layout/fragment_home.xml | 24 ------------------- 2 files changed, 30 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt index bb3a57d0a..9066033df 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt @@ -76,12 +76,6 @@ class HomeFragment : BaseFragment(R.layo makeSecretCapsuleBtn.setOnClickListener { startActivity(CreateCapsuleActivity.newIntent(requireContext(), 3)) } - snackbarTestBtn.setOnClickListener { - HomeSnackBarSmall(requireView()).show() - } - snackbarBigText.setOnClickListener { - HomeSnackBarBig(requireView(), "", "").show() - } refreshBtn.setOnClickListener { locationUtil.getCurrentLocation { latitude, longitude -> viewModel.getNearbyCapsules( diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_home.xml b/frontend/ARchive/app/src/main/res/layout/fragment_home.xml index 243cdcde3..179184749 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_home.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_home.xml @@ -35,30 +35,6 @@ android:layout_height="0dp" android:name="com.naver.maps.map.MapFragment" /> - - - - - - - - - - - - - - - - - - - - - - - - Date: Wed, 20 Mar 2024 13:02:54 +0900 Subject: [PATCH 020/301] =?UTF-8?q?Feat:=20=ED=8C=A8=ED=82=A4=E3=85=A3?= =?UTF-8?q?=EC=A7=80=20=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/presentation/base/BaseActivity.kt | 6 ++---- .../presentation/{snack => customview}/CallSnackBar.kt | 2 +- .../{dialog => customview}/CommonDialogFragment.kt | 2 +- .../presentation/{snack => customview}/HomeSnackBarBig.kt | 2 +- .../{snack => customview}/HomeSnackBarSmall.kt | 2 +- .../archive/presentation/ui/home/HomeFragment.kt | 8 -------- .../presentation/ui/mypage/setting/SettingMainFragment.kt | 4 +--- .../archive/util/MyFirebaseMessagingService.kt | 1 - 8 files changed, 7 insertions(+), 20 deletions(-) rename frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/{snack => customview}/CallSnackBar.kt (60%) rename frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/{dialog => customview}/CommonDialogFragment.kt (96%) rename frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/{snack => customview}/HomeSnackBarBig.kt (96%) rename frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/{snack => customview}/HomeSnackBarSmall.kt (96%) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseActivity.kt index 48c9b33ba..92611b691 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseActivity.kt @@ -1,6 +1,5 @@ package com.droidblossom.archive.presentation.base -import android.content.Intent import android.graphics.Color import android.os.Bundle import android.util.Log @@ -11,8 +10,7 @@ import androidx.annotation.LayoutRes import androidx.appcompat.app.AppCompatActivity import androidx.databinding.DataBindingUtil import androidx.databinding.ViewDataBinding -import com.droidblossom.archive.presentation.snack.CallSnackBar -import com.droidblossom.archive.presentation.snack.HomeSnackBarSmall +import com.droidblossom.archive.presentation.customview.HomeSnackBarSmall import kotlinx.coroutines.Job import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe @@ -47,7 +45,7 @@ abstract class BaseActivity(@LayoutRes v // 스낵바 호출로 바꾸면 될듯? Log.d("이베", "$event") binding.root.let { rootView -> - HomeSnackBarSmall(rootView).show() + //HomeSnackBarSmall(rootView).show() } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/snack/CallSnackBar.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/CallSnackBar.kt similarity index 60% rename from frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/snack/CallSnackBar.kt rename to frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/CallSnackBar.kt index bc2ce7aed..90cc3a8d3 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/snack/CallSnackBar.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/CallSnackBar.kt @@ -1,4 +1,4 @@ -package com.droidblossom.archive.presentation.snack +package com.droidblossom.archive.presentation.customview data class CallSnackBar( // 타입, 아이디 등등 추가 diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/dialog/CommonDialogFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/CommonDialogFragment.kt similarity index 96% rename from frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/dialog/CommonDialogFragment.kt rename to frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/CommonDialogFragment.kt index 5c085b92a..7a56f9669 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/dialog/CommonDialogFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/CommonDialogFragment.kt @@ -1,4 +1,4 @@ -package com.droidblossom.archive.presentation.dialog +package com.droidblossom.archive.presentation.customview import android.os.Bundle import android.view.View diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/snack/HomeSnackBarBig.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/HomeSnackBarBig.kt similarity index 96% rename from frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/snack/HomeSnackBarBig.kt rename to frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/HomeSnackBarBig.kt index 8160f3c98..b756f77a5 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/snack/HomeSnackBarBig.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/HomeSnackBarBig.kt @@ -1,4 +1,4 @@ -package com.droidblossom.archive.presentation.snack +package com.droidblossom.archive.presentation.customview import android.view.LayoutInflater import android.view.View diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/snack/HomeSnackBarSmall.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/HomeSnackBarSmall.kt similarity index 96% rename from frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/snack/HomeSnackBarSmall.kt rename to frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/HomeSnackBarSmall.kt index e3d76c0f9..01033771c 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/snack/HomeSnackBarSmall.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/HomeSnackBarSmall.kt @@ -1,4 +1,4 @@ -package com.droidblossom.archive.presentation.snack +package com.droidblossom.archive.presentation.customview import android.view.LayoutInflater diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt index 9066033df..23ec03244 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt @@ -3,10 +3,8 @@ package com.droidblossom.archive.presentation.ui.home import android.graphics.Point import android.location.Location import android.os.Bundle -import android.util.Log import android.view.View import android.view.ViewGroup -import androidx.core.content.ContentProviderCompat.requireContext import androidx.core.content.ContextCompat import androidx.core.graphics.toPointF import androidx.fragment.app.viewModels @@ -17,8 +15,6 @@ import com.droidblossom.archive.R import com.droidblossom.archive.databinding.FragmentHomeBinding import com.droidblossom.archive.domain.model.common.CapsuleMarker import com.droidblossom.archive.presentation.base.BaseFragment -import com.droidblossom.archive.presentation.snack.HomeSnackBarBig -import com.droidblossom.archive.presentation.snack.HomeSnackBarSmall import com.droidblossom.archive.presentation.ui.home.createcapsule.CreateCapsuleActivity import com.droidblossom.archive.presentation.ui.home.dialog.CapsulePreviewDialogFragment import com.droidblossom.archive.util.CapsuleTypeUtils @@ -35,11 +31,7 @@ import com.naver.maps.map.overlay.Overlay import com.naver.maps.map.overlay.OverlayImage import com.naver.maps.map.util.FusedLocationSource import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import kotlin.random.Random @AndroidEntryPoint class HomeFragment : BaseFragment(R.layout.fragment_home), diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt index 39690b427..e40ad9206 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt @@ -2,7 +2,6 @@ package com.droidblossom.archive.presentation.ui.mypage.setting import android.content.Intent import android.os.Bundle -import android.util.Log import android.view.View import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle @@ -11,9 +10,8 @@ import androidx.lifecycle.repeatOnLifecycle import com.droidblossom.archive.R import com.droidblossom.archive.databinding.FragmentSettingMainBinding import com.droidblossom.archive.presentation.base.BaseFragment -import com.droidblossom.archive.presentation.dialog.CommonDialogFragment +import com.droidblossom.archive.presentation.customview.CommonDialogFragment import com.droidblossom.archive.presentation.ui.auth.AuthActivity -import com.droidblossom.archive.presentation.ui.home.dialog.CapsulePreviewDialogFragment import com.droidblossom.archive.util.DataStoreUtils import com.google.android.gms.oss.licenses.OssLicensesMenuActivity import dagger.hilt.android.AndroidEntryPoint diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/MyFirebaseMessagingService.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/MyFirebaseMessagingService.kt index e456926b5..22addcf10 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/MyFirebaseMessagingService.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/MyFirebaseMessagingService.kt @@ -12,7 +12,6 @@ import android.os.Build import android.util.Log import androidx.core.app.NotificationCompat import com.droidblossom.archive.R -import com.droidblossom.archive.presentation.snack.CallSnackBar import com.droidblossom.archive.presentation.ui.MainActivity import com.google.firebase.messaging.FirebaseMessaging import com.google.firebase.messaging.FirebaseMessagingService From 28806fbc85aa2d930e987523503c69327e8c9952 Mon Sep 17 00:00:00 2001 From: comst19 Date: Wed, 20 Mar 2024 15:03:57 +0900 Subject: [PATCH 021/301] =?UTF-8?q?feat:=20CameraFragment=EA=B0=80=20hidde?= =?UTF-8?q?n=20=EB=90=A0=20=EB=95=8C=20=EC=95=B5=EC=BB=A4=20=EC=97=86?= =?UTF-8?q?=EC=95=A0=EB=8A=94=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ui/camera/CameraFragment.kt | 54 ++++++++++--------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt index cc0da57d2..d8f13cb74 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt @@ -33,6 +33,7 @@ class CameraFragment : FragmentManagerProvider { override val viewModel: CameraViewModelImpl by viewModels() + private val anchorNodes = mutableListOf() // private val capsules: MutableList = mutableListOf() lateinit var arSceneView: ARSceneView @@ -69,7 +70,7 @@ class CameraFragment : repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.capsuleList.collect { capsuleList -> arSceneView.onSessionUpdated = { session, frame -> - if (anchorNode == null) { + if (anchorNodes.isEmpty()) { Log.d("CameraFragmentAR", "earth setting start") val earth = session.earth if (earth == null) { @@ -137,32 +138,31 @@ class CameraFragment : private fun addAnchorNode(anchor: Anchor, capsule: CapsuleMarker) { Log.d("CameraFragmentAR", "addAnchorNode added") - val arContentNode = - arSceneView.let { scenview -> - viewAttachmentManager.let { attachManager -> - ARContentNode( - scenview, - attachManager, - this, - capsule, - layoutInflater, - requireContext(), - onLoaded = { viewNode -> - arSceneView.engine.let { - AnchorNode(it, anchor) - .apply { - isEditable = true - lifecycleScope.launch { - addChildNode(viewNode) - } - anchorNode = this - } - }.let { - arSceneView.addChildNode(it) + arSceneView.let { sceneView -> + viewAttachmentManager.let { attachManager -> + ARContentNode( + sceneView, + attachManager, + this, + capsule, + layoutInflater, + requireContext(), + onLoaded = { viewNode -> + sceneView.engine.let { + AnchorNode(it, anchor).apply { + isEditable = true + lifecycleScope.launch { + addChildNode(viewNode) + } + anchorNodes.add(this) } - }) - } + }.let { + sceneView.addChildNode(it) + } + } + ) } + } } private fun createSession() { @@ -180,6 +180,10 @@ class CameraFragment : override fun onHiddenChanged(hidden: Boolean) { super.onHiddenChanged(hidden) if (hidden) { + for (anchorNode in anchorNodes) { + arSceneView.removeChildNode(anchorNode) + } + anchorNodes.clear() viewAttachmentManager.onPause() arSceneView.session?.pause() } else { From d8f418647db39f909f10bccfcae2feb2335ada3c Mon Sep 17 00:00:00 2001 From: comst19 Date: Wed, 20 Mar 2024 15:59:05 +0900 Subject: [PATCH 022/301] =?UTF-8?q?refact:=20CameraFragment=EA=B0=80=20hid?= =?UTF-8?q?den=20=EB=90=A0=20=EB=95=8C=20=EC=95=B5=EC=BB=A4=20=EC=97=86?= =?UTF-8?q?=EC=95=A0=EB=8A=94=20=EB=A1=9C=EC=A7=81=20viewModel=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B4=EC=A0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ui/camera/CameraFragment.kt | 14 ++++++++------ .../presentation/ui/camera/CameraViewModel.kt | 8 ++++++++ .../ui/camera/CameraViewModelImpl.kt | 18 ++++++++++++++++++ 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt index d8f13cb74..56d0aa927 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt @@ -25,6 +25,7 @@ import com.google.ar.sceneform.rendering.ViewAttachmentManager import dagger.hilt.android.AndroidEntryPoint import io.github.sceneview.ar.ARSceneView import io.github.sceneview.ar.node.AnchorNode +import kotlinx.coroutines.flow.filter import kotlinx.coroutines.launch @AndroidEntryPoint @@ -33,11 +34,9 @@ class CameraFragment : FragmentManagerProvider { override val viewModel: CameraViewModelImpl by viewModels() - private val anchorNodes = mutableListOf() // private val capsules: MutableList = mutableListOf() lateinit var arSceneView: ARSceneView - private var anchorNode: AnchorNode? = null private lateinit var session: Session private lateinit var config: Config private lateinit var viewAttachmentManager: ViewAttachmentManager @@ -70,7 +69,7 @@ class CameraFragment : repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.capsuleList.collect { capsuleList -> arSceneView.onSessionUpdated = { session, frame -> - if (anchorNodes.isEmpty()) { + if (viewModel.anchorNodes.value.isEmpty()) { Log.d("CameraFragmentAR", "earth setting start") val earth = session.earth if (earth == null) { @@ -114,6 +113,7 @@ class CameraFragment : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + showLoading(requireContext()) val locationUtil = LocationUtil(requireContext()) locationUtil.getCurrentLocation { latitude, longitude -> viewModel.getCapsules(latitude = latitude, longitude = longitude) @@ -154,7 +154,7 @@ class CameraFragment : lifecycleScope.launch { addChildNode(viewNode) } - anchorNodes.add(this) + viewModel.addAnchorNode(this) } }.let { sceneView.addChildNode(it) @@ -180,13 +180,15 @@ class CameraFragment : override fun onHiddenChanged(hidden: Boolean) { super.onHiddenChanged(hidden) if (hidden) { - for (anchorNode in anchorNodes) { + dismissLoading() + for (anchorNode in viewModel.anchorNodes.value) { arSceneView.removeChildNode(anchorNode) } - anchorNodes.clear() + viewModel.clearAnchorNode() viewAttachmentManager.onPause() arSceneView.session?.pause() } else { + showLoading(requireContext()) arSceneView.session?.resume() viewAttachmentManager.onResume() val locationUtil = LocationUtil(requireContext()) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModel.kt index c9358fd54..94f6c3e1c 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModel.kt @@ -1,6 +1,8 @@ package com.droidblossom.archive.presentation.ui.camera import com.droidblossom.archive.domain.model.common.CapsuleMarker +import io.github.sceneview.ar.node.AnchorNode +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow @@ -8,10 +10,16 @@ interface CameraViewModel { val capsuleList:StateFlow> val cameraEvents: SharedFlow + val anchorNodes: StateFlow> fun getCapsules(latitude: Double, longitude: Double,): List + fun cameraEvent(event : CameraEvent) + fun addAnchorNode(anchorNode: AnchorNode) + + fun clearAnchorNode() + sealed class CameraEvent{ data class ShowCapsulePreviewDialog(val capsuleId: String, val capsuleType: String) : CameraEvent() } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModelImpl.kt index 9e5a8a726..0275a1c8c 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModelImpl.kt @@ -1,5 +1,6 @@ package com.droidblossom.archive.presentation.ui.camera +import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope import com.droidblossom.archive.domain.model.common.CapsuleMarker import com.droidblossom.archive.domain.usecase.capsule.NearbyCapsulesUseCase @@ -7,11 +8,13 @@ import com.droidblossom.archive.presentation.base.BaseViewModel import com.droidblossom.archive.util.onFail import com.droidblossom.archive.util.onSuccess import dagger.hilt.android.lifecycle.HiltViewModel +import io.github.sceneview.ar.node.AnchorNode import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch import javax.inject.Inject @@ -28,11 +31,26 @@ class CameraViewModelImpl@Inject constructor( override val capsuleList: StateFlow> get() = _capsuleList + private val _anchorNodes = MutableStateFlow>(mutableListOf()) + override val anchorNodes get() = _anchorNodes + + override fun addAnchorNode(anchorNode: AnchorNode) { + val updatedList = _anchorNodes.value.toMutableList() + updatedList.add(anchorNode) + _anchorNodes.value = updatedList + } + + override fun clearAnchorNode() { + _anchorNodes.value = mutableListOf() + } + + override fun cameraEvent(event: CameraViewModel.CameraEvent) { viewModelScope.launch { _cameraEvents.emit(event) } } + override fun getCapsules(latitude: Double, longitude: Double) : List { viewModelScope.launch { nearbyCapsulesUseCase(latitude,longitude,1.0,"ALL").collect{ result-> From cabba014e30baa66383bc3ba5c78913fc6662990 Mon Sep 17 00:00:00 2001 From: comst19 Date: Wed, 20 Mar 2024 16:02:48 +0900 Subject: [PATCH 023/301] =?UTF-8?q?feat:=20=EC=A3=BC=EB=B3=80=20=EC=BA=A1?= =?UTF-8?q?=EC=8A=90=EC=9D=98=20=EA=B0=AF=EC=88=98=EB=A5=BC=20=EA=B0=80?= =?UTF-8?q?=EC=A7=80=EB=8A=94=20=EB=B3=80=EC=88=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/presentation/ui/camera/CameraViewModel.kt | 1 + .../archive/presentation/ui/camera/CameraViewModelImpl.kt | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModel.kt index 94f6c3e1c..9186b6645 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModel.kt @@ -10,6 +10,7 @@ interface CameraViewModel { val capsuleList:StateFlow> val cameraEvents: SharedFlow + val capsuleListSize: SharedFlow val anchorNodes: StateFlow> fun getCapsules(latitude: Double, longitude: Double,): List diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModelImpl.kt index 0275a1c8c..659690685 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModelImpl.kt @@ -31,6 +31,9 @@ class CameraViewModelImpl@Inject constructor( override val capsuleList: StateFlow> get() = _capsuleList + private val _capsuleListSize = MutableStateFlow(-1) + override val capsuleListSize = _capsuleListSize.asStateFlow() + private val _anchorNodes = MutableStateFlow>(mutableListOf()) override val anchorNodes get() = _anchorNodes @@ -56,6 +59,7 @@ class CameraViewModelImpl@Inject constructor( nearbyCapsulesUseCase(latitude,longitude,1.0,"ALL").collect{ result-> result.onSuccess { _capsuleList.emit(it.capsules) + _capsuleListSize.value = _capsuleList.value.size }.onFail { } From c6696549bbcb21a504b573c3a59ce0f3146cbc29 Mon Sep 17 00:00:00 2001 From: comst19 Date: Wed, 20 Mar 2024 16:03:25 +0900 Subject: [PATCH 024/301] =?UTF-8?q?feat:=20=EC=A3=BC=EB=B3=80=20=EC=BA=A1?= =?UTF-8?q?=EC=8A=90=20=EA=B0=AF=EC=88=98=EC=99=80=20=EB=A0=8C=EB=8D=94?= =?UTF-8?q?=EB=A7=81=EB=90=9C=20=EC=95=B5=EC=BB=A4=EC=9D=98=20=EA=B0=AF?= =?UTF-8?q?=EC=88=98=EA=B0=80=20=EA=B0=99=EC=9C=BC=EB=A9=B4=20=EB=A1=9C?= =?UTF-8?q?=EB=94=A9=20=EC=97=86=EC=95=A0=EB=8A=94=20=EB=A1=9C=EC=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/presentation/ui/camera/CameraFragment.kt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt index 56d0aa927..0958ac5f6 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt @@ -65,6 +65,18 @@ class CameraFragment : } } + viewLifecycleOwner.lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.anchorNodes + .filter { anchorNodes -> + anchorNodes.size == viewModel.capsuleListSize.value + } + .collect { + dismissLoading() + } + } + } + viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.capsuleList.collect { capsuleList -> From b480a329b9698b1e61dfcc7a5660ad8adff19828 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Wed, 20 Mar 2024 18:01:50 +0900 Subject: [PATCH 025/301] =?UTF-8?q?design=20:=20=EC=8A=A4=ED=82=A8=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/src/main/res/layout/fragment_skin.xml | 28 +++++++++---------- .../app/src/main/res/layout/item_my_skin.xml | 8 ++++-- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_skin.xml b/frontend/ARchive/app/src/main/res/layout/fragment_skin.xml index bfe6afda5..b4be28025 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_skin.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_skin.xml @@ -127,30 +127,36 @@ android:id="@+id/skinRV" android:layout_width="0dp" android:layout_height="0dp" + android:layout_marginTop="100dp" + android:elevation="1dp" android:orientation="vertical" + android:clipToPadding="false" + android:paddingBottom="70dp" android:visibility="visible" - android:layout_marginTop="100dp" - app:layout_constraintBottom_toTopOf="@id/place" app:layoutManager="androidx.recyclerview.widget.GridLayoutManager" - app:spanCount="3" + app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/viewHeaderTitle" /> + app:layout_constraintTop_toBottomOf="@id/viewHeaderTitle" + app:layout_constraintVertical_bias="1.0" + app:spanCount="3" /> + app:cardElevation="1dp" + android:layout_marginBottom="110dp"> @@ -193,14 +199,6 @@ - - diff --git a/frontend/ARchive/app/src/main/res/layout/item_my_skin.xml b/frontend/ARchive/app/src/main/res/layout/item_my_skin.xml index 4cfd88dbd..d2aa11f3b 100644 --- a/frontend/ARchive/app/src/main/res/layout/item_my_skin.xml +++ b/frontend/ARchive/app/src/main/res/layout/item_my_skin.xml @@ -16,20 +16,24 @@ android:layout_height="wrap_content" android:layout_margin="4dp" app:cardCornerRadius="16dp" - app:cardElevation="4dp"> + app:strokeColor="@color/white" + android:background="@color/white" + android:elevation="0dp"> + Date: Thu, 21 Mar 2024 11:14:49 +0900 Subject: [PATCH 026/301] =?UTF-8?q?feat:=20=EC=95=8C=EB=A6=BC=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=ED=8E=98=EC=9D=B4=EC=A7=80=20init?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../page/SettingNotificationFragment.kt | 31 +++++++++++++++++++ .../layout/fragment_setting_notification.xml | 16 ++++++++++ 2 files changed, 47 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingNotificationFragment.kt create mode 100644 frontend/ARchive/app/src/main/res/layout/fragment_setting_notification.xml diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingNotificationFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingNotificationFragment.kt new file mode 100644 index 000000000..39bab7159 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingNotificationFragment.kt @@ -0,0 +1,31 @@ +package com.droidblossom.archive.presentation.ui.mypage.setting.page + +import android.os.Bundle +import android.view.View +import androidx.fragment.app.viewModels +import com.droidblossom.archive.R +import com.droidblossom.archive.databinding.FragmentSettingNotificationBinding +import com.droidblossom.archive.presentation.base.BaseFragment +import com.droidblossom.archive.presentation.ui.mypage.setting.SettingViewModelImpl +import com.droidblossom.archive.util.DataStoreUtils +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject + +@AndroidEntryPoint +class SettingNotificationFragment : + BaseFragment(R.layout.fragment_setting_notification) { + + override val viewModel: SettingViewModelImpl by viewModels() + + @Inject + lateinit var dataStoreUtils: DataStoreUtils + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.vm = viewModel + } + + override fun observeData() { + + } +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_setting_notification.xml b/frontend/ARchive/app/src/main/res/layout/fragment_setting_notification.xml new file mode 100644 index 000000000..66940357d --- /dev/null +++ b/frontend/ARchive/app/src/main/res/layout/fragment_setting_notification.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + \ No newline at end of file From f4b14a9d1c11f1a8fa3aa84a3f21b232efa51179 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Thu, 21 Mar 2024 14:45:33 +0900 Subject: [PATCH 027/301] =?UTF-8?q?feat=20:=20=EC=95=8C=EB=A6=BC=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=94=94=EC=9E=90?= =?UTF-8?q?=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/skin/skinmake/SkinMakeViewModelImpl.kt | 7 +- .../drawable/corner_radius_12_stroke_1.xml | 6 ++ .../main/res/drawable/switch_thumb_off.xml | 11 +++ .../src/main/res/drawable/switch_thumb_on.xml | 11 +++ .../res/drawable/switch_thumb_selector.xml | 5 ++ .../main/res/drawable/switch_track_off.xml | 12 ++++ .../src/main/res/drawable/switch_track_on.xml | 12 ++++ .../res/drawable/switch_track_selector.xml | 5 ++ .../main/res/layout/fragment_setting_main.xml | 3 +- .../layout/fragment_setting_notification.xml | 71 ++++++++++++++++++- 10 files changed, 138 insertions(+), 5 deletions(-) create mode 100644 frontend/ARchive/app/src/main/res/drawable/corner_radius_12_stroke_1.xml create mode 100644 frontend/ARchive/app/src/main/res/drawable/switch_thumb_off.xml create mode 100644 frontend/ARchive/app/src/main/res/drawable/switch_thumb_on.xml create mode 100644 frontend/ARchive/app/src/main/res/drawable/switch_thumb_selector.xml create mode 100644 frontend/ARchive/app/src/main/res/drawable/switch_track_off.xml create mode 100644 frontend/ARchive/app/src/main/res/drawable/switch_track_on.xml create mode 100644 frontend/ARchive/app/src/main/res/drawable/switch_track_selector.xml diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModelImpl.kt index 418d35022..1224d1ceb 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModelImpl.kt @@ -11,6 +11,8 @@ import com.droidblossom.archive.presentation.base.BaseViewModel import com.droidblossom.archive.presentation.ui.auth.AuthViewModel import com.droidblossom.archive.presentation.ui.home.createcapsule.CreateCapsuleViewModel import com.droidblossom.archive.presentation.ui.home.createcapsule.CreateCapsuleViewModelImpl +import com.droidblossom.archive.util.Motion +import com.droidblossom.archive.util.Retarget import com.droidblossom.archive.util.S3Util import com.droidblossom.archive.util.onFail import com.droidblossom.archive.util.onSuccess @@ -42,7 +44,8 @@ class SkinMakeViewModelImpl @Inject constructor( } private val _skinMakeEvents = MutableSharedFlow() - override val skinMakeEvents: SharedFlow = _skinMakeEvents.asSharedFlow() + override val skinMakeEvents: SharedFlow = + _skinMakeEvents.asSharedFlow() override val skinImgUri = MutableStateFlow(null) @@ -117,7 +120,7 @@ class SkinMakeViewModelImpl @Inject constructor( } } - private fun submitSkin(){ + private fun submitSkin() { viewModelScope.launch { capsuleSkinsMakeUseCase( CapsuleSkinsMakeRequest( diff --git a/frontend/ARchive/app/src/main/res/drawable/corner_radius_12_stroke_1.xml b/frontend/ARchive/app/src/main/res/drawable/corner_radius_12_stroke_1.xml new file mode 100644 index 000000000..2f373efed --- /dev/null +++ b/frontend/ARchive/app/src/main/res/drawable/corner_radius_12_stroke_1.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/drawable/switch_thumb_off.xml b/frontend/ARchive/app/src/main/res/drawable/switch_thumb_off.xml new file mode 100644 index 000000000..3b02f332d --- /dev/null +++ b/frontend/ARchive/app/src/main/res/drawable/switch_thumb_off.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/drawable/switch_thumb_on.xml b/frontend/ARchive/app/src/main/res/drawable/switch_thumb_on.xml new file mode 100644 index 000000000..ec4d2b1de --- /dev/null +++ b/frontend/ARchive/app/src/main/res/drawable/switch_thumb_on.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/drawable/switch_thumb_selector.xml b/frontend/ARchive/app/src/main/res/drawable/switch_thumb_selector.xml new file mode 100644 index 000000000..9fcdcf1b2 --- /dev/null +++ b/frontend/ARchive/app/src/main/res/drawable/switch_thumb_selector.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/drawable/switch_track_off.xml b/frontend/ARchive/app/src/main/res/drawable/switch_track_off.xml new file mode 100644 index 000000000..d031ada31 --- /dev/null +++ b/frontend/ARchive/app/src/main/res/drawable/switch_track_off.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/drawable/switch_track_on.xml b/frontend/ARchive/app/src/main/res/drawable/switch_track_on.xml new file mode 100644 index 000000000..1a423a206 --- /dev/null +++ b/frontend/ARchive/app/src/main/res/drawable/switch_track_on.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/drawable/switch_track_selector.xml b/frontend/ARchive/app/src/main/res/drawable/switch_track_selector.xml new file mode 100644 index 000000000..ac079f050 --- /dev/null +++ b/frontend/ARchive/app/src/main/res/drawable/switch_track_selector.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_setting_main.xml b/frontend/ARchive/app/src/main/res/layout/fragment_setting_main.xml index 0958291e9..01f73a128 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_setting_main.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_setting_main.xml @@ -11,7 +11,8 @@ + android:layout_height="match_parent" + android:background="@color/white"> - + @@ -8,9 +10,74 @@ type="com.droidblossom.archive.presentation.ui.mypage.setting.SettingViewModel" /> + + android:layout_height="match_parent" + android:background="@color/white"> + + + + + + + + + + + + \ No newline at end of file From b95bd89509046673c6e3a906143cab34651d4ddc Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Fri, 22 Mar 2024 00:42:20 +0900 Subject: [PATCH 028/301] =?UTF-8?q?feat:=20=EC=95=8C=EB=A6=BC=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20enable=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/NotificationEnabledUseCase.kt | 9 ++-- .../ui/mypage/setting/SettingViewModel.kt | 9 ++++ .../page/SettingNotificationFragment.kt | 51 +++++++++++++++++-- .../layout/fragment_setting_notification.xml | 8 +-- 4 files changed, 65 insertions(+), 12 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/member/NotificationEnabledUseCase.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/member/NotificationEnabledUseCase.kt index 528da08fc..109a91632 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/member/NotificationEnabledUseCase.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/member/NotificationEnabledUseCase.kt @@ -1,7 +1,6 @@ package com.droidblossom.archive.domain.usecase.member import android.util.Log -import com.droidblossom.archive.data.dto.member.request.FcmTokenRequsetDto import com.droidblossom.archive.data.dto.member.request.NotificationEnabledRequestDto import com.droidblossom.archive.domain.repository.MemberRepository import com.droidblossom.archive.util.RetrofitResult @@ -14,14 +13,14 @@ import javax.inject.Inject class NotificationEnabledUseCase @Inject constructor( private val repository: MemberRepository ) { - suspend operator fun invoke(request: NotificationEnabledRequestDto) = + suspend operator fun invoke(enabled: Boolean) = flow> { try { - emit(repository.patchNotificationEnabled(request) + emit(repository.patchNotificationEnabled(NotificationEnabledRequestDto(enabled)) .onSuccess { - + Log.d("알림", "성공") }.onFail { - + Log.d("알림", "실녀") }.onException { throw Exception(it) }) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingViewModel.kt index 3af8ceeef..5682061df 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingViewModel.kt @@ -2,11 +2,15 @@ package com.droidblossom.archive.presentation.ui.mypage.setting import com.droidblossom.archive.presentation.ui.skin.skinmake.SkinMakeViewModel import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.StateFlow interface SettingViewModel { val settingMainEvents: SharedFlow + val notificationEnable : StateFlow + val settingNotificationEvents : SharedFlow + fun back() fun goUser() fun goNotification() @@ -27,4 +31,9 @@ interface SettingViewModel { object GoLogout : SettingMainEvent() data class ShowToastMessage(val message : String) : SettingMainEvent() } + + sealed class SettingNotificationEvent { + object Back : SettingNotificationEvent() + data class ShowToastMessage(val message : String) : SettingNotificationEvent() + } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingNotificationFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingNotificationFragment.kt index 39bab7159..15797436c 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingNotificationFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingNotificationFragment.kt @@ -1,14 +1,29 @@ package com.droidblossom.archive.presentation.ui.mypage.setting.page import android.os.Bundle +import android.util.Log import android.view.View import androidx.fragment.app.viewModels +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import androidx.navigation.NavController +import androidx.navigation.Navigation import com.droidblossom.archive.R import com.droidblossom.archive.databinding.FragmentSettingNotificationBinding import com.droidblossom.archive.presentation.base.BaseFragment +import com.droidblossom.archive.presentation.ui.mypage.setting.SettingViewModel import com.droidblossom.archive.presentation.ui.mypage.setting.SettingViewModelImpl import com.droidblossom.archive.util.DataStoreUtils import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.async +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext +import okhttp3.internal.wait import javax.inject.Inject @AndroidEntryPoint @@ -16,16 +31,46 @@ class SettingNotificationFragment : BaseFragment(R.layout.fragment_setting_notification) { override val viewModel: SettingViewModelImpl by viewModels() - - @Inject - lateinit var dataStoreUtils: DataStoreUtils + lateinit var navController: NavController override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding.vm = viewModel + navController = Navigation.findNavController(view) + initView() + } + + private fun initView() { + viewModel.getNotificationEnable() + binding.backBtn.setOnClickListener { + viewModel.postNotificationEnable(binding.notificationSwitch.isChecked) + } } override fun observeData() { + viewLifecycleOwner.lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.notificationEnable.collect { enable -> + binding.notificationSwitch.isChecked = enable + } + } + } + viewLifecycleOwner.lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.settingNotificationEvents.collect { event -> + when (event) { + is SettingViewModel.SettingNotificationEvent.Back -> { + navController.popBackStack() + } + + is SettingViewModel.SettingNotificationEvent.ShowToastMessage -> { + showToastMessage(event.message) + } + else -> {} + } + } + } + } } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_setting_notification.xml b/frontend/ARchive/app/src/main/res/layout/fragment_setting_notification.xml index 57020221f..a98138e3b 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_setting_notification.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_setting_notification.xml @@ -20,7 +20,7 @@ android:id="@+id/settingNotiTitleT" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginTop="16dp" + android:layout_marginTop="12dp" android:text="알림 관리" android:textAppearance="@style/TextAppearance.App.body2" android:textColor="@color/black" @@ -34,13 +34,13 @@ android:layout_height="wrap_content" android:background="@android:color/transparent" android:paddingHorizontal="16dp" - android:paddingTop="16dp" + android:paddingTop="12dp" android:src="@drawable/ic_left_arrow_24" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> Date: Fri, 22 Mar 2024 00:43:30 +0900 Subject: [PATCH 029/301] =?UTF-8?q?feat:=20=20=EC=95=8C=EB=A6=BC=20?= =?UTF-8?q?=EC=84=B8=ED=8C=85=20=EC=9D=B4=EB=B2=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/mypage/setting/SettingMainFragment.kt | 7 ++- .../ui/mypage/setting/SettingViewModelImpl.kt | 51 ++++++++++++++++++- .../main/res/layout/fragment_setting_main.xml | 1 + .../main/res/navigation/nav_setting_graph.xml | 13 ++++- 4 files changed, 68 insertions(+), 4 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt index e40ad9206..7b2c05f43 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt @@ -7,6 +7,8 @@ import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle +import androidx.navigation.NavController +import androidx.navigation.Navigation import com.droidblossom.archive.R import com.droidblossom.archive.databinding.FragmentSettingMainBinding import com.droidblossom.archive.presentation.base.BaseFragment @@ -25,13 +27,14 @@ class SettingMainFragment : BaseFragment(R.layout.fragment_setting_main) { override val viewModel: SettingViewModelImpl by viewModels() - @Inject lateinit var dataStoreUtils: DataStoreUtils + lateinit var navController: NavController override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding.vm = viewModel + navController = Navigation.findNavController(view) } override fun observeData() { @@ -71,7 +74,7 @@ class SettingMainFragment : } SettingViewModel.SettingMainEvent.GoNotification -> { - + navController.navigate(R.id.action_settingMainFragment_to_settingNotificationFragment) } SettingViewModel.SettingMainEvent.GoUser -> { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingViewModelImpl.kt index 6b481dbc2..cb88a7931 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingViewModelImpl.kt @@ -1,18 +1,29 @@ package com.droidblossom.archive.presentation.ui.mypage.setting +import android.util.Log import androidx.lifecycle.viewModelScope +import com.droidblossom.archive.domain.usecase.member.NotificationEnabledUseCase import com.droidblossom.archive.presentation.base.BaseViewModel +import com.droidblossom.archive.presentation.ui.mypage.MyPageViewModel import com.droidblossom.archive.presentation.ui.skin.skinmake.SkinMakeViewModel +import com.droidblossom.archive.util.DataStoreUtils +import com.droidblossom.archive.util.onFail +import com.droidblossom.archive.util.onSuccess import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.async import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel class SettingViewModelImpl @Inject constructor( - + private val notificationEnabledUseCase: NotificationEnabledUseCase, + private val dataStoreUtils: DataStoreUtils ) : BaseViewModel(), SettingViewModel { //main @@ -20,6 +31,16 @@ class SettingViewModelImpl @Inject constructor( override val settingMainEvents: SharedFlow get() = _settingMainEvents.asSharedFlow() + //notification + private val _notificationEnable = MutableStateFlow(false) + override val notificationEnable: StateFlow + get() = _notificationEnable + + private val _settingNotificationEvent = MutableSharedFlow() + override val settingNotificationEvents: SharedFlow + get() = _settingNotificationEvent.asSharedFlow() + + override fun back() { viewModelScope.launch { _settingMainEvents.emit(SettingViewModel.SettingMainEvent.Back) @@ -67,4 +88,32 @@ class SettingViewModelImpl @Inject constructor( _settingMainEvents.emit(SettingViewModel.SettingMainEvent.GoLogout) } } + + //Notification Setting + fun getNotificationEnable() { + viewModelScope.launch { + _notificationEnable.emit(dataStoreUtils.fetchNotificationsEnabled()) + } + } + + fun postNotificationEnable(enabled: Boolean) { + viewModelScope.launch { + if (enabled xor notificationEnable.value) { + dataStoreUtils.saveNotificationsEnabled(enabled) + notificationEnabledUseCase(enabled).collect { result -> + result.onSuccess { + Log.d("알림변경", "성공") + _settingNotificationEvent.emit(SettingViewModel.SettingNotificationEvent.ShowToastMessage("알림 설정을 변경했습니다.")) + }.onFail { + Log.d("알림변경", "실패") + _settingNotificationEvent.emit(SettingViewModel.SettingNotificationEvent.ShowToastMessage("알림을 변경 실패.")) + + } + } + _settingNotificationEvent.emit(SettingViewModel.SettingNotificationEvent.Back) + } else { + _settingNotificationEvent.emit(SettingViewModel.SettingNotificationEvent.Back) + } + } + } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_setting_main.xml b/frontend/ARchive/app/src/main/res/layout/fragment_setting_main.xml index 01f73a128..fd463216c 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_setting_main.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_setting_main.xml @@ -100,6 +100,7 @@ android:layout_width="0dp" android:layout_height="60dp" android:layout_marginHorizontal="8dp" + android:onClick="@{()->vm.goNotification()}" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/userLayout"> diff --git a/frontend/ARchive/app/src/main/res/navigation/nav_setting_graph.xml b/frontend/ARchive/app/src/main/res/navigation/nav_setting_graph.xml index 994842919..a97abfac6 100644 --- a/frontend/ARchive/app/src/main/res/navigation/nav_setting_graph.xml +++ b/frontend/ARchive/app/src/main/res/navigation/nav_setting_graph.xml @@ -9,5 +9,16 @@ android:id="@+id/settingMainFragment" android:name="com.droidblossom.archive.presentation.ui.mypage.setting.SettingMainFragment" android:label="SettingMainFragment" - tools:layout="@layout/fragment_setting_main"/> + tools:layout="@layout/fragment_setting_main"> + + + + + \ No newline at end of file From a5a63636a3917d2fdcd39c98082be0f90a42cefa Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Fri, 22 Mar 2024 01:45:59 +0900 Subject: [PATCH 030/301] =?UTF-8?q?fix:=20backpress=20=EC=97=91=EC=85=98?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../createcapsule/CreateCapsule2Fragment.kt | 3 ++- .../page/SettingNotificationFragment.kt | 22 ++++++++++--------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsule2Fragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsule2Fragment.kt index b1b24b789..a12dd365f 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsule2Fragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsule2Fragment.kt @@ -47,7 +47,8 @@ class CreateCapsule2Fragment : if (viewModel.groupTypeInt != 1) { requireActivity().finish() } else { - super.remove() + isEnabled =false + requireActivity().onBackPressedDispatcher.onBackPressed() } } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingNotificationFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingNotificationFragment.kt index 15797436c..b3ebd38db 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingNotificationFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingNotificationFragment.kt @@ -1,8 +1,8 @@ package com.droidblossom.archive.presentation.ui.mypage.setting.page import android.os.Bundle -import android.util.Log import android.view.View +import androidx.activity.OnBackPressedCallback import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope @@ -14,17 +14,8 @@ import com.droidblossom.archive.databinding.FragmentSettingNotificationBinding import com.droidblossom.archive.presentation.base.BaseFragment import com.droidblossom.archive.presentation.ui.mypage.setting.SettingViewModel import com.droidblossom.archive.presentation.ui.mypage.setting.SettingViewModelImpl -import com.droidblossom.archive.util.DataStoreUtils import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Deferred -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.async import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.withContext -import okhttp3.internal.wait -import javax.inject.Inject @AndroidEntryPoint class SettingNotificationFragment : @@ -32,6 +23,7 @@ class SettingNotificationFragment : override val viewModel: SettingViewModelImpl by viewModels() lateinit var navController: NavController + private lateinit var callback: OnBackPressedCallback override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -42,9 +34,19 @@ class SettingNotificationFragment : private fun initView() { viewModel.getNotificationEnable() + binding.backBtn.setOnClickListener { viewModel.postNotificationEnable(binding.notificationSwitch.isChecked) } + + binding.notificationSwitch.setOnCheckedChangeListener { _, b -> + callback = object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + viewModel.postNotificationEnable(b) + } + } + requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, callback) + } } override fun observeData() { From 2f41e3664c92b3e7e6814c5007483f7aa2f85e7a Mon Sep 17 00:00:00 2001 From: comst19 Date: Fri, 22 Mar 2024 21:36:25 +0900 Subject: [PATCH 031/301] =?UTF-8?q?delete:=20fcm=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EC=95=8C=EB=A6=BC=20=ED=99=9C=EC=84=B1=ED=99=94=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/util/MyFirebaseMessagingService.kt | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/MyFirebaseMessagingService.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/MyFirebaseMessagingService.kt index 22addcf10..b7f61fd06 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/MyFirebaseMessagingService.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/MyFirebaseMessagingService.kt @@ -48,21 +48,13 @@ class MyFirebaseMessagingService : FirebaseMessagingService() { if (remoteMessage.data.isNotEmpty()){ EventBus.getDefault().post(remoteMessage.data) - CoroutineScope(Dispatchers.IO).launch { - val notificationsEnabled = dataStoreUtils.fetchNotificationsEnabled() + handleNotification(remoteMessage) - if (notificationsEnabled) { - handleNotification(remoteMessage) - } else { - Log.d(TAG, "사용자가 알림을 비활성화했습니다.") - } - } }else{ // 데이터 메시지 비어있음 Log.d(TAG, "메시지를 수신하지 못했습니다.") } - - + } private fun handleNotification(remoteMessage: RemoteMessage) { From 79389094b637611bd19c2dd12a2586c0c2200676 Mon Sep 17 00:00:00 2001 From: comst19 Date: Sat, 23 Mar 2024 19:14:12 +0900 Subject: [PATCH 032/301] feat: SkinMotion Data Class --- .../archive/domain/model/capsule_skin/SkinMotion.kt | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/capsule_skin/SkinMotion.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/capsule_skin/SkinMotion.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/capsule_skin/SkinMotion.kt new file mode 100644 index 000000000..164476980 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/capsule_skin/SkinMotion.kt @@ -0,0 +1,12 @@ +package com.droidblossom.archive.domain.model.capsule_skin + +import com.droidblossom.archive.util.Motion +import com.droidblossom.archive.util.Retarget + +data class SkinMotion( + val id : Long, + val motionUrl: String, + val motionName: Motion, + val retarget: Retarget, + var isClicked :Boolean +) From 50eef1a6d9fe6ebb06d9b7a18c9f4d73c06098bc Mon Sep 17 00:00:00 2001 From: comst19 Date: Sat, 23 Mar 2024 19:15:18 +0900 Subject: [PATCH 033/301] =?UTF-8?q?feat:=20SkinMotion=20Recyclerview=20?= =?UTF-8?q?=EC=85=8B=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/skin/adapter/SkinMotionRVA.kt | 51 +++++++++++++++++++ .../src/main/res/layout/item_skin_motion.xml | 50 ++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/adapter/SkinMotionRVA.kt create mode 100644 frontend/ARchive/app/src/main/res/layout/item_skin_motion.xml diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/adapter/SkinMotionRVA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/adapter/SkinMotionRVA.kt new file mode 100644 index 000000000..af12cf27c --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/adapter/SkinMotionRVA.kt @@ -0,0 +1,51 @@ +package com.droidblossom.archive.presentation.ui.skin.adapter + +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.droidblossom.archive.databinding.ItemSkinMotionBinding +import com.droidblossom.archive.domain.model.capsule_skin.SkinMotion + +class SkinMotionRVA : ListAdapter(differ) { + + inner class ItemViewHolder( + private val binding: ItemSkinMotionBinding + ) : RecyclerView.ViewHolder(binding.root) { + fun bind(data: SkinMotion) { + binding.item = data + } + } + + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): ItemViewHolder { + return ItemViewHolder( + ItemSkinMotionBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) + } + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + holder.bind(getItem(position)) + } + + + + companion object { + val differ = object : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: SkinMotion, newItem: SkinMotion): Boolean { + return oldItem.id == newItem.id + } + + override fun areContentsTheSame(oldItem: SkinMotion, newItem: SkinMotion): Boolean { + return oldItem == newItem + } + } + } +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/item_skin_motion.xml b/frontend/ARchive/app/src/main/res/layout/item_skin_motion.xml new file mode 100644 index 000000000..32f5f13c5 --- /dev/null +++ b/frontend/ARchive/app/src/main/res/layout/item_skin_motion.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file From bc2eb8ee0d747e4e26e9a5585dae16e4884110f5 Mon Sep 17 00:00:00 2001 From: comst19 Date: Sat, 23 Mar 2024 19:16:02 +0900 Subject: [PATCH 034/301] =?UTF-8?q?feat:=20skinMotions=20=EB=B3=80?= =?UTF-8?q?=EC=88=98=20=EC=84=A0=EC=96=B8=EA=B3=BC=20=EC=B4=88=EA=B8=B0?= =?UTF-8?q?=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/skin/skinmake/SkinMakeViewModel.kt | 2 + .../ui/skin/skinmake/SkinMakeViewModelImpl.kt | 49 ++++++++++++++++++- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModel.kt index baeceb39e..fa6dd39b4 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModel.kt @@ -1,6 +1,7 @@ package com.droidblossom.archive.presentation.ui.skin.skinmake import android.net.Uri +import com.droidblossom.archive.domain.model.capsule_skin.SkinMotion import com.droidblossom.archive.presentation.ui.auth.AuthViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow @@ -14,6 +15,7 @@ interface SkinMakeViewModel { val skinImgUri: MutableStateFlow val addMotion : StateFlow val skinImgFile: StateFlow + val skinMotions: MutableList fun skinMakeEvent(event: SkinMakeEvent) fun selectAddMotion() diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModelImpl.kt index 1224d1ceb..7723cdc41 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModelImpl.kt @@ -4,6 +4,7 @@ import android.net.Uri import android.util.Log import androidx.lifecycle.viewModelScope import com.droidblossom.archive.domain.model.capsule_skin.CapsuleSkinsMakeRequest +import com.droidblossom.archive.domain.model.capsule_skin.SkinMotion import com.droidblossom.archive.domain.model.s3.S3UrlRequest import com.droidblossom.archive.domain.usecase.capsule_skin.CapsuleSkinsMakeUseCase import com.droidblossom.archive.domain.usecase.s3.S3UrlsGetUseCase @@ -54,6 +55,52 @@ class SkinMakeViewModelImpl @Inject constructor( private val _skinImgFile = MutableStateFlow(null) override val skinImgFile: StateFlow = _skinImgFile + + override val skinMotions: MutableList = mutableListOf( + SkinMotion( + 1L, + "https://cdn.pixabay.com/animation/2022/10/11/09/05/09-05-26-529_512.gif", + Motion.JUMPING_JACKS, + Retarget.CMU, + false + ), + SkinMotion( + 2L, + "https://cdn.pixabay.com/animation/2022/10/11/09/05/09-05-26-529_512.gif", + Motion.DAB, + Retarget.FAIR, + false + ), + SkinMotion( + 3L, + "https://cdn.pixabay.com/animation/2022/10/11/09/05/09-05-26-529_512.gif", + Motion.JUMPING, + Retarget.FAIR, + false + ), + SkinMotion( + 4L, + "https://cdn.pixabay.com/animation/2022/10/11/09/05/09-05-26-529_512.gif", + Motion.WAVE_HELLO, + Retarget.FAIR, + false + ), + SkinMotion( + 5L, + "https://cdn.pixabay.com/animation/2022/10/11/09/05/09-05-26-529_512.gif", + Motion.ZOMBIE, + Retarget.FAIR, + false + ), + SkinMotion( + 6L, + "https://cdn.pixabay.com/animation/2022/10/11/09/05/09-05-26-529_512.gif", + Motion.JESSE_DANCE, + Retarget.ROKOKO, + false + ) + ) + override fun skinMakeEvent(event: SkinMakeViewModel.SkinMakeEvent) { viewModelScope.launch { _skinMakeEvents.emit(event) @@ -130,7 +177,7 @@ class SkinMakeViewModelImpl @Inject constructor( null, null ).toDto() - ).collect{ result -> + ).collect { result -> result.onSuccess { skinMakeEvent(SkinMakeViewModel.SkinMakeEvent.SuccessSkinMake) Log.d("스킨 생성", "생성 성공") From 521e885229fe2b42352512875523914b989b2f7e Mon Sep 17 00:00:00 2001 From: comst19 Date: Sat, 23 Mar 2024 19:18:55 +0900 Subject: [PATCH 035/301] =?UTF-8?q?feat:=20skinMotion=20=EB=A6=AC=EC=82=AC?= =?UTF-8?q?=EC=9D=B4=ED=81=B4=EB=9F=AC=EB=B7=B0=EC=97=90=20=EC=97=B0?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/skin/skinmake/SkinMakeFragment.kt | 16 ++++++++----- .../main/res/layout/fragment_skin_make.xml | 24 ++++++++++--------- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeFragment.kt index 990f46953..9fca9e20a 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeFragment.kt @@ -14,13 +14,8 @@ import androidx.navigation.NavController import androidx.navigation.Navigation import com.droidblossom.archive.R import com.droidblossom.archive.databinding.FragmentSkinMakeBinding -import com.droidblossom.archive.domain.model.common.FileName -import com.droidblossom.archive.domain.model.s3.S3UrlRequest import com.droidblossom.archive.presentation.base.BaseFragment -import com.droidblossom.archive.presentation.ui.MainActivity -import com.droidblossom.archive.presentation.ui.home.createcapsule.CreateCapsuleActivity -import com.droidblossom.archive.presentation.ui.home.createcapsule.CreateCapsuleViewModel -import com.droidblossom.archive.presentation.ui.home.createcapsule.dialog.DatePickerDialogFragment +import com.droidblossom.archive.presentation.ui.skin.adapter.SkinMotionRVA import com.droidblossom.archive.util.FileUtils import com.droidblossom.archive.util.S3Util import dagger.hilt.android.AndroidEntryPoint @@ -40,6 +35,10 @@ class SkinMakeFragment : BaseFragment if (uri != null){ viewModel.skinImgUri.value = uri @@ -72,6 +71,7 @@ class SkinMakeFragment : BaseFragment + android:layout_height="match_parent" + android:paddingVertical="20dp"> @@ -167,26 +166,29 @@ android:id="@+id/imagePlusBtn" android:layout_width="24dp" android:layout_height="24dp" - android:layout_marginEnd="18dp" android:background="@drawable/corner_radius_12" android:backgroundTint="@color/main_2" android:tint="@color/white" + android:layout_marginEnd="20dp" bind:minus_plus="@{vm.addMotion}" - app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintTop_toTopOf="@id/addMotionTextView" /> + app:layout_constraintTop_toBottomOf="@id/addMotionTextView" + app:spanCount="3"/> From 4d78d6af0e9b3e9323bc5f60ee46081e43f88b10 Mon Sep 17 00:00:00 2001 From: comst19 Date: Sat, 23 Mar 2024 22:48:03 +0900 Subject: [PATCH 036/301] =?UTF-8?q?refact:=20skinMotions=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=20MutableList=20->=20MutableState>?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/skin/skinmake/SkinMakeViewModel.kt | 5 +- .../ui/skin/skinmake/SkinMakeViewModelImpl.kt | 118 +++++++++++------- 2 files changed, 78 insertions(+), 45 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModel.kt index fa6dd39b4..685818fc5 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModel.kt @@ -15,13 +15,16 @@ interface SkinMakeViewModel { val skinImgUri: MutableStateFlow val addMotion : StateFlow val skinImgFile: StateFlow - val skinMotions: MutableList + val skinMotions: MutableStateFlow> + val skinMotionIndex: StateFlow fun skinMakeEvent(event: SkinMakeEvent) fun selectAddMotion() fun setFile(skinImgFile: File) fun makeSkin() + fun selectSkinMotion(skinMotion : SkinMotion) + sealed class SkinMakeEvent { object SuccessSkinMake : SkinMakeEvent() data class ShowToastMessage(val message : String) : SkinMakeEvent() diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModelImpl.kt index 7723cdc41..4a26a4ab0 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModelImpl.kt @@ -56,51 +56,70 @@ class SkinMakeViewModelImpl @Inject constructor( private val _skinImgFile = MutableStateFlow(null) override val skinImgFile: StateFlow = _skinImgFile - override val skinMotions: MutableList = mutableListOf( - SkinMotion( - 1L, - "https://cdn.pixabay.com/animation/2022/10/11/09/05/09-05-26-529_512.gif", - Motion.JUMPING_JACKS, - Retarget.CMU, - false - ), - SkinMotion( - 2L, - "https://cdn.pixabay.com/animation/2022/10/11/09/05/09-05-26-529_512.gif", - Motion.DAB, - Retarget.FAIR, - false - ), - SkinMotion( - 3L, - "https://cdn.pixabay.com/animation/2022/10/11/09/05/09-05-26-529_512.gif", - Motion.JUMPING, - Retarget.FAIR, - false - ), - SkinMotion( - 4L, - "https://cdn.pixabay.com/animation/2022/10/11/09/05/09-05-26-529_512.gif", - Motion.WAVE_HELLO, - Retarget.FAIR, - false - ), - SkinMotion( - 5L, - "https://cdn.pixabay.com/animation/2022/10/11/09/05/09-05-26-529_512.gif", - Motion.ZOMBIE, - Retarget.FAIR, - false - ), - SkinMotion( - 6L, - "https://cdn.pixabay.com/animation/2022/10/11/09/05/09-05-26-529_512.gif", - Motion.JESSE_DANCE, - Retarget.ROKOKO, - false + private val _skinMotions = MutableStateFlow>( + listOf( + SkinMotion( + 1L, + "https://cdn.pixabay.com/animation/2022/10/11/09/05/09-05-26-529_512.gif", + Motion.JUMPING_JACKS, + Retarget.CMU, + false + ), + SkinMotion( + 2L, + "https://cdn.pixabay.com/animation/2022/10/11/09/05/09-05-26-529_512.gif", + Motion.DAB, + Retarget.FAIR, + false + ), + SkinMotion( + 3L, + "https://cdn.pixabay.com/animation/2022/10/11/09/05/09-05-26-529_512.gif", + Motion.JUMPING, + Retarget.FAIR, + false + ), + SkinMotion( + 4L, + "https://cdn.pixabay.com/animation/2022/10/11/09/05/09-05-26-529_512.gif", + Motion.WAVE_HELLO, + Retarget.FAIR, + false + ), + SkinMotion( + 5L, + "https://cdn.pixabay.com/animation/2022/10/11/09/05/09-05-26-529_512.gif", + Motion.ZOMBIE, + Retarget.FAIR, + false + ), + SkinMotion( + 6L, + "https://cdn.pixabay.com/animation/2022/10/11/09/05/09-05-26-529_512.gif", + Motion.JESSE_DANCE, + Retarget.ROKOKO, + false + ) ) ) + override val skinMotions: MutableStateFlow> + get() = _skinMotions + private val _skinMotionIndex = MutableStateFlow(-1) + override val skinMotionIndex: StateFlow + get() = _skinMotionIndex + override fun selectSkinMotion(skinMotion: SkinMotion) { + viewModelScope.launch { + val newList = skinMotions.value.map { existingSkinMotion -> + if (existingSkinMotion == skinMotion) { + existingSkinMotion.copy(isClicked = true) + } else { + existingSkinMotion.copy(isClicked = false) + } + } + _skinMotions.emit(newList) + } + } override fun skinMakeEvent(event: SkinMakeViewModel.SkinMakeEvent) { viewModelScope.launch { _skinMakeEvents.emit(event) @@ -111,6 +130,13 @@ class SkinMakeViewModelImpl @Inject constructor( override fun selectAddMotion() { viewModelScope.launch { + if (addMotion.value) { + val newList = skinMotions.value.map { skinMotion -> + skinMotion.copy(isClicked = false) + } + _skinMotions.emit(newList) + _skinMotionIndex.emit(-1) + } _addMotion.emit(!addMotion.value) } } @@ -168,14 +194,18 @@ class SkinMakeViewModelImpl @Inject constructor( } private fun submitSkin() { + if (addMotion.value) { + val skinMotion = skinMotions.value.find { it.isClicked } + _skinMotionIndex.value = if (skinMotion != null) skinMotions.value.indexOf(skinMotion) else -1 + } viewModelScope.launch { capsuleSkinsMakeUseCase( CapsuleSkinsMakeRequest( skinName.value, skinImgFile.value!!.name, S3DIRECTORY, - null, - null + skinMotions.value.getOrNull(skinMotionIndex.value)?.motionName, + skinMotions.value.getOrNull(skinMotionIndex.value)?.retarget ).toDto() ).collect { result -> result.onSuccess { From f23a1a768a3c202165c6bc982bb23baddcb61ac7 Mon Sep 17 00:00:00 2001 From: comst19 Date: Sat, 23 Mar 2024 22:49:22 +0900 Subject: [PATCH 037/301] =?UTF-8?q?feat:=20SkinMotionRVA=EC=97=90=20click?= =?UTF-8?q?=20event=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ui/skin/adapter/SkinMotionRVA.kt | 5 ++++- .../ui/skin/skinmake/SkinMakeFragment.kt | 12 ++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/adapter/SkinMotionRVA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/adapter/SkinMotionRVA.kt index af12cf27c..7406fa923 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/adapter/SkinMotionRVA.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/adapter/SkinMotionRVA.kt @@ -8,13 +8,16 @@ import androidx.recyclerview.widget.RecyclerView import com.droidblossom.archive.databinding.ItemSkinMotionBinding import com.droidblossom.archive.domain.model.capsule_skin.SkinMotion -class SkinMotionRVA : ListAdapter(differ) { +class SkinMotionRVA(val ItemClick: (SkinMotion) -> Unit) : ListAdapter(differ) { inner class ItemViewHolder( private val binding: ItemSkinMotionBinding ) : RecyclerView.ViewHolder(binding.root) { fun bind(data: SkinMotion) { binding.item = data + binding.root.setOnClickListener { + ItemClick(data) + } } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeFragment.kt index 9fca9e20a..574025b86 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeFragment.kt @@ -21,6 +21,7 @@ import com.droidblossom.archive.util.S3Util import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.filter import kotlinx.coroutines.launch import java.text.SimpleDateFormat import java.util.Date @@ -36,7 +37,7 @@ class SkinMakeFragment : BaseFragment @@ -63,6 +64,14 @@ class SkinMakeFragment : BaseFragment Date: Mon, 25 Mar 2024 18:03:02 +0900 Subject: [PATCH 038/301] =?UTF-8?q?feat:=20=EC=9D=B4=EC=9A=A9=EC=95=BD?= =?UTF-8?q?=EA=B4=80=20init?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../setting/page/SettingAgreeFragment.kt | 24 +++++++++++++++++++ .../res/layout/fragment_setting_agree.xml | 19 +++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingAgreeFragment.kt create mode 100644 frontend/ARchive/app/src/main/res/layout/fragment_setting_agree.xml diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingAgreeFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingAgreeFragment.kt new file mode 100644 index 000000000..131c5acad --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingAgreeFragment.kt @@ -0,0 +1,24 @@ +package com.droidblossom.archive.presentation.ui.mypage.setting.page + +import android.os.Bundle +import android.view.View +import androidx.fragment.app.viewModels +import com.droidblossom.archive.R +import com.droidblossom.archive.databinding.FragmentSettingAgreeBinding +import com.droidblossom.archive.presentation.base.BaseFragment +import com.droidblossom.archive.presentation.ui.mypage.setting.SettingViewModelImpl + +class SettingAgreeFragment : + BaseFragment(R.layout.fragment_setting_agree) { + + override val viewModel: SettingViewModelImpl by viewModels() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.vm = viewModel + } + + override fun observeData() { + + } +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_setting_agree.xml b/frontend/ARchive/app/src/main/res/layout/fragment_setting_agree.xml new file mode 100644 index 000000000..31371625d --- /dev/null +++ b/frontend/ARchive/app/src/main/res/layout/fragment_setting_agree.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + \ No newline at end of file From 029648d9d5c9b866c5c42bdd8129cf0f5d47aa74 Mon Sep 17 00:00:00 2001 From: comst19 Date: Mon, 25 Mar 2024 23:07:21 +0900 Subject: [PATCH 039/301] =?UTF-8?q?refact:=20=EB=A6=AC=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20=EC=B2=98=EB=A6=AC=20=EB=B0=A9=EC=8B=9D=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/ARchive/app/build.gradle | 1 + .../home/createcapsule/CreateCapsuleViewModelImpl.kt | 12 ++++++++---- .../ui/home/createcapsule/adapter/SkinRVA.kt | 7 ------- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/frontend/ARchive/app/build.gradle b/frontend/ARchive/app/build.gradle index 340099232..4ab71d75e 100644 --- a/frontend/ARchive/app/build.gradle +++ b/frontend/ARchive/app/build.gradle @@ -182,6 +182,7 @@ dependencies { // oss-licenses : https://github.com/google/play-services-plugins/tree/master/oss-licenses-plugin implementation 'com.google.android.gms:play-services-oss-licenses:17.0.1' + } kapt { correctErrorTypes true diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt index a2562e838..846918be2 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt @@ -237,11 +237,15 @@ class CreateCapsuleViewModelImpl @Inject constructor( } override fun changeSkin(skin: CapsuleSkinSummary) { - val submitList = skins.value - submitList.map { it.isClicked = false } - submitList[submitList.indexOf(skin)].isClicked = true viewModelScope.launch { - _skins.emit(submitList) + val newList = skins.value.map { existingSkin -> + if (existingSkin == skin){ + existingSkin.copy(isClicked = true) + }else{ + existingSkin.copy(isClicked = false) + } + } + _skins.emit(newList) } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/adapter/SkinRVA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/adapter/SkinRVA.kt index 92a4d8645..9efd5a782 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/adapter/SkinRVA.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/adapter/SkinRVA.kt @@ -18,17 +18,10 @@ class SkinRVA( val SkinFlow: (CapsuleSkinSummary) -> Unit) : private val binding: ItemSkinBinding ) : RecyclerView.ViewHolder(binding.root) { - //@SuppressLint("NotifyDataSetChanged") fun bind(data: CapsuleSkinSummary) { binding.item = data binding.root.setOnClickListener { - notifyItemChanged(currentList.indexOf( - currentList.find { - it.isClicked - } - )) SkinFlow(data) - notifyItemChanged(currentList.indexOf(data)) } } } From ae06d1f40b32e5cf568fb328e404c25301ed66e3 Mon Sep 17 00:00:00 2001 From: comst19 Date: Mon, 25 Mar 2024 23:22:15 +0900 Subject: [PATCH 040/301] =?UTF-8?q?Revert=20"refact:=20=EB=A6=AC=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=B2=98=EB=A6=AC=20=EB=B0=A9=EC=8B=9D=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 029648d9d5c9b866c5c42bdd8129cf0f5d47aa74. --- frontend/ARchive/app/build.gradle | 1 - .../home/createcapsule/CreateCapsuleViewModelImpl.kt | 12 ++++-------- .../ui/home/createcapsule/adapter/SkinRVA.kt | 7 +++++++ 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/frontend/ARchive/app/build.gradle b/frontend/ARchive/app/build.gradle index 4ab71d75e..340099232 100644 --- a/frontend/ARchive/app/build.gradle +++ b/frontend/ARchive/app/build.gradle @@ -182,7 +182,6 @@ dependencies { // oss-licenses : https://github.com/google/play-services-plugins/tree/master/oss-licenses-plugin implementation 'com.google.android.gms:play-services-oss-licenses:17.0.1' - } kapt { correctErrorTypes true diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt index 846918be2..a2562e838 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt @@ -237,15 +237,11 @@ class CreateCapsuleViewModelImpl @Inject constructor( } override fun changeSkin(skin: CapsuleSkinSummary) { + val submitList = skins.value + submitList.map { it.isClicked = false } + submitList[submitList.indexOf(skin)].isClicked = true viewModelScope.launch { - val newList = skins.value.map { existingSkin -> - if (existingSkin == skin){ - existingSkin.copy(isClicked = true) - }else{ - existingSkin.copy(isClicked = false) - } - } - _skins.emit(newList) + _skins.emit(submitList) } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/adapter/SkinRVA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/adapter/SkinRVA.kt index 9efd5a782..92a4d8645 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/adapter/SkinRVA.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/adapter/SkinRVA.kt @@ -18,10 +18,17 @@ class SkinRVA( val SkinFlow: (CapsuleSkinSummary) -> Unit) : private val binding: ItemSkinBinding ) : RecyclerView.ViewHolder(binding.root) { + //@SuppressLint("NotifyDataSetChanged") fun bind(data: CapsuleSkinSummary) { binding.item = data binding.root.setOnClickListener { + notifyItemChanged(currentList.indexOf( + currentList.find { + it.isClicked + } + )) SkinFlow(data) + notifyItemChanged(currentList.indexOf(data)) } } } From 2038f1d67897652175057544a9f6568db0c12001 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Mon, 25 Mar 2024 23:26:49 +0900 Subject: [PATCH 041/301] =?UTF-8?q?Revert=20"Revert=20"refact:=20=EB=A6=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=B2=98=EB=A6=AC=20=EB=B0=A9=EC=8B=9D=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0""?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit ae06d1f40b32e5cf568fb328e404c25301ed66e3. --- frontend/ARchive/app/build.gradle | 1 + .../home/createcapsule/CreateCapsuleViewModelImpl.kt | 12 ++++++++---- .../ui/home/createcapsule/adapter/SkinRVA.kt | 7 ------- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/frontend/ARchive/app/build.gradle b/frontend/ARchive/app/build.gradle index 340099232..4ab71d75e 100644 --- a/frontend/ARchive/app/build.gradle +++ b/frontend/ARchive/app/build.gradle @@ -182,6 +182,7 @@ dependencies { // oss-licenses : https://github.com/google/play-services-plugins/tree/master/oss-licenses-plugin implementation 'com.google.android.gms:play-services-oss-licenses:17.0.1' + } kapt { correctErrorTypes true diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt index a2562e838..846918be2 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt @@ -237,11 +237,15 @@ class CreateCapsuleViewModelImpl @Inject constructor( } override fun changeSkin(skin: CapsuleSkinSummary) { - val submitList = skins.value - submitList.map { it.isClicked = false } - submitList[submitList.indexOf(skin)].isClicked = true viewModelScope.launch { - _skins.emit(submitList) + val newList = skins.value.map { existingSkin -> + if (existingSkin == skin){ + existingSkin.copy(isClicked = true) + }else{ + existingSkin.copy(isClicked = false) + } + } + _skins.emit(newList) } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/adapter/SkinRVA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/adapter/SkinRVA.kt index 92a4d8645..9efd5a782 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/adapter/SkinRVA.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/adapter/SkinRVA.kt @@ -18,17 +18,10 @@ class SkinRVA( val SkinFlow: (CapsuleSkinSummary) -> Unit) : private val binding: ItemSkinBinding ) : RecyclerView.ViewHolder(binding.root) { - //@SuppressLint("NotifyDataSetChanged") fun bind(data: CapsuleSkinSummary) { binding.item = data binding.root.setOnClickListener { - notifyItemChanged(currentList.indexOf( - currentList.find { - it.isClicked - } - )) SkinFlow(data) - notifyItemChanged(currentList.indexOf(data)) } } } From 8f41ac0e8c64c412f300c7d2d81429ca6f0bb28d Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Mon, 25 Mar 2024 23:27:35 +0900 Subject: [PATCH 042/301] =?UTF-8?q?Revert=20"Revert=20"Revert=20"refact:?= =?UTF-8?q?=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EC=B2=98=EB=A6=AC=20=EB=B0=A9?= =?UTF-8?q?=EC=8B=9D=20=EA=B0=9C=EC=84=A0"""?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 2038f1d67897652175057544a9f6568db0c12001. --- frontend/ARchive/app/build.gradle | 1 - .../home/createcapsule/CreateCapsuleViewModelImpl.kt | 12 ++++-------- .../ui/home/createcapsule/adapter/SkinRVA.kt | 7 +++++++ 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/frontend/ARchive/app/build.gradle b/frontend/ARchive/app/build.gradle index 4ab71d75e..340099232 100644 --- a/frontend/ARchive/app/build.gradle +++ b/frontend/ARchive/app/build.gradle @@ -182,7 +182,6 @@ dependencies { // oss-licenses : https://github.com/google/play-services-plugins/tree/master/oss-licenses-plugin implementation 'com.google.android.gms:play-services-oss-licenses:17.0.1' - } kapt { correctErrorTypes true diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt index 846918be2..a2562e838 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt @@ -237,15 +237,11 @@ class CreateCapsuleViewModelImpl @Inject constructor( } override fun changeSkin(skin: CapsuleSkinSummary) { + val submitList = skins.value + submitList.map { it.isClicked = false } + submitList[submitList.indexOf(skin)].isClicked = true viewModelScope.launch { - val newList = skins.value.map { existingSkin -> - if (existingSkin == skin){ - existingSkin.copy(isClicked = true) - }else{ - existingSkin.copy(isClicked = false) - } - } - _skins.emit(newList) + _skins.emit(submitList) } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/adapter/SkinRVA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/adapter/SkinRVA.kt index 9efd5a782..92a4d8645 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/adapter/SkinRVA.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/adapter/SkinRVA.kt @@ -18,10 +18,17 @@ class SkinRVA( val SkinFlow: (CapsuleSkinSummary) -> Unit) : private val binding: ItemSkinBinding ) : RecyclerView.ViewHolder(binding.root) { + //@SuppressLint("NotifyDataSetChanged") fun bind(data: CapsuleSkinSummary) { binding.item = data binding.root.setOnClickListener { + notifyItemChanged(currentList.indexOf( + currentList.find { + it.isClicked + } + )) SkinFlow(data) + notifyItemChanged(currentList.indexOf(data)) } } } From 3d699c05c63cd9c613bcc68a90d9310f610d8c7a Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Tue, 26 Mar 2024 01:12:47 +0900 Subject: [PATCH 043/301] =?UTF-8?q?feat:=20=EC=A0=95=EC=B1=85=20=EB=A6=AC?= =?UTF-8?q?=EC=82=AC=EC=9D=B4=ED=81=B4=EB=9F=AC=20=EB=B7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/domain/model/setting/Agree.kt | 7 ++ .../ui/mypage/setting/adapter/AgreeRVA.kt | 69 ++++++++++++++++++ .../app/src/main/res/layout/item_agree.xml | 72 +++++++++++++++++++ 3 files changed, 148 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/setting/Agree.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/adapter/AgreeRVA.kt create mode 100644 frontend/ARchive/app/src/main/res/layout/item_agree.xml diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/setting/Agree.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/setting/Agree.kt new file mode 100644 index 000000000..693c3f7e4 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/setting/Agree.kt @@ -0,0 +1,7 @@ +package com.droidblossom.archive.domain.model.setting + +data class Agree( + val title : String, + val content : String, + var isOpen : Boolean = false, +) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/adapter/AgreeRVA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/adapter/AgreeRVA.kt new file mode 100644 index 000000000..d9a4a8390 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/adapter/AgreeRVA.kt @@ -0,0 +1,69 @@ +package com.droidblossom.archive.presentation.ui.mypage.setting.adapter + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.core.view.isGone +import androidx.core.view.isVisible +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import com.droidblossom.archive.R +import com.droidblossom.archive.databinding.ItemAgreeBinding +import com.droidblossom.archive.domain.model.setting.Agree + + +class AgreeRVA : + ListAdapter(differ) { + + inner class ItemViewHolder( + private val binding: ItemAgreeBinding + ) : RecyclerView.ViewHolder(binding.root) { + @SuppressLint("ClickableViewAccessibility") + fun bind(data: Agree) { + binding.item = data + binding.moreBtn.setOnClickListener { + if (data.isOpen) { + data.isOpen = false + binding.contentSV.isGone = true + binding.moreBtn.setImageResource(R.drawable.ic_plus_24) + } else { + data.isOpen = true + binding.contentSV.isVisible = true + binding.moreBtn.setImageResource(R.drawable.ic_minus_24) + } + } + } + + } + + + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): ItemViewHolder { + return ItemViewHolder( + ItemAgreeBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) + } + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + holder.bind(getItem(position)) + } + + companion object { + val differ = object : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: Agree, newItem: Agree): Boolean { + return oldItem == newItem + } + + override fun areContentsTheSame(oldItem: Agree, newItem: Agree): Boolean { + return oldItem == newItem + } + } + } +} diff --git a/frontend/ARchive/app/src/main/res/layout/item_agree.xml b/frontend/ARchive/app/src/main/res/layout/item_agree.xml new file mode 100644 index 000000000..4e0a6834a --- /dev/null +++ b/frontend/ARchive/app/src/main/res/layout/item_agree.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From af9feeff4bef5c71b71cc24d8ace09e2aecdeb90 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Tue, 26 Mar 2024 01:13:24 +0900 Subject: [PATCH 044/301] =?UTF-8?q?feat:=20=EC=84=B8=ED=8C=85=20=EB=84=A4?= =?UTF-8?q?=EB=B9=84=EC=97=90=20=EC=A0=95=EC=B1=85=20=ED=94=84=EB=A0=88?= =?UTF-8?q?=EA=B7=B8=EB=A8=BC=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/mypage/setting/SettingMainFragment.kt | 2 +- .../app/src/main/res/layout/fragment_setting_main.xml | 1 + .../app/src/main/res/navigation/nav_setting_graph.xml | 10 +++++++++- frontend/ARchive/app/src/main/res/values/strings.xml | 11 +++++++++++ 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt index 7b2c05f43..bd27b862d 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt @@ -47,7 +47,7 @@ class SettingMainFragment : } SettingViewModel.SettingMainEvent.GoAgree -> { - + navController.navigate(R.id.action_settingMainFragment_to_settingAgreeFragment) } SettingViewModel.SettingMainEvent.GoInquire -> { diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_setting_main.xml b/frontend/ARchive/app/src/main/res/layout/fragment_setting_main.xml index fd463216c..ba662dde0 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_setting_main.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_setting_main.xml @@ -209,6 +209,7 @@ android:layout_width="0dp" android:layout_height="60dp" android:layout_marginHorizontal="8dp" + android:onClick="@{()->vm.goAgree()}" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/noticeLayout"> diff --git a/frontend/ARchive/app/src/main/res/navigation/nav_setting_graph.xml b/frontend/ARchive/app/src/main/res/navigation/nav_setting_graph.xml index a97abfac6..adeb2ffbc 100644 --- a/frontend/ARchive/app/src/main/res/navigation/nav_setting_graph.xml +++ b/frontend/ARchive/app/src/main/res/navigation/nav_setting_graph.xml @@ -13,12 +13,20 @@ + + tools:layout="@layout/fragment_setting_notification" /> + \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/values/strings.xml b/frontend/ARchive/app/src/main/res/values/strings.xml index b244f3cc3..d213d9c37 100644 --- a/frontend/ARchive/app/src/main/res/values/strings.xml +++ b/frontend/ARchive/app/src/main/res/values/strings.xml @@ -30,4 +30,15 @@ 나만의 스킨이 탄생했어요! 나만의 스킨이 곧 탄생해요! + +  개인정보처리자는 다음의 어느 하나에 해당하는 경우에는 개인정보를 수집할 수 있으며 그 수집 목적의 범위에서 이용할 수 있습니다(「개인정보 보호법」 제15조제1항). + 정보주체의 동의를 받은 경우 + 법률에 특별한 규정이 있거나 법령상 의무를 준수하기 위하여 불가피한 경우 + 공공기관이 법령 등에서 정하는 소관 업무의 수행을 위하여 불가피한 경우 + 정보주체와의 계약의 체결한 계약을 이행하거나 계약을 체결하는 과정에서 정보주체의 요청에 따른 조치를 이행하기 위해 필요한 경우 + 명백히 정보주체 또는 제3자의 급박한 생명, 신체, 재산의 이익을 위하여 필요하다고 인정되는 경우 + 개인정보처리자의 정당한 이익을 달성하기 위하여 필요한 경우로서 명백하게 정보주체의 권리보다 우선하는 경우. 이 경우 개인정보처리자의 정당한 이익과 상당한 관련이 있고 합리적인 범위를 초과하지 않은 경우에 한함 + 공중위생 등 공공의 안전과 안녕을 위해 긴급히 필요한 경우 +※ 이를 위반하여 개인정보를 수집한 자는 개인정보 보호위원회에게 전체 매출액의 100분의 3을 초과하지 않는 범위에서 과징금을 부과 받을 수 있습니다(「개인정보 보호법」 제64조의2제1항제1호). + \ No newline at end of file From c80a1d85370abd4a77506b0fea9c8e11c803a1d8 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Tue, 26 Mar 2024 01:13:46 +0900 Subject: [PATCH 045/301] =?UTF-8?q?feat:=20=EC=A0=95=EC=B2=B5=20=EC=97=91?= =?UTF-8?q?=ED=8B=B0=EB=B9=84=ED=8B=B0=20=EC=99=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../setting/page/SettingAgreeFragment.kt | 27 ++++++++++++ .../res/layout/fragment_setting_agree.xml | 42 +++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingAgreeFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingAgreeFragment.kt index 131c5acad..eaaa0181c 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingAgreeFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingAgreeFragment.kt @@ -3,19 +3,46 @@ package com.droidblossom.archive.presentation.ui.mypage.setting.page import android.os.Bundle import android.view.View import androidx.fragment.app.viewModels +import androidx.navigation.NavController +import androidx.navigation.Navigation import com.droidblossom.archive.R import com.droidblossom.archive.databinding.FragmentSettingAgreeBinding +import com.droidblossom.archive.domain.model.setting.Agree import com.droidblossom.archive.presentation.base.BaseFragment import com.droidblossom.archive.presentation.ui.mypage.setting.SettingViewModelImpl +import com.droidblossom.archive.presentation.ui.mypage.setting.adapter.AgreeRVA +import dagger.hilt.android.AndroidEntryPoint +@AndroidEntryPoint class SettingAgreeFragment : BaseFragment(R.layout.fragment_setting_agree) { override val viewModel: SettingViewModelImpl by viewModels() + lateinit var navController: NavController + + private val agreeAdapter by lazy { + AgreeRVA() + } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding.vm = viewModel + navController = Navigation.findNavController(view) + initRVA() + } + + private fun initRVA() { + binding.adapter.adapter = agreeAdapter + agreeAdapter.submitList( + listOf( + Agree("이용약관 1장", getString(R.string.agree_content)), + Agree("이용약관 2장", getString(R.string.agree_content)), + Agree("사용자 정보 정책", getString(R.string.agree_content)), + ) + ) + binding.backBtn.setOnClickListener { + navController.popBackStack() + } } override fun observeData() { diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_setting_agree.xml b/frontend/ARchive/app/src/main/res/layout/fragment_setting_agree.xml index 31371625d..b41fbd0b0 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_setting_agree.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_setting_agree.xml @@ -15,5 +15,47 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/white"> + + + + + + + \ No newline at end of file From f5db71057ce5601e6b59073c388578be55080921 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Tue, 26 Mar 2024 02:06:13 +0900 Subject: [PATCH 046/301] =?UTF-8?q?feat:=20=EB=AC=B8=EC=9D=98=ED=95=98?= =?UTF-8?q?=EA=B8=B0=20(=EB=A9=94=EC=9D=BC=EB=A1=9C=20=EC=97=B0=EA=B2=B0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/mypage/setting/SettingMainFragment.kt | 44 ++++++++++++++++--- .../main/res/layout/fragment_setting_main.xml | 1 + 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt index bd27b862d..05ff802f3 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt @@ -1,6 +1,8 @@ package com.droidblossom.archive.presentation.ui.mypage.setting import android.content.Intent +import android.content.pm.PackageManager +import android.net.Uri import android.os.Bundle import android.view.View import androidx.fragment.app.viewModels @@ -22,11 +24,13 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import javax.inject.Inject + @AndroidEntryPoint class SettingMainFragment : BaseFragment(R.layout.fragment_setting_main) { override val viewModel: SettingViewModelImpl by viewModels() + @Inject lateinit var dataStoreUtils: DataStoreUtils lateinit var navController: NavController @@ -39,9 +43,9 @@ class SettingMainFragment : override fun observeData() { viewLifecycleOwner.lifecycleScope.launch { - repeatOnLifecycle(Lifecycle.State.STARTED){ - viewModel.settingMainEvents.collect{ event-> - when(event){ + repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.settingMainEvents.collect { event -> + when (event) { SettingViewModel.SettingMainEvent.Back -> { (activity as SettingActivity).finish() } @@ -51,16 +55,21 @@ class SettingMainFragment : } SettingViewModel.SettingMainEvent.GoInquire -> { - + goEmailIntent() } SettingViewModel.SettingMainEvent.GoLicenses -> { - startActivity(Intent(requireContext(), OssLicensesMenuActivity::class.java)) + startActivity( + Intent( + requireContext(), + OssLicensesMenuActivity::class.java + ) + ) OssLicensesMenuActivity.setActivityTitle("오픈소스 라이선스") } SettingViewModel.SettingMainEvent.GoLogout -> { - val sheet = CommonDialogFragment.newIntent("정말 로그아웃 하시겠습니까?","로그아웃") { + val sheet = CommonDialogFragment.newIntent("정말 로그아웃 하시겠습니까?", "로그아웃") { CoroutineScope(Dispatchers.IO).launch { dataStoreUtils.resetTokenData() } @@ -91,4 +100,27 @@ class SettingMainFragment : } } } + + private fun goEmailIntent() { + val emailSelectorIntent = Intent(Intent.ACTION_SENDTO) + emailSelectorIntent.data = Uri.parse("mailto:") + + val address = arrayOf("ARchive@address.com") + + val emailIntent = Intent(Intent.ACTION_SEND) + emailIntent.putExtra(Intent.EXTRA_EMAIL, address) + emailIntent.putExtra(Intent.EXTRA_SUBJECT, "") + emailIntent.putExtra(Intent.EXTRA_TEXT, "") + + emailIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + emailIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION) + emailIntent.selector = emailSelectorIntent + + startActivity(emailIntent) +// if (emailIntent.resolveActivity((activity as SettingActivity).packageManager) != null) { +// startActivity(emailIntent) +// } else { +// showToastMessage("메일을 연결할 앱이 없습니다.") +// } + } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_setting_main.xml b/frontend/ARchive/app/src/main/res/layout/fragment_setting_main.xml index ba662dde0..0c26e20a2 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_setting_main.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_setting_main.xml @@ -264,6 +264,7 @@ android:layout_width="0dp" android:layout_height="60dp" android:layout_marginHorizontal="8dp" + android:onClick="@{()->vm.goInquire()}" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/agreeLayout"> From 3d5d6f8f9d8b0aaddf02b4a083ad18831babaf7b Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Tue, 26 Mar 2024 02:47:21 +0900 Subject: [PATCH 047/301] =?UTF-8?q?feat:=20=EA=B3=B5=EC=A7=80=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20init?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/mypage/setting/SettingMainFragment.kt | 2 - .../setting/page/SettingNoticeFragment.kt | 38 +++++++++++++++++ .../res/layout/fragment_setting_notice.xml | 42 +++++++++++++++++++ 3 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingNoticeFragment.kt create mode 100644 frontend/ARchive/app/src/main/res/layout/fragment_setting_notice.xml diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt index 05ff802f3..c271e685d 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt @@ -1,7 +1,6 @@ package com.droidblossom.archive.presentation.ui.mypage.setting import android.content.Intent -import android.content.pm.PackageManager import android.net.Uri import android.os.Bundle import android.view.View @@ -24,7 +23,6 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import javax.inject.Inject - @AndroidEntryPoint class SettingMainFragment : BaseFragment(R.layout.fragment_setting_main) { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingNoticeFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingNoticeFragment.kt new file mode 100644 index 000000000..f071e916f --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingNoticeFragment.kt @@ -0,0 +1,38 @@ +package com.droidblossom.archive.presentation.ui.mypage.setting.page + +import android.os.Bundle +import android.view.View +import androidx.activity.OnBackPressedCallback +import androidx.fragment.app.viewModels +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import androidx.navigation.NavController +import androidx.navigation.Navigation +import com.droidblossom.archive.R +import com.droidblossom.archive.databinding.FragmentSettingNoticeBinding +import com.droidblossom.archive.databinding.FragmentSettingNotificationBinding +import com.droidblossom.archive.presentation.base.BaseFragment +import com.droidblossom.archive.presentation.ui.mypage.setting.SettingViewModel +import com.droidblossom.archive.presentation.ui.mypage.setting.SettingViewModelImpl +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.launch + +@AndroidEntryPoint +class SettingNoticeFragment : + BaseFragment(R.layout.fragment_setting_notice) { + + override val viewModel: SettingViewModelImpl by viewModels() + lateinit var navController: NavController + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.vm = viewModel + navController = Navigation.findNavController(view) + initView() + } + + private fun initView() {} + + override fun observeData() {} +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_setting_notice.xml b/frontend/ARchive/app/src/main/res/layout/fragment_setting_notice.xml new file mode 100644 index 000000000..0a0ce8fed --- /dev/null +++ b/frontend/ARchive/app/src/main/res/layout/fragment_setting_notice.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file From 71f40aa3a0d801b2db400ea63b29386ef04e8065 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Tue, 26 Mar 2024 03:00:30 +0900 Subject: [PATCH 048/301] =?UTF-8?q?faet:=20=EC=84=B8=ED=8C=85=20=EB=84=A4?= =?UTF-8?q?=EB=B9=84=EC=97=90=20=EA=B3=B5=EC=A7=80=EC=82=AC=ED=95=AD=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/mypage/setting/SettingMainFragment.kt | 2 +- .../setting/page/SettingNoticeFragment.kt | 17 ++++++++-------- .../res/layout/fragment_setting_agree.xml | 5 ++--- .../res/layout/fragment_setting_notice.xml | 20 ++++++++++++++++++- .../main/res/navigation/nav_setting_graph.xml | 8 ++++++++ 5 files changed, 38 insertions(+), 14 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt index c271e685d..863f82326 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt @@ -77,7 +77,7 @@ class SettingMainFragment : } SettingViewModel.SettingMainEvent.GoNotice -> { - + navController.navigate(R.id.action_settingMainFragment_to_settingNoticeFragment) } SettingViewModel.SettingMainEvent.GoNotification -> { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingNoticeFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingNoticeFragment.kt index f071e916f..829646bf9 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingNoticeFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingNoticeFragment.kt @@ -2,21 +2,14 @@ package com.droidblossom.archive.presentation.ui.mypage.setting.page import android.os.Bundle import android.view.View -import androidx.activity.OnBackPressedCallback import androidx.fragment.app.viewModels -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.NavController import androidx.navigation.Navigation import com.droidblossom.archive.R import com.droidblossom.archive.databinding.FragmentSettingNoticeBinding -import com.droidblossom.archive.databinding.FragmentSettingNotificationBinding import com.droidblossom.archive.presentation.base.BaseFragment -import com.droidblossom.archive.presentation.ui.mypage.setting.SettingViewModel import com.droidblossom.archive.presentation.ui.mypage.setting.SettingViewModelImpl import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.launch @AndroidEntryPoint class SettingNoticeFragment : @@ -32,7 +25,13 @@ class SettingNoticeFragment : initView() } - private fun initView() {} + private fun initView() { + binding.backBtn.setOnClickListener { + navController.popBackStack() + } + } + + override fun observeData() { - override fun observeData() {} + } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_setting_agree.xml b/frontend/ARchive/app/src/main/res/layout/fragment_setting_agree.xml index b41fbd0b0..35a5df475 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_setting_agree.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_setting_agree.xml @@ -44,17 +44,16 @@ android:layout_width="0dp" android:layout_height="0dp" android:layout_marginHorizontal="16dp" - android:layout_marginTop="32dp" + android:layout_marginTop="16dp" android:clipToPadding="false" android:overScrollMode="never" - android:paddingTop="16dp" + android:paddingTop="32dp" android:paddingBottom="12dp" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/backBtn" - tools:itemCount="5" tools:listitem="@layout/item_agree" /> diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_setting_notice.xml b/frontend/ARchive/app/src/main/res/layout/fragment_setting_notice.xml index 0a0ce8fed..8a920a9d5 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_setting_notice.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_setting_notice.xml @@ -1,6 +1,7 @@ + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools"> @@ -37,6 +38,23 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> + + \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/navigation/nav_setting_graph.xml b/frontend/ARchive/app/src/main/res/navigation/nav_setting_graph.xml index adeb2ffbc..9f429faa1 100644 --- a/frontend/ARchive/app/src/main/res/navigation/nav_setting_graph.xml +++ b/frontend/ARchive/app/src/main/res/navigation/nav_setting_graph.xml @@ -16,6 +16,9 @@ + + \ No newline at end of file From f8c75bc1f046cf36c9757f1099433c33e7f670d2 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Tue, 26 Mar 2024 03:10:25 +0900 Subject: [PATCH 049/301] =?UTF-8?q?feat:=20=EC=9C=A0=EC=A0=80=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=20init?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../setting/page/SettingUserFragment.kt | 38 +++++++++++++++++++ .../main/res/layout/fragment_setting_user.xml | 30 +++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingUserFragment.kt create mode 100644 frontend/ARchive/app/src/main/res/layout/fragment_setting_user.xml diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingUserFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingUserFragment.kt new file mode 100644 index 000000000..fab8ceb21 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingUserFragment.kt @@ -0,0 +1,38 @@ +package com.droidblossom.archive.presentation.ui.mypage.setting.page + +import android.os.Bundle +import android.view.View +import androidx.fragment.app.viewModels +import androidx.navigation.NavController +import androidx.navigation.Navigation +import com.droidblossom.archive.R +import com.droidblossom.archive.databinding.FragmentSettingNoticeBinding +import com.droidblossom.archive.databinding.FragmentSettingUserBinding +import com.droidblossom.archive.presentation.base.BaseFragment +import com.droidblossom.archive.presentation.ui.mypage.setting.SettingViewModelImpl +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class SettingUserFragment : + BaseFragment(R.layout.fragment_setting_user) { + + override val viewModel: SettingViewModelImpl by viewModels() + lateinit var navController: NavController + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.vm = viewModel + navController = Navigation.findNavController(view) + initView() + } + + private fun initView() { + binding.backBtn.setOnClickListener { + navController.popBackStack() + } + } + + override fun observeData() { + + } +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_setting_user.xml b/frontend/ARchive/app/src/main/res/layout/fragment_setting_user.xml new file mode 100644 index 000000000..d87fa539f --- /dev/null +++ b/frontend/ARchive/app/src/main/res/layout/fragment_setting_user.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + \ No newline at end of file From 15ee38ee6ead383e00fa386724fd678eb605e9c3 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Tue, 26 Mar 2024 03:10:47 +0900 Subject: [PATCH 050/301] =?UTF-8?q?feat:=20=EC=84=B8=ED=8C=85=20=EB=84=A4?= =?UTF-8?q?=EB=B9=84=EC=97=90=20=EC=9C=A0=EC=A0=80=20=EA=B4=80=EB=A6=AC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ui/mypage/setting/SettingMainFragment.kt | 2 +- .../app/src/main/res/layout/fragment_setting_main.xml | 2 ++ .../app/src/main/res/navigation/nav_setting_graph.xml | 8 ++++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt index 863f82326..f3abeffd0 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/SettingMainFragment.kt @@ -85,7 +85,7 @@ class SettingMainFragment : } SettingViewModel.SettingMainEvent.GoUser -> { - + navController.navigate(R.id.action_settingMainFragment_to_settingUserFragment) } is SettingViewModel.SettingMainEvent.ShowToastMessage -> { diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_setting_main.xml b/frontend/ARchive/app/src/main/res/layout/fragment_setting_main.xml index 0c26e20a2..2d0ba93c8 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_setting_main.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_setting_main.xml @@ -46,6 +46,7 @@ android:layout_height="60dp" android:layout_marginHorizontal="8dp" android:layout_marginTop="16dp" + android:onClick="@{()->vm.goUser()}" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/backBtn"> @@ -155,6 +156,7 @@ android:layout_width="0dp" android:layout_height="60dp" android:layout_marginHorizontal="8dp" + android:onClick="@{()->vm.goNotice()}" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/notiLayout"> diff --git a/frontend/ARchive/app/src/main/res/navigation/nav_setting_graph.xml b/frontend/ARchive/app/src/main/res/navigation/nav_setting_graph.xml index 9f429faa1..a12b44105 100644 --- a/frontend/ARchive/app/src/main/res/navigation/nav_setting_graph.xml +++ b/frontend/ARchive/app/src/main/res/navigation/nav_setting_graph.xml @@ -19,6 +19,9 @@ + + \ No newline at end of file From 66efbe6f6aa57ed6daedc91d0bed2eab61bd22cf Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Tue, 26 Mar 2024 04:24:52 +0900 Subject: [PATCH 051/301] =?UTF-8?q?refact=20:=20=EB=A6=AC=EC=82=AC?= =?UTF-8?q?=EC=9D=B4=ED=81=B4=EB=B7=B0=EC=95=88=EC=97=90=20=EC=8A=A4?= =?UTF-8?q?=ED=81=AC=EB=A1=A4=EC=9D=80=20=EA=B6=8C=EC=9E=A5=EC=95=88?= =?UTF-8?q?=ED=95=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/mypage/setting/adapter/AgreeRVA.kt | 1 - .../app/src/main/res/layout/item_agree.xml | 25 +++++++------------ 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/adapter/AgreeRVA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/adapter/AgreeRVA.kt index d9a4a8390..ec4b3ccdf 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/adapter/AgreeRVA.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/adapter/AgreeRVA.kt @@ -19,7 +19,6 @@ class AgreeRVA : inner class ItemViewHolder( private val binding: ItemAgreeBinding ) : RecyclerView.ViewHolder(binding.root) { - @SuppressLint("ClickableViewAccessibility") fun bind(data: Agree) { binding.item = data binding.moreBtn.setOnClickListener { diff --git a/frontend/ARchive/app/src/main/res/layout/item_agree.xml b/frontend/ARchive/app/src/main/res/layout/item_agree.xml index 4e0a6834a..08927fed7 100644 --- a/frontend/ARchive/app/src/main/res/layout/item_agree.xml +++ b/frontend/ARchive/app/src/main/res/layout/item_agree.xml @@ -40,33 +40,26 @@ app:layout_constraintTop_toTopOf="@id/agreeTitle" app:tint="@color/gray_700" /> - - - - - - - - + android:layout_height="wrap_content" + android:text="@{item.content}" + android:textAppearance="@style/TextAppearance.App.caption4" /> + \ No newline at end of file From a934aae36b0252e30d404dbad95626f193ce393d Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Tue, 26 Mar 2024 04:25:16 +0900 Subject: [PATCH 052/301] =?UTF-8?q?feat:=20=EA=B3=B5=EC=A7=80=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EC=96=B4=EB=8E=81=ED=84=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/domain/model/setting/Notice.kt | 8 ++ .../ui/mypage/setting/adapter/NoticeRVA.kt | 72 +++++++++++++ .../app/src/main/res/layout/item_notice.xml | 101 ++++++++++++++++++ 3 files changed, 181 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/setting/Notice.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/adapter/NoticeRVA.kt create mode 100644 frontend/ARchive/app/src/main/res/layout/item_notice.xml diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/setting/Notice.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/setting/Notice.kt new file mode 100644 index 000000000..e8366db59 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/setting/Notice.kt @@ -0,0 +1,8 @@ +package com.droidblossom.archive.domain.model.setting + +data class Notice( + val title: String, + val date : String, + val contents : List, + var isOpen : Boolean = false +) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/adapter/NoticeRVA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/adapter/NoticeRVA.kt new file mode 100644 index 000000000..a4eb52acd --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/adapter/NoticeRVA.kt @@ -0,0 +1,72 @@ +package com.droidblossom.archive.presentation.ui.mypage.setting.adapter + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.core.view.isGone +import androidx.core.view.isVisible +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import com.droidblossom.archive.R +import com.droidblossom.archive.databinding.ItemNoticeBinding +import com.droidblossom.archive.domain.model.setting.Notice + + +class NoticeRVA : + ListAdapter(differ) { + + inner class ItemViewHolder( + private val binding: ItemNoticeBinding + ) : RecyclerView.ViewHolder(binding.root) { + fun bind(data: Notice) { + binding.item = data + val adapter = NoticeContentRVA() + binding.contentRVA.adapter = adapter + adapter.submitList(data.contents) + binding.moreBtn.setOnClickListener{ + if (data.isOpen) { + data.isOpen = false + binding.contentLayout.isGone = true + binding.moreBtn.rotation = 180f + binding.line.setBackgroundResource(R.color.gray_400) + } else { + data.isOpen = true + binding.contentLayout.isVisible = true + binding.moreBtn.rotation = 180f + binding.line.setBackgroundResource(R.color.black) + } + } + } + + } + + + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): ItemViewHolder { + return ItemViewHolder( + ItemNoticeBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) + } + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + holder.bind(getItem(position)) + } + + companion object { + val differ = object : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: Notice, newItem: Notice): Boolean { + return oldItem == newItem + } + + override fun areContentsTheSame(oldItem: Notice, newItem: Notice): Boolean { + return oldItem == newItem + } + } + } +} diff --git a/frontend/ARchive/app/src/main/res/layout/item_notice.xml b/frontend/ARchive/app/src/main/res/layout/item_notice.xml new file mode 100644 index 000000000..9e83d6b24 --- /dev/null +++ b/frontend/ARchive/app/src/main/res/layout/item_notice.xml @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 05cbe926610ebf3996835f6914d862595b726d8e Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Tue, 26 Mar 2024 04:25:33 +0900 Subject: [PATCH 053/301] =?UTF-8?q?feat:=20=EA=B3=B5=EC=A7=80=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EC=BB=A8=ED=85=90=EC=B8=A0=20=EC=96=B4=EB=8E=81?= =?UTF-8?q?=ED=84=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/model/setting/NoticeContent.kt | 5 ++ .../setting/adapter/NoticeContentRVA.kt | 55 +++++++++++++++++++ .../main/res/layout/item_notice_content.xml | 36 ++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/setting/NoticeContent.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/adapter/NoticeContentRVA.kt create mode 100644 frontend/ARchive/app/src/main/res/layout/item_notice_content.xml diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/setting/NoticeContent.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/setting/NoticeContent.kt new file mode 100644 index 000000000..ba0c1d3dc --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/setting/NoticeContent.kt @@ -0,0 +1,5 @@ +package com.droidblossom.archive.domain.model.setting + +data class NoticeContent( + val content: String, +) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/adapter/NoticeContentRVA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/adapter/NoticeContentRVA.kt new file mode 100644 index 000000000..28cdd7282 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/adapter/NoticeContentRVA.kt @@ -0,0 +1,55 @@ +package com.droidblossom.archive.presentation.ui.mypage.setting.adapter + +import android.annotation.SuppressLint +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.droidblossom.archive.databinding.ItemNoticeBinding +import com.droidblossom.archive.databinding.ItemNoticeContentBinding +import com.droidblossom.archive.domain.model.setting.Notice +import com.droidblossom.archive.domain.model.setting.NoticeContent + + +class NoticeContentRVA : + ListAdapter(differ) { + + inner class ItemViewHolder( + private val binding: ItemNoticeContentBinding + ) : RecyclerView.ViewHolder(binding.root) { + fun bind(data: NoticeContent) { + binding.item = data + } + } + + + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): ItemViewHolder { + return ItemViewHolder( + ItemNoticeContentBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) + } + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + holder.bind(getItem(position)) + } + + companion object { + val differ = object : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: NoticeContent, newItem: NoticeContent): Boolean { + return oldItem == newItem + } + + override fun areContentsTheSame(oldItem: NoticeContent, newItem: NoticeContent): Boolean { + return oldItem == newItem + } + } + } +} diff --git a/frontend/ARchive/app/src/main/res/layout/item_notice_content.xml b/frontend/ARchive/app/src/main/res/layout/item_notice_content.xml new file mode 100644 index 000000000..44d45e0b7 --- /dev/null +++ b/frontend/ARchive/app/src/main/res/layout/item_notice_content.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + \ No newline at end of file From 302ef929528eec72b31f021f5dafb895339e3568 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Tue, 26 Mar 2024 04:25:48 +0900 Subject: [PATCH 054/301] =?UTF-8?q?feat:=20=EA=B3=B5=EC=A7=80=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EC=99=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../setting/page/SettingNoticeFragment.kt | 28 +++++++++++++++++++ .../res/layout/fragment_setting_notice.xml | 2 +- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingNoticeFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingNoticeFragment.kt index 829646bf9..5f9b822df 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingNoticeFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingNoticeFragment.kt @@ -7,8 +7,11 @@ import androidx.navigation.NavController import androidx.navigation.Navigation import com.droidblossom.archive.R import com.droidblossom.archive.databinding.FragmentSettingNoticeBinding +import com.droidblossom.archive.domain.model.setting.Notice +import com.droidblossom.archive.domain.model.setting.NoticeContent import com.droidblossom.archive.presentation.base.BaseFragment import com.droidblossom.archive.presentation.ui.mypage.setting.SettingViewModelImpl +import com.droidblossom.archive.presentation.ui.mypage.setting.adapter.NoticeRVA import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint @@ -18,6 +21,10 @@ class SettingNoticeFragment : override val viewModel: SettingViewModelImpl by viewModels() lateinit var navController: NavController + private val noticeAdapter by lazy { + NoticeRVA() + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding.vm = viewModel @@ -26,6 +33,27 @@ class SettingNoticeFragment : } private fun initView() { + binding.adapter.adapter = noticeAdapter + noticeAdapter.submitList( + listOf( + Notice( + "아카이브 업데이트 : 1.1.0", + "2024/03/01", + listOf( + NoticeContent("업데이트"), + NoticeContent("했습니다 : 1.1.0") + ) + ), + Notice( + "아카이브 업데이트 : 1.1.0", + "2024/03/01", + listOf( + NoticeContent("업데이트"), + NoticeContent("했습니다 : 1.1.0") + ) + ) + ) + ) binding.backBtn.setOnClickListener { navController.popBackStack() } diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_setting_notice.xml b/frontend/ARchive/app/src/main/res/layout/fragment_setting_notice.xml index 8a920a9d5..ea0a75a3e 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_setting_notice.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_setting_notice.xml @@ -53,7 +53,7 @@ app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/backBtn" - tools:listitem="@layout/item_agree" /> + tools:listitem="@layout/item_notice" /> From a7adb126d29fbb5e86bdd99e2f97411e1a754c1c Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Tue, 26 Mar 2024 04:28:25 +0900 Subject: [PATCH 055/301] chore --- .../archive/presentation/ui/mypage/setting/adapter/NoticeRVA.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/adapter/NoticeRVA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/adapter/NoticeRVA.kt index a4eb52acd..d3ecf2cef 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/adapter/NoticeRVA.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/adapter/NoticeRVA.kt @@ -27,7 +27,7 @@ class NoticeRVA : if (data.isOpen) { data.isOpen = false binding.contentLayout.isGone = true - binding.moreBtn.rotation = 180f + binding.moreBtn.rotation = 0f binding.line.setBackgroundResource(R.color.gray_400) } else { data.isOpen = true From 62794ee7f08e3469a2050952bfb9a6074ed642da Mon Sep 17 00:00:00 2001 From: comst19 Date: Tue, 26 Mar 2024 12:39:44 +0900 Subject: [PATCH 056/301] =?UTF-8?q?refact:=20=EB=B7=B0=EB=AA=A8=EB=8D=B8?= =?UTF-8?q?=EC=9D=98=20=EB=AE=A4=ED=84=B0=EB=B8=94=20=EC=86=8D=EC=84=B1?= =?UTF-8?q?=EC=9D=84=20=EC=9E=84=EB=AE=A4=ED=84=B0=EB=B8=94=EB=A1=9C=20?= =?UTF-8?q?=EC=A0=84=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/presentation/ui/auth/AuthViewModelImpl.kt | 9 ++++----- .../presentation/ui/mypage/MyPageViewModelImpl.kt | 2 +- .../presentation/ui/skin/skinmake/SkinMakeViewModel.kt | 2 +- .../ui/skin/skinmake/SkinMakeViewModelImpl.kt | 3 +-- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/auth/AuthViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/auth/AuthViewModelImpl.kt index 0d1177505..90a29399b 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/auth/AuthViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/auth/AuthViewModelImpl.kt @@ -56,10 +56,9 @@ class AuthViewModelImpl @Inject constructor( private val _signUpEvents = MutableSharedFlow() override val signUpEvents: SharedFlow = _signUpEvents.asSharedFlow() - private val _phoneNumber = MutableStateFlow("") - override val phoneNumber: MutableStateFlow = _phoneNumber + override val phoneNumber = MutableStateFlow("") - private val _rawPhoneNumber = _phoneNumber + private val _rawPhoneNumber = phoneNumber .map { it.replace("-", "") } .stateIn(viewModelScope, SharingStarted.Eagerly, "") override val rawPhoneNumber: StateFlow = _rawPhoneNumber @@ -104,7 +103,7 @@ class AuthViewModelImpl @Inject constructor( certificationNumber3.value = "" certificationNumber2.value = "" certificationNumber1.value = "" - _phoneNumber.value = "" + phoneNumber.value = "" } override fun memberStatusCheck(memberStatusCheckData : CheckStatus, signUpData : SignUp){ @@ -193,7 +192,7 @@ class AuthViewModelImpl @Inject constructor( override fun clearPhoneNumber(){ viewModelScope.launch { - _phoneNumber.emit("") + phoneNumber.emit("") } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt index 8d17150fe..c8913cd6d 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt @@ -35,7 +35,7 @@ class MyPageViewModelImpl @Inject constructor( get() =_myPageEvents.asSharedFlow() - private val _myInfo = MutableStateFlow(MemberDetail("USER", "", "")) + private val _myInfo = MutableStateFlow(MemberDetail("USER", "", "")) override val myInfo: StateFlow get() = _myInfo diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModel.kt index 685818fc5..84a3f565d 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModel.kt @@ -15,7 +15,7 @@ interface SkinMakeViewModel { val skinImgUri: MutableStateFlow val addMotion : StateFlow val skinImgFile: StateFlow - val skinMotions: MutableStateFlow> + val skinMotions: StateFlow> val skinMotionIndex: StateFlow fun skinMakeEvent(event: SkinMakeEvent) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModelImpl.kt index 4a26a4ab0..9bb3597ff 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModelImpl.kt @@ -102,8 +102,7 @@ class SkinMakeViewModelImpl @Inject constructor( ) ) ) - override val skinMotions: MutableStateFlow> - get() = _skinMotions + override val skinMotions get() = _skinMotions private val _skinMotionIndex = MutableStateFlow(-1) override val skinMotionIndex: StateFlow From 226eef6090c76cb8044028015eccb2556fdba706 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Tue, 26 Mar 2024 14:00:01 +0900 Subject: [PATCH 057/301] =?UTF-8?q?chore=20:=20=EA=B3=B5=EC=A7=80=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EC=98=A4=ED=94=88=20=ED=84=B0=EC=B9=98=20=EB=B2=94?= =?UTF-8?q?=EC=9C=84=20=EB=8A=98=EB=A6=AC=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ui/mypage/setting/adapter/NoticeRVA.kt | 2 +- .../ui/mypage/setting/page/SettingNoticeFragment.kt | 5 +++-- frontend/ARchive/app/src/main/res/layout/item_notice.xml | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/adapter/NoticeRVA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/adapter/NoticeRVA.kt index d3ecf2cef..a7cad41f2 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/adapter/NoticeRVA.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/adapter/NoticeRVA.kt @@ -23,7 +23,7 @@ class NoticeRVA : val adapter = NoticeContentRVA() binding.contentRVA.adapter = adapter adapter.submitList(data.contents) - binding.moreBtn.setOnClickListener{ + binding.noticeLayout.setOnClickListener{ if (data.isOpen) { data.isOpen = false binding.contentLayout.isGone = true diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingNoticeFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingNoticeFragment.kt index 5f9b822df..97a5ddeae 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingNoticeFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingNoticeFragment.kt @@ -45,11 +45,12 @@ class SettingNoticeFragment : ) ), Notice( - "아카이브 업데이트 : 1.1.0", + "아카이브 업데이트 : 1.0.9", "2024/03/01", listOf( NoticeContent("업데이트"), - NoticeContent("했습니다 : 1.1.0") + NoticeContent("hey"), + NoticeContent("keep going"), ) ) ) diff --git a/frontend/ARchive/app/src/main/res/layout/item_notice.xml b/frontend/ARchive/app/src/main/res/layout/item_notice.xml index 9e83d6b24..b9fa11d48 100644 --- a/frontend/ARchive/app/src/main/res/layout/item_notice.xml +++ b/frontend/ARchive/app/src/main/res/layout/item_notice.xml @@ -48,7 +48,7 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> - Date: Tue, 26 Mar 2024 14:38:41 +0900 Subject: [PATCH 058/301] =?UTF-8?q?refact:=20=EB=A6=AC=EC=82=AC=EC=9D=B4?= =?UTF-8?q?=ED=81=B4=EB=9F=AC=EB=B7=B0=20=EC=95=84=EC=9D=B4=ED=85=9C=20?= =?UTF-8?q?=ED=81=B4=EB=A6=AD=20=EC=8B=9C=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EB=B3=B5=EC=82=AC=20=EB=B9=88=EB=B2=88=ED=95=98=EA=B2=8C=20?= =?UTF-8?q?=EB=B0=9C=EC=83=9D=ED=95=98=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0=20-=20=EC=84=B1=EB=8A=A5=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/skin/adapter/SkinMotionRVA.kt | 18 ++++++++++++++++-- .../ui/skin/skinmake/SkinMakeFragment.kt | 4 +++- .../ui/skin/skinmake/SkinMakeViewModel.kt | 2 +- .../ui/skin/skinmake/SkinMakeViewModelImpl.kt | 12 +++++------- 4 files changed, 25 insertions(+), 11 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/adapter/SkinMotionRVA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/adapter/SkinMotionRVA.kt index 7406fa923..955f09947 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/adapter/SkinMotionRVA.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/adapter/SkinMotionRVA.kt @@ -1,5 +1,6 @@ package com.droidblossom.archive.presentation.ui.skin.adapter +import android.util.Log import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.DiffUtil @@ -8,7 +9,9 @@ import androidx.recyclerview.widget.RecyclerView import com.droidblossom.archive.databinding.ItemSkinMotionBinding import com.droidblossom.archive.domain.model.capsule_skin.SkinMotion -class SkinMotionRVA(val ItemClick: (SkinMotion) -> Unit) : ListAdapter(differ) { +class SkinMotionRVA(val ItemClick: (previousPosition: Int?, currentPosition: Int) -> Unit) : ListAdapter(differ) { + + private var previousClickedPosition: Int? = null inner class ItemViewHolder( private val binding: ItemSkinMotionBinding @@ -16,7 +19,13 @@ class SkinMotionRVA(val ItemClick: (SkinMotion) -> Unit) : ListAdapter notifyItemChanged(previousClickedPosition) } + notifyItemChanged(currentClickedPosition) + previousClickedPosition = currentClickedPosition + } } } } @@ -38,6 +47,11 @@ class SkinMotionRVA(val ItemClick: (SkinMotion) -> Unit) : ListAdapter?) { + previousClickedPosition = null + super.submitList(list) + } + companion object { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeFragment.kt index 574025b86..ac05389d2 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeFragment.kt @@ -37,7 +37,9 @@ class SkinMakeFragment : BaseFragment + viewModel.selectSkinMotion(previousPosition, currentPosition) + } } private val picMedia = registerForActivityResult(ActivityResultContracts.PickVisualMedia()) {uri -> diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModel.kt index 84a3f565d..6fdfecf75 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModel.kt @@ -23,7 +23,7 @@ interface SkinMakeViewModel { fun setFile(skinImgFile: File) fun makeSkin() - fun selectSkinMotion(skinMotion : SkinMotion) + fun selectSkinMotion(previousPosition : Int?, currentPosition : Int) sealed class SkinMakeEvent { object SuccessSkinMake : SkinMakeEvent() diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModelImpl.kt index 9bb3597ff..d386021cf 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModelImpl.kt @@ -107,15 +107,13 @@ class SkinMakeViewModelImpl @Inject constructor( private val _skinMotionIndex = MutableStateFlow(-1) override val skinMotionIndex: StateFlow get() = _skinMotionIndex - override fun selectSkinMotion(skinMotion: SkinMotion) { + override fun selectSkinMotion(previousPosition: Int?, currentPosition: Int) { viewModelScope.launch { - val newList = skinMotions.value.map { existingSkinMotion -> - if (existingSkinMotion == skinMotion) { - existingSkinMotion.copy(isClicked = true) - } else { - existingSkinMotion.copy(isClicked = false) - } + val newList = _skinMotions.value + previousPosition?.let { + newList[it].isClicked = false } + newList[currentPosition].isClicked = true _skinMotions.emit(newList) } } From 4335ec87045eefc535ffba4afc53b3c2f0ca08e9 Mon Sep 17 00:00:00 2001 From: comst19 Date: Tue, 26 Mar 2024 16:38:43 +0900 Subject: [PATCH 059/301] =?UTF-8?q?refact:=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EB=A6=AC=EC=82=AC=EC=9D=B4=ED=81=B4?= =?UTF-8?q?=EB=9F=AC=EB=B7=B0=20=EC=95=84=EC=9D=B4=ED=85=9C=20=ED=81=B4?= =?UTF-8?q?=EB=A6=AD=20=EC=8B=9C=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EB=B3=B5?= =?UTF-8?q?=EC=82=AC=20=EB=B9=88=EB=B2=88=ED=95=98=EA=B2=8C=20=EB=B0=9C?= =?UTF-8?q?=EC=83=9D=ED=95=98=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0=20-=20=EC=84=B1=EB=8A=A5=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/domain/model/common/MyCapsule.kt | 2 +- .../presentation/ui/camera/ARContentNode.kt | 2 +- .../presentation/ui/camera/CameraFragment.kt | 1 + .../archive/presentation/ui/home/HomeFragment.kt | 2 +- .../home/dialog/CapsulePreviewDialogFragment.kt | 9 ++++++++- .../presentation/ui/mypage/MyPageFragment.kt | 14 +++++++++++--- .../presentation/ui/mypage/MyPageViewModel.kt | 4 +++- .../ui/mypage/MyPageViewModelImpl.kt | 16 +++++----------- .../ui/mypage/adapter/MyCapsuleRVA.kt | 4 ++-- 9 files changed, 33 insertions(+), 21 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/common/MyCapsule.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/common/MyCapsule.kt index b6332a421..9cc7e0679 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/common/MyCapsule.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/common/MyCapsule.kt @@ -5,7 +5,7 @@ data class MyCapsule ( val capsuleSkinUrl: String, val createdDate: String, val dueDate: String?, - val isOpened: Boolean, + var isOpened: Boolean, val title: String, val capsuleType: String ) \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/ARContentNode.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/ARContentNode.kt index 09b0b25d8..fde472817 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/ARContentNode.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/ARContentNode.kt @@ -53,7 +53,7 @@ class ARContentNode( isEditable = true } onSingleTapConfirmed = { - val sheet = CapsulePreviewDialogFragment.newInstance(capsule.id.toString(), capsule.capsuleType.toString(), true) + val sheet = CapsulePreviewDialogFragment.newInstance("-1",capsule.id.toString(), capsule.capsuleType.toString(), true) sheet.show(fragmentManagerProvider.provideFragmentManager(), "CapsulePreviewDialog") false } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt index 0958ac5f6..3240ad9a7 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt @@ -52,6 +52,7 @@ class CameraFragment : when (event) { is CameraViewModel.CameraEvent.ShowCapsulePreviewDialog -> { val sheet = CapsulePreviewDialogFragment.newInstance( + "-1", event.capsuleId, event.capsuleType, true diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt index 23ec03244..ac6dc16b2 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt @@ -109,7 +109,7 @@ class HomeFragment : BaseFragment(R.layo when (event) { is HomeViewModel.HomeEvent.ShowCapsulePreviewDialog -> { - val sheet = CapsulePreviewDialogFragment.newInstance(event.capsuleId, event.capsuleType, false) + val sheet = CapsulePreviewDialogFragment.newInstance("-1",event.capsuleId, event.capsuleType, false) sheet.show(parentFragmentManager, "CapsulePreviewDialog") } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogFragment.kt index 0c1ac31a9..cb8d6bc85 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogFragment.kt @@ -31,6 +31,11 @@ class CapsulePreviewDialogFragment : private val viewModel: CapsulePreviewDialogViewModelImpl by viewModels() + val capsuleIndex: Int by lazy { + arguments?.getString("capsule_index")!!.toInt() + } + + val capsuleId: Int by lazy { arguments?.getString("capsule_id")!!.toInt() } @@ -203,6 +208,7 @@ class CapsulePreviewDialogFragment : override fun onDismiss(dialog: DialogInterface) { super.onDismiss(dialog) val capsuleState = Bundle().apply { + putInt("capsuleIndex",capsuleIndex) putLong("capsuleId", capsuleId.toLong()) putBoolean("isOpened", viewModel.capsuleOpenState.value) } @@ -210,8 +216,9 @@ class CapsulePreviewDialogFragment : } companion object { - fun newInstance(capsuleId: String, capsuleType: String, calledFromCamera : Boolean): CapsulePreviewDialogFragment { + fun newInstance(capsuleIndex: String, capsuleId: String, capsuleType: String, calledFromCamera : Boolean): CapsulePreviewDialogFragment { val args = Bundle().apply { + putString("capsule_index", capsuleIndex) putString("capsule_id", capsuleId) putString("capsule_type", capsuleType) putBoolean("called_from_camera", calledFromCamera) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt index b76ba07c3..0676ae8cc 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt @@ -47,8 +47,8 @@ class MyPageFragment : ) ) }, - { id, type -> - val sheet = CapsulePreviewDialogFragment.newInstance(id.toString(), type.toString(), false) + { capsuleIndex, id, type -> + val sheet = CapsulePreviewDialogFragment.newInstance(capsuleIndex.toString(), id.toString(), type.toString(), false) sheet.show(parentFragmentManager, "CapsulePreviewDialog") }, ) @@ -61,9 +61,13 @@ class MyPageFragment : viewModel.getSecretCapsulePage() parentFragmentManager.setFragmentResultListener("capsuleState", viewLifecycleOwner) { key, bundle -> + val capsuleIndex = bundle.getInt("capsuleIndex") val capsuleId = bundle.getLong("capsuleId") val capsuleOpenState = bundle.getBoolean("isOpened") - viewModel.updateCapsuleOpenState(capsuleId,capsuleOpenState) + if (capsuleIndex != -1 && capsuleOpenState) { + viewModel.updateCapsuleOpenState(capsuleIndex,capsuleId) + myCapsuleRVA.notifyItemChanged(capsuleIndex) + } } initRVA() @@ -126,6 +130,10 @@ class MyPageFragment : startActivity(SettingActivity.newIntent(requireContext())) } + is MyPageViewModel.MyPageEvent.CapsuleStateUpdate -> { + //myCapsuleRVA.notifyItemChanged(event.capsuleIndex) + } + else -> {} } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModel.kt index fb7dfbc6b..cabccefb6 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModel.kt @@ -18,12 +18,14 @@ interface MyPageViewModel { fun getSecretCapsulePage() fun clearCapsules() fun updateMyCapsulesUI() - fun updateCapsuleOpenState(capsuleId: Long, isOpened: Boolean) + fun updateCapsuleOpenState(capsuleIndex: Int, capsuleId: Long) fun clickSetting() sealed class MyPageEvent { data class ShowToastMessage(val message : String) : MyPageEvent() + data class CapsuleStateUpdate(val capsuleIndex: Int) : MyPageEvent() + object ClickSetting : MyPageEvent() } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt index c8913cd6d..e121572fc 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt @@ -104,18 +104,12 @@ class MyPageViewModelImpl @Inject constructor( } } - override fun updateCapsuleOpenState(capsuleId: Long, isOpened: Boolean) { + override fun updateCapsuleOpenState(capsuleIndex: Int, capsuleId: Long) { viewModelScope.launch { - val updatedCapsules = withContext(Dispatchers.Default) { - _myCapsules.value.map { capsule -> - if (capsule.capsuleId == capsuleId) { - capsule.copy(isOpened = isOpened) - } else { - capsule - } - } - } - _myCapsules.emit(updatedCapsules) + val newList = _myCapsules.value + newList[capsuleIndex].isOpened = true + _myCapsules.emit(newList) + _myCapsulesUI.emit(myCapsules.value) } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/MyCapsuleRVA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/MyCapsuleRVA.kt index 988bfd1a8..376bad1d4 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/MyCapsuleRVA.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/MyCapsuleRVA.kt @@ -17,7 +17,7 @@ import com.droidblossom.archive.util.CapsuleTypeUtils.stringToEnum class MyCapsuleRVA( private val goDetail: (Long, HomeFragment.CapsuleType) -> Unit, - private val goSummary: (Long, HomeFragment.CapsuleType) -> Unit + private val goSummary: (Int, Long, HomeFragment.CapsuleType) -> Unit ) : ListAdapter(differ) { @@ -30,7 +30,7 @@ class MyCapsuleRVA( if (data.isOpened) { goDetail(data.capsuleId, stringToEnum(data.capsuleType)) } else { - goSummary(data.capsuleId, stringToEnum(data.capsuleType)) + goSummary(bindingAdapterPosition, data.capsuleId, stringToEnum(data.capsuleType)) } } From a866ab10c2cf15c509fbdfa45b851a7e0dd33e9d Mon Sep 17 00:00:00 2001 From: comst19 Date: Tue, 26 Mar 2024 17:25:35 +0900 Subject: [PATCH 060/301] =?UTF-8?q?refact:=20=EC=8A=A4=ED=82=A8=20?= =?UTF-8?q?=EC=84=A0=ED=83=9D=20=EB=A6=AC=EC=82=AC=EC=9D=B4=ED=81=B4?= =?UTF-8?q?=EB=9F=AC=EB=B7=B0=20=EC=95=84=EC=9D=B4=ED=85=9C=20=ED=81=B4?= =?UTF-8?q?=EB=A6=AD=20=EC=8B=9C=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=ED=83=90?= =?UTF-8?q?=EC=83=89=20=EB=B9=88=EB=B2=88=ED=95=98=EA=B2=8C=20=EB=B0=9C?= =?UTF-8?q?=EC=83=9D=ED=95=98=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0=20-=20=EC=84=B1=EB=8A=A5=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../createcapsule/CreateCapsule2Fragment.kt | 5 +++- .../createcapsule/CreateCapsuleViewModel.kt | 2 +- .../CreateCapsuleViewModelImpl.kt | 13 +++++++---- .../ui/home/createcapsule/adapter/SkinRVA.kt | 23 +++++++++++-------- 4 files changed, 27 insertions(+), 16 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsule2Fragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsule2Fragment.kt index a12dd365f..3ab8ca915 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsule2Fragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsule2Fragment.kt @@ -23,6 +23,7 @@ import com.droidblossom.archive.R import com.droidblossom.archive.databinding.FragmentCreateCapsule2Binding import com.droidblossom.archive.presentation.base.BaseFragment import com.droidblossom.archive.presentation.ui.home.createcapsule.adapter.SkinRVA +import com.droidblossom.archive.presentation.ui.skin.adapter.SkinMotionRVA import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch @@ -36,7 +37,9 @@ class CreateCapsule2Fragment : private lateinit var callback: OnBackPressedCallback private val skinRVA by lazy { - SkinRVA { viewModel.changeSkin(it) } + SkinRVA { previousPosition, currentPosition -> + viewModel.changeSkin(previousPosition, currentPosition) + } } //그룹캡슐이 아닐 경우 바로 엑티비티 닫기 diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModel.kt index d0951995c..833332bde 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModel.kt @@ -65,7 +65,7 @@ interface CreateCapsuleViewModel { fun openSearchSkin() fun closeSearchSkin() fun searchSkin() - fun changeSkin(skin: CapsuleSkinSummary) + fun changeSkin(previousPosition: Int?, currentPosition: Int) fun getSkinList() fun moveFinish() fun moveLocation() diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt index a2562e838..5c06c6f7b 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt @@ -236,13 +236,16 @@ class CreateCapsuleViewModelImpl @Inject constructor( } } - override fun changeSkin(skin: CapsuleSkinSummary) { - val submitList = skins.value - submitList.map { it.isClicked = false } - submitList[submitList.indexOf(skin)].isClicked = true + override fun changeSkin(previousPosition: Int?, currentPosition: Int) { viewModelScope.launch { - _skins.emit(submitList) + val newList = _skins.value + previousPosition?.let { + newList[it].isClicked = false + } + newList[currentPosition].isClicked = true + _skins.emit(newList) } + } override fun getSkinList() { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/adapter/SkinRVA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/adapter/SkinRVA.kt index 92a4d8645..774e816d3 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/adapter/SkinRVA.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/adapter/SkinRVA.kt @@ -11,28 +11,33 @@ import com.droidblossom.archive.databinding.ItemSkinBinding import com.droidblossom.archive.domain.model.common.CapsuleSkinSummary import com.droidblossom.archive.domain.model.common.Skin -class SkinRVA( val SkinFlow: (CapsuleSkinSummary) -> Unit) : +class SkinRVA( val SkinFlow: (previousPosition: Int?, currentPosition: Int) -> Unit) : ListAdapter(differ) { + private var previousClickedPosition: Int? = null inner class ItemViewHolder( private val binding: ItemSkinBinding ) : RecyclerView.ViewHolder(binding.root) { - //@SuppressLint("NotifyDataSetChanged") fun bind(data: CapsuleSkinSummary) { binding.item = data binding.root.setOnClickListener { - notifyItemChanged(currentList.indexOf( - currentList.find { - it.isClicked - } - )) - SkinFlow(data) - notifyItemChanged(currentList.indexOf(data)) + val currentClickedPosition = bindingAdapterPosition + if (currentClickedPosition != RecyclerView.NO_POSITION) { + SkinFlow(previousClickedPosition, currentClickedPosition) + previousClickedPosition?.let { previousClickedPosition -> notifyItemChanged(previousClickedPosition) } + notifyItemChanged(currentClickedPosition) + previousClickedPosition = currentClickedPosition + } } } } + override fun submitList(list: List?) { + previousClickedPosition = null + super.submitList(list) + } + override fun onCreateViewHolder( parent: ViewGroup, viewType: Int From e26a47ee7b405d31a05f66c23c89bd31f3b18236 Mon Sep 17 00:00:00 2001 From: comst19 Date: Tue, 26 Mar 2024 19:28:45 +0900 Subject: [PATCH 061/301] =?UTF-8?q?fix:=20=ED=8E=98=EC=9D=B4=EC=A7=95=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EC=8B=9C=EC=97=90=20=EC=8A=A4=ED=81=AC?= =?UTF-8?q?=EB=A1=A4=20=EB=81=9D=EC=9D=84=20=EC=9D=B8=EC=8B=9D=20=EB=AA=BB?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../createcapsule/CreateCapsule2Fragment.kt | 24 +++++++++---------- .../CreateCapsuleViewModelImpl.kt | 4 ++-- .../ui/home/createcapsule/adapter/SkinRVA.kt | 5 ---- .../presentation/ui/mypage/MyPageFragment.kt | 1 + .../ui/mypage/MyPageViewModelImpl.kt | 2 +- .../presentation/ui/skin/SkinFragment.kt | 24 +++++++++++-------- .../presentation/ui/skin/SkinViewModelImpl.kt | 9 +++---- 7 files changed, 34 insertions(+), 35 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsule2Fragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsule2Fragment.kt index 3ab8ca915..449b9af2b 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsule2Fragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsule2Fragment.kt @@ -112,25 +112,23 @@ class CreateCapsule2Fragment : } - private fun initRVA(){ + + private fun initRVA() { binding.recycleView.adapter = skinRVA - val defaultItemAnimator = DefaultItemAnimator() - defaultItemAnimator.changeDuration = 100 - binding.recycleView.itemAnimator = defaultItemAnimator binding.recycleView.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { super.onScrollStateChanged(recyclerView, newState) - val lastVisibleItemPosition = - (recyclerView.layoutManager as LinearLayoutManager?)!!.findLastCompletelyVisibleItemPosition() - val totalItemViewCount = recyclerView.adapter!!.itemCount - 1 - - if (newState == 2 && !recyclerView.canScrollVertically(1) - && lastVisibleItemPosition == totalItemViewCount - ) { - viewModel.getSkinList() + + if (newState == RecyclerView.SCROLL_STATE_IDLE || newState == RecyclerView.SCROLL_STATE_DRAGGING) { + val layoutManager = recyclerView.layoutManager as LinearLayoutManager + val totalItemCount = layoutManager.itemCount + val lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition() + + if (totalItemCount - lastVisibleItemPosition <= 5) { + viewModel.getSkinList() + } } } - }) } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt index 5c06c6f7b..14411b312 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt @@ -258,9 +258,9 @@ class CreateCapsuleViewModelImpl @Inject constructor( ) ).collect { result -> result.onSuccess { - _skins.emit(it.skins) _hasNextSkins.emit(it.hasNext) - _lastCreatedSkinTime.emit(it.skins.last().createdAt) + _skins.emit(skins.value + it.skins) + _lastCreatedSkinTime.value = _skins.value.last().createdAt }.onFail { _create2Events.emit(CreateCapsuleViewModel.Create2Event.ShowToastMessage("스킨 불러오기 실패.")) } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/adapter/SkinRVA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/adapter/SkinRVA.kt index 774e816d3..b90e99e1f 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/adapter/SkinRVA.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/adapter/SkinRVA.kt @@ -33,11 +33,6 @@ class SkinRVA( val SkinFlow: (previousPosition: Int?, currentPosition: Int) -> U } } - override fun submitList(list: List?) { - previousClickedPosition = null - super.submitList(list) - } - override fun onCreateViewHolder( parent: ViewGroup, viewType: Int diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt index 0676ae8cc..69c18a9f7 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt @@ -81,6 +81,7 @@ class MyPageFragment : binding.profileImg.layoutParams = layoutParams } + private fun initRVA() { binding.capsuleRecycleView.adapter = myCapsuleRVA binding.capsuleRecycleView.animation = null diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt index e121572fc..69adafffb 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt @@ -79,7 +79,7 @@ class MyPageViewModelImpl @Inject constructor( result.onSuccess { _hasNextPage.value = it.hasNext _myCapsules.emit(myCapsules.value + it.capsules) - _lastCreatedTime.value = _myCapsules.value.last().createdDate + _lastCreatedTime.value = myCapsules.value.last().createdDate }.onFail { _myPageEvents.emit(MyPageViewModel.MyPageEvent.ShowToastMessage("정보 불러오기 실패")) } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinFragment.kt index 39e7e4175..f0973158b 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinFragment.kt @@ -51,19 +51,21 @@ class SkinFragment : BaseFragment(R.layo binding.viewHeaderTitle.layoutParams = layoutParams } + private fun initRVA() { binding.skinRV.adapter = mySkinRVA binding.skinRV.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { super.onScrollStateChanged(recyclerView, newState) - val lastVisibleItemPosition = - (recyclerView.layoutManager as LinearLayoutManager?)!!.findLastCompletelyVisibleItemPosition() - val totalItemViewCount = recyclerView.adapter!!.itemCount - 1 - - if (newState == 2 && !recyclerView.canScrollVertically(1) - && lastVisibleItemPosition == totalItemViewCount - ) { - viewModel.getSkinList() + + if (newState == RecyclerView.SCROLL_STATE_IDLE || newState == RecyclerView.SCROLL_STATE_DRAGGING) { + val layoutManager = recyclerView.layoutManager as LinearLayoutManager + val totalItemCount = layoutManager.itemCount + val lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition() + + if (totalItemCount - lastVisibleItemPosition <= 5) { + viewModel.getSkinList() + } } } }) @@ -120,15 +122,17 @@ class SkinFragment : BaseFragment(R.layo override fun onHiddenChanged(hidden: Boolean) { super.onHiddenChanged(hidden) if (hidden) { - - } else { + Log.d("라프","히든") viewModel.clearSkins() viewModel.closeSearchSkin() + } else { + Log.d("라프","쇼") } } override fun onResume() { super.onResume() + Log.d("라프","온리쥼") viewModel.clearSkins() viewModel.closeSearchSkin() } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinViewModelImpl.kt index 6d41ed2c2..c3713a3be 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinViewModelImpl.kt @@ -1,5 +1,6 @@ package com.droidblossom.archive.presentation.ui.skin +import android.util.Log import androidx.lifecycle.viewModelScope import com.droidblossom.archive.data.dto.capsule_skin.request.CapsuleSkinsPageRequestDto import com.droidblossom.archive.domain.model.common.CapsuleSkinSummary @@ -55,13 +56,13 @@ class SkinViewModelImpl @Inject constructor( capsuleSkinsPageUseCase( CapsuleSkinsPageRequestDto( 15, - _lastCreatedSkinTime.value + lastCreatedSkinTime.value ) ).collect { result -> result.onSuccess { - _skins.emit(it.skins) - _hasNextSkins.emit(it.hasNext) - _lastCreatedSkinTime.emit(it.skins.last().createdAt) + _hasNextSkins.value = it.hasNext + _skins.emit(skins.value + it.skins) + _lastCreatedSkinTime.value = skins.value.last().createdAt }.onFail { _skinEvents.emit(SkinViewModel.SkinEvent.ShowToastMessage("스킨 불러오기 실패.")) } From d9239bd14f7d2702bf9761086e244de77b4132bc Mon Sep 17 00:00:00 2001 From: comst19 Date: Tue, 26 Mar 2024 19:37:10 +0900 Subject: [PATCH 062/301] =?UTF-8?q?fix:=20=EC=8A=A4=ED=82=A8=ED=94=84?= =?UTF-8?q?=EB=9E=98=EA=B7=B8=EB=A8=BC=ED=8A=B8=20=EC=B2=98=EC=9D=8C=20?= =?UTF-8?q?=EB=93=A4=EC=96=B4=EC=A7=84=EC=9E=85=EC=8B=9C=20=EC=8A=A4?= =?UTF-8?q?=ED=82=A8=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EB=91=90=20=EB=B2=88?= =?UTF-8?q?=20=ED=98=B8=EC=B6=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/presentation/ui/skin/SkinFragment.kt | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinFragment.kt index f0973158b..ac38ac5f3 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinFragment.kt @@ -39,7 +39,6 @@ class SkinFragment : BaseFragment(R.layo override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding.vm = viewModel - viewModel.getSkinList() initRVA() initSearchEdit() binding.createCapsuleLayout.setOnClickListener { @@ -121,18 +120,14 @@ class SkinFragment : BaseFragment(R.layo override fun onHiddenChanged(hidden: Boolean) { super.onHiddenChanged(hidden) - if (hidden) { - Log.d("라프","히든") + if (!hidden) { viewModel.clearSkins() viewModel.closeSearchSkin() - } else { - Log.d("라프","쇼") } } override fun onResume() { super.onResume() - Log.d("라프","온리쥼") viewModel.clearSkins() viewModel.closeSearchSkin() } From 8319d8aed3462471010d85cf7870ba5c03a8a323 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Tue, 26 Mar 2024 19:40:52 +0900 Subject: [PATCH 063/301] Merge branch 'feat/-F-setting_and_fcmlist' of https://github.com/tukcomCD2024/DroidBlossom into feat/-F-setting_and_fcmlist --- .../ARchive/app/src/main/AndroidManifest.xml | 6 +++-- .../presentation/ui/auth/AuthViewModelImpl.kt | 4 ++- .../createcapsule/CreateCapsuleViewModel.kt | 1 - .../CreateCapsuleViewModelImpl.kt | 1 - .../ui/home/createcapsule/adapter/SkinRVA.kt | 17 ++++++++++--- .../home/notification/NotificationActivity.kt | 25 +++++++++++++++++++ .../notification/NotificationViewModel.kt | 4 +++ .../notification/NotificationViewModelImpl.kt | 12 +++++++++ .../main/res/layout/activity_notification.xml | 18 +++++++++++++ 9 files changed, 79 insertions(+), 9 deletions(-) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationActivity.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationViewModel.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationViewModelImpl.kt create mode 100644 frontend/ARchive/app/src/main/res/layout/activity_notification.xml diff --git a/frontend/ARchive/app/src/main/AndroidManifest.xml b/frontend/ARchive/app/src/main/AndroidManifest.xml index ea4e7c61f..d7c96e0ee 100644 --- a/frontend/ARchive/app/src/main/AndroidManifest.xml +++ b/frontend/ARchive/app/src/main/AndroidManifest.xml @@ -32,6 +32,9 @@ android:theme="@style/Theme.ARchive" android:usesCleartextTraffic="true" tools:targetApi="31"> + @@ -87,7 +90,7 @@ android:exported="true" android:launchMode="singleTask" android:screenOrientation="portrait" - tools:ignore="LockedOrientationActivity"> + tools:ignore="LockedOrientationActivity" /> - + validMessageUseCase( + VerificationNumberValid(certificationNumber.value, rawPhoneNumber.value).toDto() + ).collect{ result -> result.onSuccess { dataStoreUtils.saveAccessToken(it.accessToken) dataStoreUtils.saveRefreshToken(it.refreshToken) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModel.kt index 833332bde..07eefc233 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModel.kt @@ -85,7 +85,6 @@ interface CreateCapsuleViewModel { fun coordToAddress(latitude: Double, longitude: Double) fun getDueTime(tiem:String) fun getUploadUrls(getS3UrlData : S3UrlRequest) - fun setFiles(imageFiles: List, videoFiles: List) fun closeTimeSetting() fun openTimeSetting() diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt index 14411b312..649a972fc 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt @@ -245,7 +245,6 @@ class CreateCapsuleViewModelImpl @Inject constructor( newList[currentPosition].isClicked = true _skins.emit(newList) } - } override fun getSkinList() { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/adapter/SkinRVA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/adapter/SkinRVA.kt index b90e99e1f..eb1f978b5 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/adapter/SkinRVA.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/adapter/SkinRVA.kt @@ -11,10 +11,11 @@ import com.droidblossom.archive.databinding.ItemSkinBinding import com.droidblossom.archive.domain.model.common.CapsuleSkinSummary import com.droidblossom.archive.domain.model.common.Skin -class SkinRVA( val SkinFlow: (previousPosition: Int?, currentPosition: Int) -> Unit) : +class SkinRVA(val SkinFlow: (previousPosition: Int?, currentPosition: Int) -> Unit) : ListAdapter(differ) { private var previousClickedPosition: Int? = null + inner class ItemViewHolder( private val binding: ItemSkinBinding ) : RecyclerView.ViewHolder(binding.root) { @@ -25,7 +26,9 @@ class SkinRVA( val SkinFlow: (previousPosition: Int?, currentPosition: Int) -> U val currentClickedPosition = bindingAdapterPosition if (currentClickedPosition != RecyclerView.NO_POSITION) { SkinFlow(previousClickedPosition, currentClickedPosition) - previousClickedPosition?.let { previousClickedPosition -> notifyItemChanged(previousClickedPosition) } + previousClickedPosition?.let { previousClickedPosition -> + notifyItemChanged(previousClickedPosition) + } notifyItemChanged(currentClickedPosition) previousClickedPosition = currentClickedPosition } @@ -52,11 +55,17 @@ class SkinRVA( val SkinFlow: (previousPosition: Int?, currentPosition: Int) -> U companion object { val differ = object : DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: CapsuleSkinSummary, newItem: CapsuleSkinSummary): Boolean { + override fun areItemsTheSame( + oldItem: CapsuleSkinSummary, + newItem: CapsuleSkinSummary + ): Boolean { return oldItem.id == newItem.id } - override fun areContentsTheSame(oldItem: CapsuleSkinSummary, newItem: CapsuleSkinSummary): Boolean { + override fun areContentsTheSame( + oldItem: CapsuleSkinSummary, + newItem: CapsuleSkinSummary + ): Boolean { return oldItem == newItem } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationActivity.kt new file mode 100644 index 000000000..d23ce3e1f --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationActivity.kt @@ -0,0 +1,25 @@ +package com.droidblossom.archive.presentation.ui.home.notification + +import android.os.Bundle +import android.view.View +import androidx.fragment.app.viewModels +import com.droidblossom.archive.R +import com.droidblossom.archive.databinding.ActivityNotificationBinding +import com.droidblossom.archive.presentation.base.BaseFragment +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class NotificationActivity : + BaseFragment(R.layout.activity_notification) { + + override val viewModel: NotificationViewModelImpl by viewModels() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.vm = viewModel + } + + override fun observeData() { + + } +} diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationViewModel.kt new file mode 100644 index 000000000..3b32071db --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationViewModel.kt @@ -0,0 +1,4 @@ +package com.droidblossom.archive.presentation.ui.home.notification + +interface NotificationViewModel { +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationViewModelImpl.kt new file mode 100644 index 000000000..7909bc470 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationViewModelImpl.kt @@ -0,0 +1,12 @@ +package com.droidblossom.archive.presentation.ui.home.notification + +import com.droidblossom.archive.presentation.base.BaseViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject + +@HiltViewModel +class NotificationViewModelImpl @Inject constructor( + +) : BaseViewModel(), NotificationViewModel { + +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/activity_notification.xml b/frontend/ARchive/app/src/main/res/layout/activity_notification.xml new file mode 100644 index 000000000..b1b7bad29 --- /dev/null +++ b/frontend/ARchive/app/src/main/res/layout/activity_notification.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + \ No newline at end of file From 2195f1345c657c0bc6f97638ed6a62ca48c30ba7 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Tue, 26 Mar 2024 20:09:59 +0900 Subject: [PATCH 064/301] =?UTF-8?q?fix:=20=EB=A6=AC=EC=82=AC=EC=9D=B4?= =?UTF-8?q?=ED=81=B4=EB=B7=B0=20=EB=B2=84=ED=8A=BC=20=EB=92=A4=EC=97=90=20?= =?UTF-8?q?=EC=A7=A4=EB=A6=AC=EB=8A=94=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/src/main/res/layout/fragment_create_capsule2.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_create_capsule2.xml b/frontend/ARchive/app/src/main/res/layout/fragment_create_capsule2.xml index f72f38134..1d41721fc 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_create_capsule2.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_create_capsule2.xml @@ -125,6 +125,8 @@ android:layout_height="0dp" android:layout_marginHorizontal="12dp" android:layout_marginTop="100dp" + android:paddingBottom="88dp" + android:clipToPadding="false" app:layoutManager="androidx.recyclerview.widget.GridLayoutManager" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" From b2de0e9c51594d68e0af5190d75c506a780ea284 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Thu, 28 Mar 2024 16:45:55 +0900 Subject: [PATCH 065/301] =?UTF-8?q?desgin=20:=20=EC=95=8C=EB=A6=BC?= =?UTF-8?q?=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/res/layout/activity_notification.xml | 43 +++++++ .../src/main/res/layout/item_notification.xml | 114 ++++++++++++++++++ 2 files changed, 157 insertions(+) create mode 100644 frontend/ARchive/app/src/main/res/layout/item_notification.xml diff --git a/frontend/ARchive/app/src/main/res/layout/activity_notification.xml b/frontend/ARchive/app/src/main/res/layout/activity_notification.xml index b1b7bad29..689a29df9 100644 --- a/frontend/ARchive/app/src/main/res/layout/activity_notification.xml +++ b/frontend/ARchive/app/src/main/res/layout/activity_notification.xml @@ -4,15 +4,58 @@ xmlns:tools="http://schemas.android.com/tools"> + + + + + + + + + \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/item_notification.xml b/frontend/ARchive/app/src/main/res/layout/item_notification.xml new file mode 100644 index 000000000..7c8a62912 --- /dev/null +++ b/frontend/ARchive/app/src/main/res/layout/item_notification.xml @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From c0abf67406f4d06e6abac7cf68674a3b481b325d Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Thu, 28 Mar 2024 16:46:27 +0900 Subject: [PATCH 066/301] =?UTF-8?q?feat=20:=20=EC=95=8C=EB=A6=BC=20RVA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notification/adapter/NotificationRVA.kt | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/adapter/NotificationRVA.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/adapter/NotificationRVA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/adapter/NotificationRVA.kt new file mode 100644 index 000000000..fce42f8d5 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/adapter/NotificationRVA.kt @@ -0,0 +1,52 @@ +package com.droidblossom.archive.presentation.ui.home.notification.adapter + +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.droidblossom.archive.databinding.ItemNotificationBinding +import com.droidblossom.archive.domain.model.member.NotificationModel + +class NotificationRVA(val onClick: () -> Unit) : + ListAdapter(differ) { + + inner class ItemViewHolder( + private val binding: ItemNotificationBinding + ) : RecyclerView.ViewHolder(binding.root) { + fun bind(data: NotificationModel) { + binding.item = data + binding.root.setOnClickListener { + onClick() + } + } + } + + companion object { + val differ = object : DiffUtil.ItemCallback() { + override fun areItemsTheSame( + oldItem: NotificationModel, newItem: NotificationModel + ): Boolean { + return oldItem == newItem + } + + override fun areContentsTheSame( + oldItem: NotificationModel, newItem: NotificationModel + ): Boolean { + return oldItem == newItem + } + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder { + return ItemViewHolder( + ItemNotificationBinding.inflate( + LayoutInflater.from(parent.context), parent, false + ) + ) + } + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + holder.bind(getItem(position)) + } +} \ No newline at end of file From f890b5d64cd273d729e3e336f5710d3abc264a18 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Thu, 28 Mar 2024 16:46:51 +0900 Subject: [PATCH 067/301] =?UTF-8?q?feat:=20=EC=95=8C=EB=A6=BC=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EB=B7=B0=EB=AA=A8=EB=8D=B8,=20=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20=EB=B0=94=EC=9D=B8=EB=94=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../home/notification/NotificationActivity.kt | 81 +++++++++++++++++-- .../notification/NotificationViewModel.kt | 15 ++++ .../notification/NotificationViewModelImpl.kt | 50 +++++++++++- 3 files changed, 139 insertions(+), 7 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationActivity.kt index d23ce3e1f..55dadf868 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationActivity.kt @@ -1,25 +1,94 @@ package com.droidblossom.archive.presentation.ui.home.notification +import android.content.Context +import android.content.Intent import android.os.Bundle -import android.view.View -import androidx.fragment.app.viewModels +import android.view.ViewGroup +import androidx.activity.viewModels +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView import com.droidblossom.archive.R import com.droidblossom.archive.databinding.ActivityNotificationBinding -import com.droidblossom.archive.presentation.base.BaseFragment +import com.droidblossom.archive.presentation.base.BaseActivity +import com.droidblossom.archive.presentation.ui.home.notification.adapter.NotificationRVA import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.launch @AndroidEntryPoint class NotificationActivity : - BaseFragment(R.layout.activity_notification) { + BaseActivity(R.layout.activity_notification) { override val viewModel: NotificationViewModelImpl by viewModels() - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) + private val notificationRVA by lazy { + NotificationRVA { + + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) binding.vm = viewModel + initView() + viewModel.getNotificationPage() + } + + private fun initView() { + val layoutParams = binding.backBtn.layoutParams as ViewGroup.MarginLayoutParams + layoutParams.topMargin += getStatusBarHeight() + binding.backBtn.layoutParams = layoutParams + + binding.backBtn.setOnClickListener { + finish() + } + binding.rv.adapter = notificationRVA + binding.rv.addOnScrollListener(object : RecyclerView.OnScrollListener() { + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + super.onScrollStateChanged(recyclerView, newState) + val lastVisibleItemPosition = + (recyclerView.layoutManager as LinearLayoutManager?)!!.findLastCompletelyVisibleItemPosition() + val totalItemViewCount = recyclerView.adapter!!.itemCount - 1 + + if (newState == 2 && !recyclerView.canScrollVertically(1) + && lastVisibleItemPosition == totalItemViewCount + ) { + viewModel.getNotificationPage() + } + } + }) } override fun observeData() { + lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.notifications.collect { notifications -> + notificationRVA.submitList(notifications) + } + } + } + + lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.notificationEvent.collect { event -> + when (event) { + is NotificationViewModel.NotificationEvent.ShowToastMessage -> { + showToastMessage(event.message) + } + + else -> {} + } + } + } + } + } + + companion object { + const val NOTIFICATION = "notificaiton" + fun newIntent(context: Context) = + Intent(context, NotificationActivity::class.java) } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationViewModel.kt index 3b32071db..c712fe28f 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationViewModel.kt @@ -1,4 +1,19 @@ package com.droidblossom.archive.presentation.ui.home.notification +import com.droidblossom.archive.domain.model.member.NotificationModel +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.StateFlow + interface NotificationViewModel { + val notificationEvent: SharedFlow + + val notifications: StateFlow> + val hasNextPage: StateFlow + val lastCreatedTime: StateFlow + + fun getNotificationPage() + + sealed class NotificationEvent { + data class ShowToastMessage(val message: String) : NotificationEvent() + } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationViewModelImpl.kt index 7909bc470..df3cdc1c8 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationViewModelImpl.kt @@ -1,12 +1,60 @@ package com.droidblossom.archive.presentation.ui.home.notification +import androidx.lifecycle.viewModelScope +import com.droidblossom.archive.domain.model.member.NotificationModel +import com.droidblossom.archive.domain.usecase.member.GetNotificationsUseCase import com.droidblossom.archive.presentation.base.BaseViewModel +import com.droidblossom.archive.util.DateUtils +import com.droidblossom.archive.util.onFail +import com.droidblossom.archive.util.onSuccess import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel class NotificationViewModelImpl @Inject constructor( - + private val getNotificationsUseCase: GetNotificationsUseCase ) : BaseViewModel(), NotificationViewModel { + private val _notificationEvent = MutableSharedFlow() + override val notificationEvent: SharedFlow + get() = _notificationEvent.asSharedFlow() + + private val _notifications = MutableStateFlow(listOf()) + + override val notifications: StateFlow> + get() = _notifications + + private val _hasNextPage = MutableStateFlow(true) + override val hasNextPage: StateFlow + get() = _hasNextPage + + private val _lastCreatedTime = MutableStateFlow(DateUtils.dataServerString) + override val lastCreatedTime: StateFlow + get() = _lastCreatedTime + + override fun getNotificationPage() { + viewModelScope.launch { + if (hasNextPage.value) { + getNotificationsUseCase(15, lastCreatedTime.value).collect { result -> + result.onSuccess { + _hasNextPage.value = it.hasNext + _notifications.emit(notifications.value + it.notifications) + _lastCreatedTime.value = it.notifications.last().createdAt + }.onFail { + _notificationEvent.emit( + NotificationViewModel.NotificationEvent.ShowToastMessage( + "알림 불러오기 실패" + ) + ) + } + } + } + } + } } \ No newline at end of file From a6052740df86e5de693594c4d7d7fcbb3f6fbc41 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Thu, 28 Mar 2024 16:47:01 +0900 Subject: [PATCH 068/301] =?UTF-8?q?feat:=20=EC=95=8C=EB=A6=BC=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/presentation/ui/home/HomeFragment.kt | 6 ++++++ .../archive/presentation/ui/home/HomeViewModel.kt | 2 ++ .../archive/presentation/ui/home/HomeViewModelImpl.kt | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt index ac6dc16b2..12e992b35 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt @@ -17,6 +17,7 @@ import com.droidblossom.archive.domain.model.common.CapsuleMarker import com.droidblossom.archive.presentation.base.BaseFragment import com.droidblossom.archive.presentation.ui.home.createcapsule.CreateCapsuleActivity import com.droidblossom.archive.presentation.ui.home.dialog.CapsulePreviewDialogFragment +import com.droidblossom.archive.presentation.ui.home.notification.NotificationActivity import com.droidblossom.archive.util.CapsuleTypeUtils import com.droidblossom.archive.util.LocationUtil import com.naver.maps.geometry.LatLng @@ -107,11 +108,16 @@ class HomeFragment : BaseFragment(R.layo repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.homeEvents.collect { event -> when (event) { + HomeViewModel.HomeEvent.GoNotification -> { + startActivity(NotificationActivity.newIntent(requireContext())) + } is HomeViewModel.HomeEvent.ShowCapsulePreviewDialog -> { val sheet = CapsulePreviewDialogFragment.newInstance("-1",event.capsuleId, event.capsuleType, false) sheet.show(parentFragmentManager, "CapsulePreviewDialog") } + + else -> {} } } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeViewModel.kt index 9b6d36bb9..228d57ac9 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeViewModel.kt @@ -43,6 +43,8 @@ interface HomeViewModel { } sealed class HomeEvent{ + + object GoNotification : HomeEvent() data class ShowCapsulePreviewDialog(val capsuleId: String, val capsuleType: String) : HomeEvent() } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeViewModelImpl.kt index df93325af..944a31f11 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeViewModelImpl.kt @@ -92,7 +92,7 @@ class HomeViewModelImpl @Inject constructor( override fun clickNotificationBtn() { viewModelScope.launch { - _existsNotification.emit(!existsNotification.value) + _homeEvents.emit(HomeViewModel.HomeEvent.GoNotification) } } From ce592abd199a67ff37f1c3f5087d51881ed5345f Mon Sep 17 00:00:00 2001 From: comst19 Date: Fri, 29 Mar 2024 03:09:05 +0900 Subject: [PATCH 069/301] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC=EC=9A=94?= =?UTF-8?q?=EC=B2=AD=20API=EC=9D=98=20Dto,=20model?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/friend/request/FriendReqRequestDto.kt | 5 +++++ .../friend/response/FriendReqStatusResponseDto.kt | 15 +++++++++++++++ .../domain/model/friend/FriendReqRequest.kt | 11 +++++++++++ .../model/friend/FriendReqStatusResponse.kt | 6 ++++++ 4 files changed, 37 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/request/FriendReqRequestDto.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendReqStatusResponseDto.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendReqRequest.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendReqStatusResponse.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/request/FriendReqRequestDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/request/FriendReqRequestDto.kt new file mode 100644 index 000000000..07e5bf9a1 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/request/FriendReqRequestDto.kt @@ -0,0 +1,5 @@ +package com.droidblossom.archive.data.dto.friend.request + +data class FriendReqRequestDto( + val friendId : Double +) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendReqStatusResponseDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendReqStatusResponseDto.kt new file mode 100644 index 000000000..58272de4a --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendReqStatusResponseDto.kt @@ -0,0 +1,15 @@ +package com.droidblossom.archive.data.dto.friend.response + +import com.droidblossom.archive.domain.model.friend.FriendReqStatusResponse +import java.io.Serializable + +data class FriendReqStatusResponseDto ( + val httpStatus : String, + val result : String +) : Serializable { + + fun toModel() = FriendReqStatusResponse( + httpStatus = this.httpStatus, + result = this.result, + ) +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendReqRequest.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendReqRequest.kt new file mode 100644 index 000000000..9c5b535f0 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendReqRequest.kt @@ -0,0 +1,11 @@ +package com.droidblossom.archive.domain.model.friend + +import com.droidblossom.archive.data.dto.friend.request.FriendReqRequestDto + +data class FriendReqRequest( + val friendId : Double +){ + fun toDto() = FriendReqRequestDto( + friendId = this.friendId, + ) +} diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendReqStatusResponse.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendReqStatusResponse.kt new file mode 100644 index 000000000..ead942596 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendReqStatusResponse.kt @@ -0,0 +1,6 @@ +package com.droidblossom.archive.domain.model.friend + +data class FriendReqStatusResponse ( + val httpStatus : String, + val result : String +) \ No newline at end of file From 51c8170edc4848fdfbbbd39eb833d813d64de4d4 Mon Sep 17 00:00:00 2001 From: comst19 Date: Fri, 29 Mar 2024 03:09:55 +0900 Subject: [PATCH 070/301] =?UTF-8?q?feat:=20FriendService,=20=EC=B9=9C?= =?UTF-8?q?=EA=B5=AC=EC=9A=94=EC=B2=AD=20api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/source/remote/api/FriendService.kt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt new file mode 100644 index 000000000..bc2d59820 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt @@ -0,0 +1,17 @@ +package com.droidblossom.archive.data.source.remote.api + +import com.droidblossom.archive.data.dto.friend.request.FriendReqRequestDto +import retrofit2.http.Body +import retrofit2.http.POST +import retrofit2.Response +import com.droidblossom.archive.data.dto.ResponseBody +import com.droidblossom.archive.data.dto.friend.response.FriendReqStatusResponseDto +import retrofit2.http.Query + +interface FriendService { + + @POST("friends/{friend_id/request}") + suspend fun postFriendsRequestApi( + @Query("friend_id") friendId : Double, + ) : Response> +} \ No newline at end of file From 82ae1e9709dc68b1cd0c64847f43849445c4d15b Mon Sep 17 00:00:00 2001 From: comst19 Date: Fri, 29 Mar 2024 03:10:49 +0900 Subject: [PATCH 071/301] =?UTF-8?q?feat:=20FriendRepository=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/repository/FriendRepositoryImpl.kt | 20 +++++++++++++++++++ .../domain/repository/FriendRepository.kt | 10 ++++++++++ 2 files changed, 30 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/FriendRepositoryImpl.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/FriendRepository.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/FriendRepositoryImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/FriendRepositoryImpl.kt new file mode 100644 index 000000000..c67d46dca --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/FriendRepositoryImpl.kt @@ -0,0 +1,20 @@ +package com.droidblossom.archive.data.repository + +import com.droidblossom.archive.data.dto.ResponseBody +import com.droidblossom.archive.data.dto.capsule_skin.response.CapsuleSkinsPageResponseDto +import com.droidblossom.archive.data.dto.friend.request.FriendReqRequestDto +import com.droidblossom.archive.data.dto.friend.response.FriendReqStatusResponseDto +import com.droidblossom.archive.data.source.remote.api.FriendService +import com.droidblossom.archive.domain.model.friend.FriendReqStatusResponse +import com.droidblossom.archive.domain.repository.FriendRepository +import com.droidblossom.archive.util.RetrofitResult +import com.droidblossom.archive.util.apiHandler +import javax.inject.Inject + +class FriendRepositoryImpl @Inject constructor( + private val api : FriendService +) : FriendRepository { + override suspend fun postFriendFriendsRequest(request: FriendReqRequestDto): RetrofitResult { + return apiHandler({ api.postFriendsRequestApi(request.friendId) }) { response: ResponseBody -> response.result.toModel() } + } +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/FriendRepository.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/FriendRepository.kt new file mode 100644 index 000000000..929009171 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/FriendRepository.kt @@ -0,0 +1,10 @@ +package com.droidblossom.archive.domain.repository + +import com.droidblossom.archive.data.dto.friend.request.FriendReqRequestDto +import com.droidblossom.archive.domain.model.friend.FriendReqStatusResponse +import com.droidblossom.archive.util.RetrofitResult + +interface FriendRepository { + + suspend fun postFriendFriendsRequest(request: FriendReqRequestDto) : RetrofitResult +} \ No newline at end of file From ebac1484a6d2f3425d85e4040514a43195c59726 Mon Sep 17 00:00:00 2001 From: comst19 Date: Fri, 29 Mar 2024 03:11:16 +0900 Subject: [PATCH 072/301] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC=EC=9A=94?= =?UTF-8?q?=EC=B2=AD=20UseCase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../usecase/friend/FriendsRequestUseCase.kt | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsRequestUseCase.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsRequestUseCase.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsRequestUseCase.kt new file mode 100644 index 000000000..7a86aa36b --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsRequestUseCase.kt @@ -0,0 +1,31 @@ +package com.droidblossom.archive.domain.usecase.friend + +import android.util.Log +import com.droidblossom.archive.domain.model.friend.FriendReqRequest +import com.droidblossom.archive.domain.repository.FriendRepository +import com.droidblossom.archive.util.onException +import com.droidblossom.archive.util.onFail +import com.droidblossom.archive.util.onSuccess +import kotlinx.coroutines.flow.flow +import javax.inject.Inject + +class FriendsRequestUseCase @Inject constructor( + private val repository: FriendRepository +) { + + suspend operator fun invoke(request : FriendReqRequest) = + flow { + try { + emit(repository.postFriendFriendsRequest(request.toDto()).onSuccess { + + }.onFail { + + }.onException { + throw Exception(it) + }) + }catch (e : Exception){ + Log.d("예외확인", "$e") + e.printStackTrace() + } + } +} \ No newline at end of file From 5995d0377a2a05f301911b928ca150f92a3c667a Mon Sep 17 00:00:00 2001 From: comst19 Date: Fri, 29 Mar 2024 03:12:08 +0900 Subject: [PATCH 073/301] =?UTF-8?q?feat:=20FriendService,=20FriendReposito?= =?UTF-8?q?ry=20=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=A3=BC=EC=9E=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/droidblossom/archive/di/RepositoryModule.kt | 7 +++++++ .../main/java/com/droidblossom/archive/di/ServiceModule.kt | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/di/RepositoryModule.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/di/RepositoryModule.kt index efcae288e..b650a97ea 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/di/RepositoryModule.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/di/RepositoryModule.kt @@ -3,6 +3,7 @@ package com.droidblossom.archive.di import com.droidblossom.archive.data.repository.AuthRepositoryImpl import com.droidblossom.archive.data.repository.CapsuleRepositoryImpl import com.droidblossom.archive.data.repository.CapsuleSkinRepositoryImpl +import com.droidblossom.archive.data.repository.FriendRepositoryImpl import com.droidblossom.archive.data.repository.MemberRepositoryImpl import com.droidblossom.archive.data.repository.KakaoRepositoryImpl import com.droidblossom.archive.data.repository.S3RepositoryImpl @@ -10,6 +11,7 @@ import com.droidblossom.archive.data.repository.SecretRepositoryImpl import com.droidblossom.archive.data.source.remote.api.AuthService import com.droidblossom.archive.data.source.remote.api.CapsuleService import com.droidblossom.archive.data.source.remote.api.CapsuleSkinService +import com.droidblossom.archive.data.source.remote.api.FriendService import com.droidblossom.archive.data.source.remote.api.MemberService import com.droidblossom.archive.data.source.remote.api.KakaoService import com.droidblossom.archive.data.source.remote.api.S3Service @@ -17,6 +19,7 @@ import com.droidblossom.archive.data.source.remote.api.SecretService import com.droidblossom.archive.domain.repository.AuthRepository import com.droidblossom.archive.domain.repository.CapsuleRepository import com.droidblossom.archive.domain.repository.CapsuleSkinRepository +import com.droidblossom.archive.domain.repository.FriendRepository import com.droidblossom.archive.domain.repository.MemberRepository import com.droidblossom.archive.domain.repository.KakaoRepository import com.droidblossom.archive.domain.repository.S3Repository @@ -64,4 +67,8 @@ object RepositoryModule { @ViewModelScoped fun providesCapsuleSkinRepository(api : CapsuleSkinService) : CapsuleSkinRepository = CapsuleSkinRepositoryImpl(api) + @Provides + @ViewModelScoped + fun providesFriendRepository(api : FriendService) : FriendRepository = FriendRepositoryImpl(api) + } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/di/ServiceModule.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/di/ServiceModule.kt index 6f4dc0f5f..8ca410362 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/di/ServiceModule.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/di/ServiceModule.kt @@ -3,6 +3,7 @@ package com.droidblossom.archive.di import com.droidblossom.archive.data.source.remote.api.AuthService import com.droidblossom.archive.data.source.remote.api.CapsuleService import com.droidblossom.archive.data.source.remote.api.CapsuleSkinService +import com.droidblossom.archive.data.source.remote.api.FriendService import com.droidblossom.archive.data.source.remote.api.MemberService import com.droidblossom.archive.data.source.remote.api.S3Service import com.droidblossom.archive.data.source.remote.api.SecretService @@ -41,4 +42,8 @@ object ServiceModule { @Singleton @Provides fun providesCapsuleSkinService(retrofit: Retrofit) : CapsuleSkinService = retrofit.create(CapsuleSkinService::class.java) + + @Singleton + @Provides + fun providesFriendService(retrofit : Retrofit) : FriendService = retrofit.create(FriendService::class.java) } \ No newline at end of file From a899c3a7d119dc6c14fbeb65040cc4d675f2c837 Mon Sep 17 00:00:00 2001 From: comst19 Date: Fri, 29 Mar 2024 03:23:28 +0900 Subject: [PATCH 074/301] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC=20=EC=9A=94?= =?UTF-8?q?=EC=B2=AD=20=EC=88=98=EB=9D=BD=20API=20dto,model?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/friend/request/FriendAcceptRequestDto.kt | 5 +++++ .../domain/model/friend/FriendAcceptRequest.kt | 12 ++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/request/FriendAcceptRequestDto.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendAcceptRequest.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/request/FriendAcceptRequestDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/request/FriendAcceptRequestDto.kt new file mode 100644 index 000000000..dd874ff77 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/request/FriendAcceptRequestDto.kt @@ -0,0 +1,5 @@ +package com.droidblossom.archive.data.dto.friend.request + +data class FriendAcceptRequestDto ( + val friendId : Double +) \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendAcceptRequest.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendAcceptRequest.kt new file mode 100644 index 000000000..7d39dec71 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendAcceptRequest.kt @@ -0,0 +1,12 @@ +package com.droidblossom.archive.domain.model.friend + +import com.droidblossom.archive.data.dto.friend.request.FriendAcceptRequestDto + + +data class FriendAcceptRequest ( + val friendId : Double +){ + fun toDto() = FriendAcceptRequestDto( + friendId = this.friendId, + ) +} From bdbfb042564ef66f2f513a842c811a105eef1887 Mon Sep 17 00:00:00 2001 From: comst19 Date: Fri, 29 Mar 2024 03:23:54 +0900 Subject: [PATCH 075/301] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC=20=EC=9A=94?= =?UTF-8?q?=EC=B2=AD=20=EC=88=98=EB=9D=BD=20API=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/data/source/remote/api/FriendService.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt index bc2d59820..71a921b82 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt @@ -14,4 +14,9 @@ interface FriendService { suspend fun postFriendsRequestApi( @Query("friend_id") friendId : Double, ) : Response> + + @POST("friends/{friend_id/accept-request}") + suspend fun postFriendsAcceptRequestApi( + @Query("friend_id") friendId : Double, + ) : Response> } \ No newline at end of file From 6c8784eba73aee26bd635c951d3da8312fcd7bad Mon Sep 17 00:00:00 2001 From: comst19 Date: Fri, 29 Mar 2024 03:25:13 +0900 Subject: [PATCH 076/301] feat: postFriendsAcceptRequest() --- .../archive/data/repository/FriendRepositoryImpl.kt | 8 +++++++- .../archive/domain/repository/FriendRepository.kt | 5 ++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/FriendRepositoryImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/FriendRepositoryImpl.kt index c67d46dca..c323b7ddf 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/FriendRepositoryImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/FriendRepositoryImpl.kt @@ -2,6 +2,8 @@ package com.droidblossom.archive.data.repository import com.droidblossom.archive.data.dto.ResponseBody import com.droidblossom.archive.data.dto.capsule_skin.response.CapsuleSkinsPageResponseDto +import com.droidblossom.archive.data.dto.common.toModel +import com.droidblossom.archive.data.dto.friend.request.FriendAcceptRequestDto import com.droidblossom.archive.data.dto.friend.request.FriendReqRequestDto import com.droidblossom.archive.data.dto.friend.response.FriendReqStatusResponseDto import com.droidblossom.archive.data.source.remote.api.FriendService @@ -14,7 +16,11 @@ import javax.inject.Inject class FriendRepositoryImpl @Inject constructor( private val api : FriendService ) : FriendRepository { - override suspend fun postFriendFriendsRequest(request: FriendReqRequestDto): RetrofitResult { + override suspend fun postFriendsRequest(request: FriendReqRequestDto): RetrofitResult { return apiHandler({ api.postFriendsRequestApi(request.friendId) }) { response: ResponseBody -> response.result.toModel() } } + + override suspend fun postFriendsAcceptRequest(request: FriendAcceptRequestDto): RetrofitResult { + return apiHandler({ api.postFriendsAcceptRequestApi(request.friendId) }) {response: ResponseBody -> response.result.toModel()} + } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/FriendRepository.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/FriendRepository.kt index 929009171..6764f5bf0 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/FriendRepository.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/FriendRepository.kt @@ -1,10 +1,13 @@ package com.droidblossom.archive.domain.repository +import com.droidblossom.archive.data.dto.friend.request.FriendAcceptRequestDto import com.droidblossom.archive.data.dto.friend.request.FriendReqRequestDto import com.droidblossom.archive.domain.model.friend.FriendReqStatusResponse import com.droidblossom.archive.util.RetrofitResult interface FriendRepository { - suspend fun postFriendFriendsRequest(request: FriendReqRequestDto) : RetrofitResult + suspend fun postFriendsRequest(request: FriendReqRequestDto) : RetrofitResult + + suspend fun postFriendsAcceptRequest(request: FriendAcceptRequestDto) : RetrofitResult } \ No newline at end of file From b6d4535f6a2ba26b99c2362ef7ccc795e255b997 Mon Sep 17 00:00:00 2001 From: comst19 Date: Fri, 29 Mar 2024 03:26:11 +0900 Subject: [PATCH 077/301] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC=20=EC=9A=94?= =?UTF-8?q?=EC=B2=AD=20=EC=88=98=EB=9D=BD=20UseCase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../friend/FriendAcceptRequestUseCase.kt | 31 +++++++++++++++++++ .../usecase/friend/FriendsRequestUseCase.kt | 2 +- 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendAcceptRequestUseCase.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendAcceptRequestUseCase.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendAcceptRequestUseCase.kt new file mode 100644 index 000000000..4a5c6b625 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendAcceptRequestUseCase.kt @@ -0,0 +1,31 @@ +package com.droidblossom.archive.domain.usecase.friend + +import android.util.Log +import com.droidblossom.archive.domain.model.friend.FriendAcceptRequest +import com.droidblossom.archive.domain.model.friend.FriendReqRequest +import com.droidblossom.archive.domain.repository.FriendRepository +import com.droidblossom.archive.util.onException +import com.droidblossom.archive.util.onFail +import com.droidblossom.archive.util.onSuccess +import kotlinx.coroutines.flow.flow +import javax.inject.Inject + +class FriendAcceptRequestUseCase @Inject constructor( + private val repository: FriendRepository +) { + suspend operator fun invoke(request : FriendAcceptRequest) = + flow { + try { + emit(repository.postFriendsAcceptRequest(request.toDto()).onSuccess { + + }.onFail { + + }.onException { + throw Exception(it) + }) + }catch (e : Exception){ + Log.d("예외확인", "$e") + e.printStackTrace() + } + } +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsRequestUseCase.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsRequestUseCase.kt index 7a86aa36b..734841e30 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsRequestUseCase.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsRequestUseCase.kt @@ -16,7 +16,7 @@ class FriendsRequestUseCase @Inject constructor( suspend operator fun invoke(request : FriendReqRequest) = flow { try { - emit(repository.postFriendFriendsRequest(request.toDto()).onSuccess { + emit(repository.postFriendsRequest(request.toDto()).onSuccess { }.onFail { From 05e95fb9d1bd553434550e77287ff23468f7fe5c Mon Sep 17 00:00:00 2001 From: comst19 Date: Fri, 29 Mar 2024 04:13:10 +0900 Subject: [PATCH 078/301] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20tag,=20phone=20API=20dto,=20model?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../request/FriendsSearchPhoneRequestDto.kt | 5 +++++ .../friend/request/FriendsSearchRequestDto.kt | 5 +++++ .../response/FriendsSearchPhoneResponseDto.kt | 13 +++++++++++++ .../response/FriendsSearchResponseDto.kt | 19 +++++++++++++++++++ .../model/friend/FriendsSearchPhoneRequest.kt | 11 +++++++++++ .../friend/FriendsSearchPhoneResponse.kt | 6 ++++++ .../model/friend/FriendsSearchRequest.kt | 11 +++++++++++ .../model/friend/FriendsSearchResponse.kt | 8 ++++++++ ...Case.kt => FriendsAcceptRequestUseCase.kt} | 0 9 files changed, 78 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/request/FriendsSearchPhoneRequestDto.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/request/FriendsSearchRequestDto.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendsSearchPhoneResponseDto.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendsSearchResponseDto.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendsSearchPhoneRequest.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendsSearchPhoneResponse.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendsSearchRequest.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendsSearchResponse.kt rename frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/{FriendAcceptRequestUseCase.kt => FriendsAcceptRequestUseCase.kt} (100%) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/request/FriendsSearchPhoneRequestDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/request/FriendsSearchPhoneRequestDto.kt new file mode 100644 index 000000000..8d1ec0556 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/request/FriendsSearchPhoneRequestDto.kt @@ -0,0 +1,5 @@ +package com.droidblossom.archive.data.dto.friend.request + +data class FriendsSearchPhoneRequestDto( + val phones : List +) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/request/FriendsSearchRequestDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/request/FriendsSearchRequestDto.kt new file mode 100644 index 000000000..31e865c2b --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/request/FriendsSearchRequestDto.kt @@ -0,0 +1,5 @@ +package com.droidblossom.archive.data.dto.friend.request + +data class FriendsSearchRequestDto ( + val friendTag : String +) \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendsSearchPhoneResponseDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendsSearchPhoneResponseDto.kt new file mode 100644 index 000000000..54534b438 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendsSearchPhoneResponseDto.kt @@ -0,0 +1,13 @@ +package com.droidblossom.archive.data.dto.friend.response + +import com.droidblossom.archive.domain.model.friend.FriendsSearchPhoneResponse +import java.io.Serializable + +data class FriendsSearchPhoneResponseDto( + val friends : List +) : Serializable { + + fun toModel() = FriendsSearchPhoneResponse( + friends = this.friends.map { it.toModel() }, + ) +} diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendsSearchResponseDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendsSearchResponseDto.kt new file mode 100644 index 000000000..e6124d942 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendsSearchResponseDto.kt @@ -0,0 +1,19 @@ +package com.droidblossom.archive.data.dto.friend.response + +import com.droidblossom.archive.domain.model.friend.FriendsSearchResponse +import java.io.Serializable + +data class FriendsSearchResponseDto( + val id: Long, + val profileUrl: String, + val nickname: String, + val isFriend: Boolean +) : Serializable { + + fun toModel() = FriendsSearchResponse( + id = this.id, + profileUrl = this.profileUrl, + nickname = this.nickname, + isFriend = this.isFriend, + ) +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendsSearchPhoneRequest.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendsSearchPhoneRequest.kt new file mode 100644 index 000000000..9cce2c986 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendsSearchPhoneRequest.kt @@ -0,0 +1,11 @@ +package com.droidblossom.archive.domain.model.friend + +import com.droidblossom.archive.data.dto.friend.request.FriendsSearchPhoneRequestDto + +data class FriendsSearchPhoneRequest ( + val phones : List +){ + fun toDto() = FriendsSearchPhoneRequestDto( + phones = this.phones, + ) +} diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendsSearchPhoneResponse.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendsSearchPhoneResponse.kt new file mode 100644 index 000000000..616f3eb3e --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendsSearchPhoneResponse.kt @@ -0,0 +1,6 @@ +package com.droidblossom.archive.domain.model.friend + + +data class FriendsSearchPhoneResponse ( + val friends : List +) \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendsSearchRequest.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendsSearchRequest.kt new file mode 100644 index 000000000..b976d11bb --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendsSearchRequest.kt @@ -0,0 +1,11 @@ +package com.droidblossom.archive.domain.model.friend + +import com.droidblossom.archive.data.dto.friend.request.FriendsSearchRequestDto + +data class FriendsSearchRequest ( + val friendTag : String +){ + fun toDto() = FriendsSearchRequestDto( + friendTag = this.friendTag, + ) +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendsSearchResponse.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendsSearchResponse.kt new file mode 100644 index 000000000..40f5b66bf --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendsSearchResponse.kt @@ -0,0 +1,8 @@ +package com.droidblossom.archive.domain.model.friend + +data class FriendsSearchResponse ( + val id : Long, + val profileUrl : String, + val nickname : String, + val isFriend : Boolean +) \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendAcceptRequestUseCase.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsAcceptRequestUseCase.kt similarity index 100% rename from frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendAcceptRequestUseCase.kt rename to frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsAcceptRequestUseCase.kt From 7ddb380417d6ab24ed8c3dfcfd67005c8b0dcf5a Mon Sep 17 00:00:00 2001 From: comst19 Date: Fri, 29 Mar 2024 04:13:45 +0900 Subject: [PATCH 079/301] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20tag,=20phone=20api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/repository/FriendRepositoryImpl.kt | 14 ++++++++++++++ .../archive/domain/repository/FriendRepository.kt | 7 +++++++ 2 files changed, 21 insertions(+) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/FriendRepositoryImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/FriendRepositoryImpl.kt index c323b7ddf..f0ac520d5 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/FriendRepositoryImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/FriendRepositoryImpl.kt @@ -5,9 +5,15 @@ import com.droidblossom.archive.data.dto.capsule_skin.response.CapsuleSkinsPageR import com.droidblossom.archive.data.dto.common.toModel import com.droidblossom.archive.data.dto.friend.request.FriendAcceptRequestDto import com.droidblossom.archive.data.dto.friend.request.FriendReqRequestDto +import com.droidblossom.archive.data.dto.friend.request.FriendsSearchPhoneRequestDto import com.droidblossom.archive.data.dto.friend.response.FriendReqStatusResponseDto +import com.droidblossom.archive.data.dto.friend.response.FriendsSearchPhoneResponseDto +import com.droidblossom.archive.data.dto.friend.response.FriendsSearchResponseDto import com.droidblossom.archive.data.source.remote.api.FriendService import com.droidblossom.archive.domain.model.friend.FriendReqStatusResponse +import com.droidblossom.archive.domain.model.friend.FriendsSearchPhoneRequest +import com.droidblossom.archive.domain.model.friend.FriendsSearchPhoneResponse +import com.droidblossom.archive.domain.model.friend.FriendsSearchResponse import com.droidblossom.archive.domain.repository.FriendRepository import com.droidblossom.archive.util.RetrofitResult import com.droidblossom.archive.util.apiHandler @@ -23,4 +29,12 @@ class FriendRepositoryImpl @Inject constructor( override suspend fun postFriendsAcceptRequest(request: FriendAcceptRequestDto): RetrofitResult { return apiHandler({ api.postFriendsAcceptRequestApi(request.friendId) }) {response: ResponseBody -> response.result.toModel()} } + + override suspend fun postFriendsSearch(): RetrofitResult { + return apiHandler({ api.postFriendsSearchApi() }) {response: ResponseBody -> response.result.toModel()} + } + + override suspend fun postFriendsSearchPhone(request: FriendsSearchPhoneRequestDto): RetrofitResult { + return apiHandler({ api.postFriendsSearchPhoneApi(request) }) {response: ResponseBody -> response.result.toModel()} + } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/FriendRepository.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/FriendRepository.kt index 6764f5bf0..159a13c43 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/FriendRepository.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/FriendRepository.kt @@ -2,7 +2,10 @@ package com.droidblossom.archive.domain.repository import com.droidblossom.archive.data.dto.friend.request.FriendAcceptRequestDto import com.droidblossom.archive.data.dto.friend.request.FriendReqRequestDto +import com.droidblossom.archive.data.dto.friend.request.FriendsSearchPhoneRequestDto import com.droidblossom.archive.domain.model.friend.FriendReqStatusResponse +import com.droidblossom.archive.domain.model.friend.FriendsSearchPhoneResponse +import com.droidblossom.archive.domain.model.friend.FriendsSearchResponse import com.droidblossom.archive.util.RetrofitResult interface FriendRepository { @@ -10,4 +13,8 @@ interface FriendRepository { suspend fun postFriendsRequest(request: FriendReqRequestDto) : RetrofitResult suspend fun postFriendsAcceptRequest(request: FriendAcceptRequestDto) : RetrofitResult + + suspend fun postFriendsSearch() : RetrofitResult + suspend fun postFriendsSearchPhone(request : FriendsSearchPhoneRequestDto) : RetrofitResult + } \ No newline at end of file From 55d475e398d0ea357eae8334f79d6261f914f9b8 Mon Sep 17 00:00:00 2001 From: comst19 Date: Fri, 29 Mar 2024 04:14:00 +0900 Subject: [PATCH 080/301] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20tag,=20phone=20api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/source/remote/api/FriendService.kt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt index 71a921b82..aabf3e402 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt @@ -5,7 +5,10 @@ import retrofit2.http.Body import retrofit2.http.POST import retrofit2.Response import com.droidblossom.archive.data.dto.ResponseBody +import com.droidblossom.archive.data.dto.friend.request.FriendsSearchPhoneRequestDto import com.droidblossom.archive.data.dto.friend.response.FriendReqStatusResponseDto +import com.droidblossom.archive.data.dto.friend.response.FriendsSearchPhoneResponseDto +import com.droidblossom.archive.data.dto.friend.response.FriendsSearchResponseDto import retrofit2.http.Query interface FriendService { @@ -19,4 +22,15 @@ interface FriendService { suspend fun postFriendsAcceptRequestApi( @Query("friend_id") friendId : Double, ) : Response> + + // 쿼리 쪽이없음 + @POST("friends/search") + suspend fun postFriendsSearchApi( + + ) : Response> + + @POST("friends/search/phone") + suspend fun postFriendsSearchPhoneApi( + @Body request : FriendsSearchPhoneRequestDto + ) : Response> } \ No newline at end of file From f17a16d9636a28d82f8f95ea9eeff7920761d2b2 Mon Sep 17 00:00:00 2001 From: comst19 Date: Fri, 29 Mar 2024 04:14:41 +0900 Subject: [PATCH 081/301] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20tag,=20phone=20UseCase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../friend/FriendsSearchPhoneUseCase.kt | 30 ++++++++++++++++++ .../usecase/friend/FriendsSearchUseCase.kt | 31 +++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsSearchPhoneUseCase.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsSearchUseCase.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsSearchPhoneUseCase.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsSearchPhoneUseCase.kt new file mode 100644 index 000000000..b71b3f2b4 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsSearchPhoneUseCase.kt @@ -0,0 +1,30 @@ +package com.droidblossom.archive.domain.usecase.friend + +import android.util.Log +import com.droidblossom.archive.domain.model.friend.FriendsSearchPhoneRequest +import com.droidblossom.archive.domain.repository.FriendRepository +import com.droidblossom.archive.util.onException +import com.droidblossom.archive.util.onFail +import com.droidblossom.archive.util.onSuccess +import kotlinx.coroutines.flow.flow +import javax.inject.Inject + +class FriendsSearchPhoneUseCase @Inject constructor( + private val repository: FriendRepository +){ + suspend operator fun invoke(request : FriendsSearchPhoneRequest) = + flow { + try { + emit(repository.postFriendsSearchPhone(request.toDto()).onSuccess { + + }.onFail { + + }.onException { + throw Exception(it) + }) + }catch (e : Exception){ + Log.d("예외확인", "$e") + e.printStackTrace() + } + } +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsSearchUseCase.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsSearchUseCase.kt new file mode 100644 index 000000000..abedc2714 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsSearchUseCase.kt @@ -0,0 +1,31 @@ +package com.droidblossom.archive.domain.usecase.friend + +import android.util.Log +import com.droidblossom.archive.domain.model.friend.FriendsSearchPhoneRequest +import com.droidblossom.archive.domain.model.friend.FriendsSearchRequest +import com.droidblossom.archive.domain.repository.FriendRepository +import com.droidblossom.archive.util.onException +import com.droidblossom.archive.util.onFail +import com.droidblossom.archive.util.onSuccess +import kotlinx.coroutines.flow.flow +import javax.inject.Inject + +class FriendsSearchUseCase@Inject constructor( + private val repository: FriendRepository +) { + suspend operator fun invoke(request : FriendsSearchRequest) = + flow { + try { + emit(repository.postFriendsSearch().onSuccess { + + }.onFail { + + }.onException { + throw Exception(it) + }) + }catch (e : Exception){ + Log.d("예외확인", "$e") + e.printStackTrace() + } + } +} \ No newline at end of file From 54c29b9911d9fb37bd2795a9d5a6cf9a8809c039 Mon Sep 17 00:00:00 2001 From: comst19 Date: Fri, 29 Mar 2024 04:16:16 +0900 Subject: [PATCH 082/301] rename: FriendAcceptRequestUseCase -> FriendsAcceptRequestUseCase --- .../domain/usecase/friend/FriendsAcceptRequestUseCase.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsAcceptRequestUseCase.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsAcceptRequestUseCase.kt index 4a5c6b625..38283d001 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsAcceptRequestUseCase.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsAcceptRequestUseCase.kt @@ -2,7 +2,6 @@ package com.droidblossom.archive.domain.usecase.friend import android.util.Log import com.droidblossom.archive.domain.model.friend.FriendAcceptRequest -import com.droidblossom.archive.domain.model.friend.FriendReqRequest import com.droidblossom.archive.domain.repository.FriendRepository import com.droidblossom.archive.util.onException import com.droidblossom.archive.util.onFail @@ -10,7 +9,7 @@ import com.droidblossom.archive.util.onSuccess import kotlinx.coroutines.flow.flow import javax.inject.Inject -class FriendAcceptRequestUseCase @Inject constructor( +class FriendsAcceptRequestUseCase @Inject constructor( private val repository: FriendRepository ) { suspend operator fun invoke(request : FriendAcceptRequest) = From 6cf3f146aa89394b51e7064edb9fc9750f60f2b0 Mon Sep 17 00:00:00 2001 From: comst19 Date: Fri, 29 Mar 2024 10:10:39 +0900 Subject: [PATCH 083/301] fix: Query -> Path --- .../archive/data/repository/FriendRepositoryImpl.kt | 5 +++-- .../archive/data/source/remote/api/FriendService.kt | 8 ++++---- .../archive/domain/repository/FriendRepository.kt | 3 ++- .../archive/domain/usecase/friend/FriendsSearchUseCase.kt | 4 ++-- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/FriendRepositoryImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/FriendRepositoryImpl.kt index f0ac520d5..3be45d97f 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/FriendRepositoryImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/FriendRepositoryImpl.kt @@ -6,6 +6,7 @@ import com.droidblossom.archive.data.dto.common.toModel import com.droidblossom.archive.data.dto.friend.request.FriendAcceptRequestDto import com.droidblossom.archive.data.dto.friend.request.FriendReqRequestDto import com.droidblossom.archive.data.dto.friend.request.FriendsSearchPhoneRequestDto +import com.droidblossom.archive.data.dto.friend.request.FriendsSearchRequestDto import com.droidblossom.archive.data.dto.friend.response.FriendReqStatusResponseDto import com.droidblossom.archive.data.dto.friend.response.FriendsSearchPhoneResponseDto import com.droidblossom.archive.data.dto.friend.response.FriendsSearchResponseDto @@ -30,8 +31,8 @@ class FriendRepositoryImpl @Inject constructor( return apiHandler({ api.postFriendsAcceptRequestApi(request.friendId) }) {response: ResponseBody -> response.result.toModel()} } - override suspend fun postFriendsSearch(): RetrofitResult { - return apiHandler({ api.postFriendsSearchApi() }) {response: ResponseBody -> response.result.toModel()} + override suspend fun postFriendsSearch(request: FriendsSearchRequestDto): RetrofitResult { + return apiHandler({ api.postFriendsSearchApi(request.friendTag) }) {response: ResponseBody -> response.result.toModel()} } override suspend fun postFriendsSearchPhone(request: FriendsSearchPhoneRequestDto): RetrofitResult { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt index aabf3e402..6cd161ed6 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt @@ -9,24 +9,24 @@ import com.droidblossom.archive.data.dto.friend.request.FriendsSearchPhoneReques import com.droidblossom.archive.data.dto.friend.response.FriendReqStatusResponseDto import com.droidblossom.archive.data.dto.friend.response.FriendsSearchPhoneResponseDto import com.droidblossom.archive.data.dto.friend.response.FriendsSearchResponseDto +import retrofit2.http.Path import retrofit2.http.Query interface FriendService { @POST("friends/{friend_id/request}") suspend fun postFriendsRequestApi( - @Query("friend_id") friendId : Double, + @Path("friend_id") friendId : Double, ) : Response> @POST("friends/{friend_id/accept-request}") suspend fun postFriendsAcceptRequestApi( - @Query("friend_id") friendId : Double, + @Path("friend_id") friendId : Double, ) : Response> - // 쿼리 쪽이없음 @POST("friends/search") suspend fun postFriendsSearchApi( - + @Query("friend-tag") friendTag : String ) : Response> @POST("friends/search/phone") diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/FriendRepository.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/FriendRepository.kt index 159a13c43..b6ade0cb9 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/FriendRepository.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/FriendRepository.kt @@ -3,6 +3,7 @@ package com.droidblossom.archive.domain.repository import com.droidblossom.archive.data.dto.friend.request.FriendAcceptRequestDto import com.droidblossom.archive.data.dto.friend.request.FriendReqRequestDto import com.droidblossom.archive.data.dto.friend.request.FriendsSearchPhoneRequestDto +import com.droidblossom.archive.data.dto.friend.request.FriendsSearchRequestDto import com.droidblossom.archive.domain.model.friend.FriendReqStatusResponse import com.droidblossom.archive.domain.model.friend.FriendsSearchPhoneResponse import com.droidblossom.archive.domain.model.friend.FriendsSearchResponse @@ -14,7 +15,7 @@ interface FriendRepository { suspend fun postFriendsAcceptRequest(request: FriendAcceptRequestDto) : RetrofitResult - suspend fun postFriendsSearch() : RetrofitResult + suspend fun postFriendsSearch(request: FriendsSearchRequestDto) : RetrofitResult suspend fun postFriendsSearchPhone(request : FriendsSearchPhoneRequestDto) : RetrofitResult } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsSearchUseCase.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsSearchUseCase.kt index abedc2714..cdd70c91e 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsSearchUseCase.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsSearchUseCase.kt @@ -10,13 +10,13 @@ import com.droidblossom.archive.util.onSuccess import kotlinx.coroutines.flow.flow import javax.inject.Inject -class FriendsSearchUseCase@Inject constructor( +class FriendsSearchUseCase @Inject constructor( private val repository: FriendRepository ) { suspend operator fun invoke(request : FriendsSearchRequest) = flow { try { - emit(repository.postFriendsSearch().onSuccess { + emit(repository.postFriendsSearch(request.toDto()).onSuccess { }.onFail { From 139df651aa0f4027ce9ad8dca3ccd3410a34cb30 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Fri, 29 Mar 2024 17:39:30 +0900 Subject: [PATCH 084/301] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC=20=EC=88=98?= =?UTF-8?q?=EB=9D=BD,=20=EC=9A=94=EC=B2=AD=20FCM?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../customview/SkinDialogFragment.kt | 4 +++ .../archive/presentation/ui/MainActivity.kt | 9 +++++ .../util/MyFirebaseMessagingService.kt | 36 +++++++++++++++---- .../layout/fragment_skin_preview_dialog.xml | 13 +++++++ 4 files changed, 56 insertions(+), 6 deletions(-) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/SkinDialogFragment.kt create mode 100644 frontend/ARchive/app/src/main/res/layout/fragment_skin_preview_dialog.xml diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/SkinDialogFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/SkinDialogFragment.kt new file mode 100644 index 000000000..a9afa2cf4 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/SkinDialogFragment.kt @@ -0,0 +1,4 @@ +package com.droidblossom.archive.presentation.customview + +class SkinDialogFragment { +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt index 748e959ce..4534c0752 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt @@ -131,6 +131,15 @@ class MainActivity : BaseActivity(R.layout.activi MyFirebaseMessagingService.FragmentDestination.SKIN_FRAGMENT.name -> { showFragment(SkinFragment.newIntent(), SkinFragment.TAG) } + + MyFirebaseMessagingService.FragmentDestination.FRIEND_REQUEST_DIALOG.name -> { + + } + + MyFirebaseMessagingService.FragmentDestination.FRIEND_ACCEPT_DIALOG.name -> { + + } + else -> { } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/MyFirebaseMessagingService.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/MyFirebaseMessagingService.kt index b7f61fd06..d99ddbdc3 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/MyFirebaseMessagingService.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/MyFirebaseMessagingService.kt @@ -54,20 +54,31 @@ class MyFirebaseMessagingService : FirebaseMessagingService() { // 데이터 메시지 비어있음 Log.d(TAG, "메시지를 수신하지 못했습니다.") } - + } private fun handleNotification(remoteMessage: RemoteMessage) { var channelName = "공지사항" when(remoteMessage.data["topic"]){ - FcmTopic.CAPSULE_SKIN.toString() -> { + FcmTopic.CAPSULE_SKIN.name -> { channelName = "캡슐 스킨 생성" + sendNotification(remoteMessage, FcmTopic.CAPSULE_SKIN.name, channelName) + } + + FcmTopic.FRIEND_REQUEST.name -> { + channelName = "친구 요청" + sendNotification(remoteMessage, FcmTopic.FRIEND_REQUEST.name, channelName) + } + + FcmTopic.FRIEND_ACCEPT.name -> { + channelName = "친구 요청 수락" + sendNotification(remoteMessage, FcmTopic.FRIEND_ACCEPT.name, channelName) } - else -> {} + else -> {} } - sendNotification(remoteMessage, FcmTopic.CAPSULE_SKIN.toString(), channelName) + } @@ -82,6 +93,15 @@ class MyFirebaseMessagingService : FirebaseMessagingService() { FcmTopic.CAPSULE_SKIN.name -> { intent.putExtra("fragmentDestination", FragmentDestination.SKIN_FRAGMENT.name) } + + FcmTopic.FRIEND_REQUEST.name -> { + intent.putExtra("fragmentDestination", FragmentDestination.FRIEND_REQUEST_DIALOG.name) + } + + FcmTopic.FRIEND_ACCEPT.name -> { + intent.putExtra("fragmentDestination", FragmentDestination.FRIEND_ACCEPT_DIALOG.name) + } + else -> { } @@ -157,10 +177,14 @@ class MyFirebaseMessagingService : FirebaseMessagingService() { } enum class FragmentDestination { - SKIN_FRAGMENT + SKIN_FRAGMENT, + FRIEND_REQUEST_DIALOG, + FRIEND_ACCEPT_DIALOG, } enum class FcmTopic{ - CAPSULE_SKIN + CAPSULE_SKIN, + FRIEND_REQUEST, + FRIEND_ACCEPT, } } diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_skin_preview_dialog.xml b/frontend/ARchive/app/src/main/res/layout/fragment_skin_preview_dialog.xml new file mode 100644 index 000000000..67ed5d4c8 --- /dev/null +++ b/frontend/ARchive/app/src/main/res/layout/fragment_skin_preview_dialog.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + \ No newline at end of file From 83f7f4a3ef6260ce647f2074d4c1a051fa11f0b1 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Fri, 29 Mar 2024 19:50:28 +0900 Subject: [PATCH 085/301] =?UTF-8?q?feat=20:=20=EC=B9=9C=EA=B5=AC=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=95=20model/dto?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/friend/response/FriendResponseDto.kt | 17 +++++++++++++++++ .../friend/response/FriendsPageResponseDto.kt | 13 +++++++++++++ .../archive/domain/model/friend/Friend.kt | 8 ++++++++ .../archive/domain/model/friend/FriendsPage.kt | 6 ++++++ 4 files changed, 44 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendResponseDto.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendsPageResponseDto.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/Friend.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendsPage.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendResponseDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendResponseDto.kt new file mode 100644 index 000000000..9d02821b7 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendResponseDto.kt @@ -0,0 +1,17 @@ +package com.droidblossom.archive.data.dto.friend.response + +import com.droidblossom.archive.domain.model.friend.Friend + +data class FriendResponseDto( + val createdAt: String, + val id: Int, + val nickname: String, + val profileUrl: String +) { + fun toModel() = Friend( + createdAt = this.createdAt, + id = this.id, + nickname = this.nickname, + profileUrl = this.profileUrl + ) +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendsPageResponseDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendsPageResponseDto.kt new file mode 100644 index 000000000..51ccca46f --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendsPageResponseDto.kt @@ -0,0 +1,13 @@ +package com.droidblossom.archive.data.dto.friend.response + +import com.droidblossom.archive.domain.model.friend.FriendsPage + +data class FriendsPageResponseDto( + val friends: List, + val hasNext: Boolean +) { + fun toModel() = FriendsPage( + friends = this.friends.map { it.toModel() }, + hasNext = this.hasNext + ) +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/Friend.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/Friend.kt new file mode 100644 index 000000000..c3295b2be --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/Friend.kt @@ -0,0 +1,8 @@ +package com.droidblossom.archive.domain.model.friend + +data class Friend( + val createdAt: String, + val id: Int, + val nickname: String, + val profileUrl: String +) \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendsPage.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendsPage.kt new file mode 100644 index 000000000..eb580c5d3 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendsPage.kt @@ -0,0 +1,6 @@ +package com.droidblossom.archive.domain.model.friend + +data class FriendsPage( + val friends: List, + val hasNext: Boolean +) \ No newline at end of file From 44ccb2297ab77dd21b920872560e4032bfac3d31 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Fri, 29 Mar 2024 19:55:44 +0900 Subject: [PATCH 086/301] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=20=ED=8E=98=EC=9D=B4=EC=A7=95=20api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/data/source/remote/api/FriendService.kt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt index 6cd161ed6..d12c090f1 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt @@ -7,8 +7,10 @@ import retrofit2.Response import com.droidblossom.archive.data.dto.ResponseBody import com.droidblossom.archive.data.dto.friend.request.FriendsSearchPhoneRequestDto import com.droidblossom.archive.data.dto.friend.response.FriendReqStatusResponseDto +import com.droidblossom.archive.data.dto.friend.response.FriendsPageResponseDto import com.droidblossom.archive.data.dto.friend.response.FriendsSearchPhoneResponseDto import com.droidblossom.archive.data.dto.friend.response.FriendsSearchResponseDto +import retrofit2.http.GET import retrofit2.http.Path import retrofit2.http.Query @@ -33,4 +35,10 @@ interface FriendService { suspend fun postFriendsSearchPhoneApi( @Body request : FriendsSearchPhoneRequestDto ) : Response> + + @GET("friends") + suspend fun getFriends( + @Query("size") size : Int, + @Query("createdAt") createdAt : String, + ) : Response> } \ No newline at end of file From d98f3c351116a84ff2eeb17dbf4b62467ad6a6d4 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Fri, 29 Mar 2024 19:59:31 +0900 Subject: [PATCH 087/301] =?UTF-8?q?feat=20:=20=EC=B9=9C=EA=B5=AC=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=20=ED=8E=98=EC=9D=B4=EC=A7=95=20repository?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/data/repository/FriendRepositoryImpl.kt | 6 ++++++ .../archive/data/source/remote/api/FriendService.kt | 2 +- .../archive/domain/repository/FriendRepository.kt | 3 ++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/FriendRepositoryImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/FriendRepositoryImpl.kt index 3be45d97f..cbf1c9bba 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/FriendRepositoryImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/FriendRepositoryImpl.kt @@ -8,10 +8,12 @@ import com.droidblossom.archive.data.dto.friend.request.FriendReqRequestDto import com.droidblossom.archive.data.dto.friend.request.FriendsSearchPhoneRequestDto import com.droidblossom.archive.data.dto.friend.request.FriendsSearchRequestDto import com.droidblossom.archive.data.dto.friend.response.FriendReqStatusResponseDto +import com.droidblossom.archive.data.dto.friend.response.FriendsPageResponseDto import com.droidblossom.archive.data.dto.friend.response.FriendsSearchPhoneResponseDto import com.droidblossom.archive.data.dto.friend.response.FriendsSearchResponseDto import com.droidblossom.archive.data.source.remote.api.FriendService import com.droidblossom.archive.domain.model.friend.FriendReqStatusResponse +import com.droidblossom.archive.domain.model.friend.FriendsPage import com.droidblossom.archive.domain.model.friend.FriendsSearchPhoneRequest import com.droidblossom.archive.domain.model.friend.FriendsSearchPhoneResponse import com.droidblossom.archive.domain.model.friend.FriendsSearchResponse @@ -38,4 +40,8 @@ class FriendRepositoryImpl @Inject constructor( override suspend fun postFriendsSearchPhone(request: FriendsSearchPhoneRequestDto): RetrofitResult { return apiHandler({ api.postFriendsSearchPhoneApi(request) }) {response: ResponseBody -> response.result.toModel()} } + + override suspend fun getFriendsPage(size: Int, createdAt: String): RetrofitResult { + return apiHandler({api.getFriendsPageApi(size, createdAt)}) {response: ResponseBody -> response.result.toModel()} + } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt index d12c090f1..1d07caf99 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt @@ -37,7 +37,7 @@ interface FriendService { ) : Response> @GET("friends") - suspend fun getFriends( + suspend fun getFriendsPageApi( @Query("size") size : Int, @Query("createdAt") createdAt : String, ) : Response> diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/FriendRepository.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/FriendRepository.kt index b6ade0cb9..fbde66f94 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/FriendRepository.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/FriendRepository.kt @@ -5,6 +5,7 @@ import com.droidblossom.archive.data.dto.friend.request.FriendReqRequestDto import com.droidblossom.archive.data.dto.friend.request.FriendsSearchPhoneRequestDto import com.droidblossom.archive.data.dto.friend.request.FriendsSearchRequestDto import com.droidblossom.archive.domain.model.friend.FriendReqStatusResponse +import com.droidblossom.archive.domain.model.friend.FriendsPage import com.droidblossom.archive.domain.model.friend.FriendsSearchPhoneResponse import com.droidblossom.archive.domain.model.friend.FriendsSearchResponse import com.droidblossom.archive.util.RetrofitResult @@ -17,5 +18,5 @@ interface FriendRepository { suspend fun postFriendsSearch(request: FriendsSearchRequestDto) : RetrofitResult suspend fun postFriendsSearchPhone(request : FriendsSearchPhoneRequestDto) : RetrofitResult - + suspend fun getFriendsPage(size: Int ,createdAt : String) : RetrofitResult } \ No newline at end of file From f32e07444fe34039b505ce407b957e46eb2c26ed Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Fri, 29 Mar 2024 20:01:30 +0900 Subject: [PATCH 088/301] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=20=ED=8E=98=EC=9D=B4=EC=A7=80=20usecase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../usecase/friend/FriendsPageUseCase.kt | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsPageUseCase.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsPageUseCase.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsPageUseCase.kt new file mode 100644 index 000000000..ca3b8469b --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsPageUseCase.kt @@ -0,0 +1,31 @@ +package com.droidblossom.archive.domain.usecase.friend + +import android.util.Log +import com.droidblossom.archive.domain.model.friend.FriendsSearchPhoneRequest +import com.droidblossom.archive.domain.model.friend.FriendsSearchRequest +import com.droidblossom.archive.domain.repository.FriendRepository +import com.droidblossom.archive.util.onException +import com.droidblossom.archive.util.onFail +import com.droidblossom.archive.util.onSuccess +import kotlinx.coroutines.flow.flow +import javax.inject.Inject + +class FriendsPageUseCase @Inject constructor( + private val repository: FriendRepository +) { + suspend operator fun invoke(size: Int, createdAt: String) = + flow { + try { + emit(repository.getFriendsPage(size, createdAt).onSuccess { + + }.onFail { + + }.onException { + throw Exception(it) + }) + } catch (e: Exception) { + Log.d("예외확인", "$e") + e.printStackTrace() + } + } +} \ No newline at end of file From 9e1a34a252d0eaec1a644023e653b5c1b6d01e15 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Fri, 29 Mar 2024 20:06:23 +0900 Subject: [PATCH 089/301] =?UTF-8?q?feat=20:=20=EC=B9=9C=EA=B5=AC=20?= =?UTF-8?q?=EC=9A=94=EC=B2=AD=20=ED=8E=98=EC=9D=B4=EC=A7=80=20api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/data/source/remote/api/FriendService.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt index 1d07caf99..d7c78fa55 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt @@ -41,4 +41,10 @@ interface FriendService { @Query("size") size : Int, @Query("createdAt") createdAt : String, ) : Response> + + @GET("friends/requests") + suspend fun getFriendsRequestsPageApi( + @Query("size") size : Int, + @Query("createdAt") createdAt : String, + ) : Response> } \ No newline at end of file From 5985dac1d1a2f150954788f83d79f89c1333c097 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Fri, 29 Mar 2024 20:08:28 +0900 Subject: [PATCH 090/301] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC=EC=9A=94?= =?UTF-8?q?=EC=B2=AD=20repository?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/data/repository/FriendRepositoryImpl.kt | 4 ++++ .../archive/domain/repository/FriendRepository.kt | 1 + 2 files changed, 5 insertions(+) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/FriendRepositoryImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/FriendRepositoryImpl.kt index cbf1c9bba..66a87f7bd 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/FriendRepositoryImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/FriendRepositoryImpl.kt @@ -44,4 +44,8 @@ class FriendRepositoryImpl @Inject constructor( override suspend fun getFriendsPage(size: Int, createdAt: String): RetrofitResult { return apiHandler({api.getFriendsPageApi(size, createdAt)}) {response: ResponseBody -> response.result.toModel()} } + + override suspend fun getFriendsRequestsPage(size: Int, createdAt: String): RetrofitResult { + return apiHandler({api.getFriendsRequestsPageApi(size, createdAt)}) {response: ResponseBody -> response.result.toModel()} + } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/FriendRepository.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/FriendRepository.kt index fbde66f94..2aad065ea 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/FriendRepository.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/FriendRepository.kt @@ -19,4 +19,5 @@ interface FriendRepository { suspend fun postFriendsSearch(request: FriendsSearchRequestDto) : RetrofitResult suspend fun postFriendsSearchPhone(request : FriendsSearchPhoneRequestDto) : RetrofitResult suspend fun getFriendsPage(size: Int ,createdAt : String) : RetrofitResult + suspend fun getFriendsRequestsPage(size: Int, createdAt: String) : RetrofitResult } \ No newline at end of file From 5434488c6e4a177278c13f519d27c649a194fcb4 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Fri, 29 Mar 2024 20:09:34 +0900 Subject: [PATCH 091/301] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC=20=EC=9A=94?= =?UTF-8?q?=EC=B2=AD=20=ED=8E=98=EC=9D=B4=EC=A7=95=20usecase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../friend/FriendsRequestsPageUseCase.kt | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsRequestsPageUseCase.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsRequestsPageUseCase.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsRequestsPageUseCase.kt new file mode 100644 index 000000000..76d70f699 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsRequestsPageUseCase.kt @@ -0,0 +1,31 @@ +package com.droidblossom.archive.domain.usecase.friend + +import android.util.Log +import com.droidblossom.archive.domain.model.friend.FriendsSearchPhoneRequest +import com.droidblossom.archive.domain.model.friend.FriendsSearchRequest +import com.droidblossom.archive.domain.repository.FriendRepository +import com.droidblossom.archive.util.onException +import com.droidblossom.archive.util.onFail +import com.droidblossom.archive.util.onSuccess +import kotlinx.coroutines.flow.flow +import javax.inject.Inject + +class FriendsRequestsPageUseCase @Inject constructor( + private val repository: FriendRepository +) { + suspend operator fun invoke(size: Int, createdAt: String) = + flow { + try { + emit(repository.getFriendsRequestsPage(size, createdAt).onSuccess { + + }.onFail { + + }.onException { + throw Exception(it) + }) + } catch (e: Exception) { + Log.d("예외확인", "$e") + e.printStackTrace() + } + } +} \ No newline at end of file From d0df8f815697091ddc2a6356e0df3846dcfb76dd Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Fri, 29 Mar 2024 20:15:21 +0900 Subject: [PATCH 092/301] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/data/source/remote/api/FriendService.kt | 6 ++++++ .../archive/domain/usecase/friend/FriendsPageUseCase.kt | 2 -- .../domain/usecase/friend/FriendsRequestsPageUseCase.kt | 2 -- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt index d7c78fa55..8ec6f485a 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt @@ -10,6 +10,7 @@ import com.droidblossom.archive.data.dto.friend.response.FriendReqStatusResponse import com.droidblossom.archive.data.dto.friend.response.FriendsPageResponseDto import com.droidblossom.archive.data.dto.friend.response.FriendsSearchPhoneResponseDto import com.droidblossom.archive.data.dto.friend.response.FriendsSearchResponseDto +import retrofit2.http.DELETE import retrofit2.http.GET import retrofit2.http.Path import retrofit2.http.Query @@ -47,4 +48,9 @@ interface FriendService { @Query("size") size : Int, @Query("createdAt") createdAt : String, ) : Response> + + @DELETE("friends/{friend_id}") + suspend fun deleteFriendApi( + @Path("friend_id") friendId : Long, + ) : Response> } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsPageUseCase.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsPageUseCase.kt index ca3b8469b..56990988a 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsPageUseCase.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsPageUseCase.kt @@ -1,8 +1,6 @@ package com.droidblossom.archive.domain.usecase.friend import android.util.Log -import com.droidblossom.archive.domain.model.friend.FriendsSearchPhoneRequest -import com.droidblossom.archive.domain.model.friend.FriendsSearchRequest import com.droidblossom.archive.domain.repository.FriendRepository import com.droidblossom.archive.util.onException import com.droidblossom.archive.util.onFail diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsRequestsPageUseCase.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsRequestsPageUseCase.kt index 76d70f699..7c733e7c4 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsRequestsPageUseCase.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsRequestsPageUseCase.kt @@ -1,8 +1,6 @@ package com.droidblossom.archive.domain.usecase.friend import android.util.Log -import com.droidblossom.archive.domain.model.friend.FriendsSearchPhoneRequest -import com.droidblossom.archive.domain.model.friend.FriendsSearchRequest import com.droidblossom.archive.domain.repository.FriendRepository import com.droidblossom.archive.util.onException import com.droidblossom.archive.util.onFail From 28e01504ee6ac6d5c6c6316efb197a97c580b179 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Fri, 29 Mar 2024 20:18:04 +0900 Subject: [PATCH 093/301] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20repository?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/data/repository/FriendRepositoryImpl.kt | 4 ++++ .../archive/domain/repository/FriendRepository.kt | 1 + 2 files changed, 5 insertions(+) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/FriendRepositoryImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/FriendRepositoryImpl.kt index 66a87f7bd..582c914b3 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/FriendRepositoryImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/FriendRepositoryImpl.kt @@ -48,4 +48,8 @@ class FriendRepositoryImpl @Inject constructor( override suspend fun getFriendsRequestsPage(size: Int, createdAt: String): RetrofitResult { return apiHandler({api.getFriendsRequestsPageApi(size, createdAt)}) {response: ResponseBody -> response.result.toModel()} } + + override suspend fun deleteFriend(friendId: Long): RetrofitResult { + return apiHandler({api.deleteFriendApi(friendId)}) {response: ResponseBody -> response.result.toModel()} + } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/FriendRepository.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/FriendRepository.kt index 2aad065ea..91bfe9e2e 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/FriendRepository.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/FriendRepository.kt @@ -20,4 +20,5 @@ interface FriendRepository { suspend fun postFriendsSearchPhone(request : FriendsSearchPhoneRequestDto) : RetrofitResult suspend fun getFriendsPage(size: Int ,createdAt : String) : RetrofitResult suspend fun getFriendsRequestsPage(size: Int, createdAt: String) : RetrofitResult + suspend fun deleteFriend(friendId: Long) : RetrofitResult } \ No newline at end of file From 2f1e2d6d970806c27236a563d97170ec8f98e2e9 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Fri, 29 Mar 2024 20:23:41 +0900 Subject: [PATCH 094/301] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20usecase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../usecase/friend/FriendDeleteUseCase.kt | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendDeleteUseCase.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendDeleteUseCase.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendDeleteUseCase.kt new file mode 100644 index 000000000..098abf9a9 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendDeleteUseCase.kt @@ -0,0 +1,30 @@ +package com.droidblossom.archive.domain.usecase.friend + +import android.util.Log +import com.droidblossom.archive.domain.model.friend.FriendAcceptRequest +import com.droidblossom.archive.domain.repository.FriendRepository +import com.droidblossom.archive.util.onException +import com.droidblossom.archive.util.onFail +import com.droidblossom.archive.util.onSuccess +import kotlinx.coroutines.flow.flow +import javax.inject.Inject + +class FriendDeleteUseCase @Inject constructor( + private val repository: FriendRepository +) { + suspend operator fun invoke(friendId: Long) = + flow { + try { + emit(repository.deleteFriend(friendId).onSuccess { + + }.onFail { + + }.onException { + throw Exception(it) + }) + }catch (e : Exception){ + Log.d("예외확인", "$e") + e.printStackTrace() + } + } +} \ No newline at end of file From 39d8d37daf5dd63c903f9f1705cf1654d62fe883 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Fri, 29 Mar 2024 20:37:48 +0900 Subject: [PATCH 095/301] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC=20=EC=9A=94?= =?UTF-8?q?=EC=B2=AD=20=EA=B1=B0=EC=A0=88=20api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/data/source/remote/api/FriendService.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt index 8ec6f485a..b40436da3 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt @@ -53,4 +53,9 @@ interface FriendService { suspend fun deleteFriendApi( @Path("friend_id") friendId : Long, ) : Response> + + @DELETE("friends/{friend_id}/deny-request") + suspend fun deleteFriendDenyRequestApi( + @Path("friend_id") friendId : Long, + ) : Response> } \ No newline at end of file From e17cecdeaf563252778c559aafe0b17cec24cb10 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Fri, 29 Mar 2024 20:39:05 +0900 Subject: [PATCH 096/301] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC=20=EC=9A=94?= =?UTF-8?q?=EC=B2=9C=20=EA=B1=B0=EC=A0=88=20repositry?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/data/repository/FriendRepositoryImpl.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/FriendRepositoryImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/FriendRepositoryImpl.kt index 582c914b3..a619f9526 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/FriendRepositoryImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/FriendRepositoryImpl.kt @@ -50,6 +50,10 @@ class FriendRepositoryImpl @Inject constructor( } override suspend fun deleteFriend(friendId: Long): RetrofitResult { - return apiHandler({api.deleteFriendApi(friendId)}) {response: ResponseBody -> response.result.toModel()} + return apiHandler({api.deleteFriendApi(friendId)}) {response: ResponseBody -> response.result.toModel()} + } + + override suspend fun deleteFriendDeny(friendId: Long): RetrofitResult { + return apiHandler({api.deleteFriendDenyRequestApi(friendId)}) {response: ResponseBody -> response.result.toModel()} } } \ No newline at end of file From 57cf4c6645ade585aeb1288aa17c4654f0747f9c Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Fri, 29 Mar 2024 20:39:59 +0900 Subject: [PATCH 097/301] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC=20=EC=9A=94?= =?UTF-8?q?=EC=B2=AD=20=EA=B1=B0=EC=A0=88=20usecase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/repository/FriendRepository.kt | 3 +- .../friend/FriendDenyRequestUseCase.kt | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendDenyRequestUseCase.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/FriendRepository.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/FriendRepository.kt index 91bfe9e2e..3c04d094b 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/FriendRepository.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/FriendRepository.kt @@ -13,12 +13,11 @@ import com.droidblossom.archive.util.RetrofitResult interface FriendRepository { suspend fun postFriendsRequest(request: FriendReqRequestDto) : RetrofitResult - suspend fun postFriendsAcceptRequest(request: FriendAcceptRequestDto) : RetrofitResult - suspend fun postFriendsSearch(request: FriendsSearchRequestDto) : RetrofitResult suspend fun postFriendsSearchPhone(request : FriendsSearchPhoneRequestDto) : RetrofitResult suspend fun getFriendsPage(size: Int ,createdAt : String) : RetrofitResult suspend fun getFriendsRequestsPage(size: Int, createdAt: String) : RetrofitResult suspend fun deleteFriend(friendId: Long) : RetrofitResult + suspend fun deleteFriendDeny(friendId: Long) : RetrofitResult } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendDenyRequestUseCase.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendDenyRequestUseCase.kt new file mode 100644 index 000000000..45dab6896 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendDenyRequestUseCase.kt @@ -0,0 +1,30 @@ +package com.droidblossom.archive.domain.usecase.friend + +import android.util.Log +import com.droidblossom.archive.domain.model.friend.FriendAcceptRequest +import com.droidblossom.archive.domain.repository.FriendRepository +import com.droidblossom.archive.util.onException +import com.droidblossom.archive.util.onFail +import com.droidblossom.archive.util.onSuccess +import kotlinx.coroutines.flow.flow +import javax.inject.Inject + +class FriendDenyRequestUseCase @Inject constructor( + private val repository: FriendRepository +) { + suspend operator fun invoke(friendId: Long) = + flow { + try { + emit(repository.deleteFriendDeny(friendId).onSuccess { + + }.onFail { + + }.onException { + throw Exception(it) + }) + }catch (e : Exception){ + Log.d("예외확인", "$e") + e.printStackTrace() + } + } +} \ No newline at end of file From 8bc00acd41ab5bfb93e2c3f0ed679f4f285c677b Mon Sep 17 00:00:00 2001 From: comst19 Date: Sun, 31 Mar 2024 14:11:52 +0900 Subject: [PATCH 098/301] =?UTF-8?q?build:=20=EC=95=B1=20=EC=95=84=EC=9D=B4?= =?UTF-8?q?=EC=BD=98=20=EC=95=B1=20=EB=A1=9C=EA=B3=A0=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/ARchive/app/src/main/AndroidManifest.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/ARchive/app/src/main/AndroidManifest.xml b/frontend/ARchive/app/src/main/AndroidManifest.xml index d7c96e0ee..d4d609f5e 100644 --- a/frontend/ARchive/app/src/main/AndroidManifest.xml +++ b/frontend/ARchive/app/src/main/AndroidManifest.xml @@ -24,9 +24,9 @@ android:allowBackup="false" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="false" - android:icon="@mipmap/ic_launcher" + android:icon="@drawable/app_symbol" android:label="@string/app_name" - android:roundIcon="@mipmap/ic_launcher_round" + android:roundIcon="@drawable/app_symbol" android:screenOrientation="portrait" android:supportsRtl="true" android:theme="@style/Theme.ARchive" From f1d9f98b68e09d5f794bc49ea42b791b8aab32fb Mon Sep 17 00:00:00 2001 From: comst19 Date: Sun, 31 Mar 2024 14:14:00 +0900 Subject: [PATCH 099/301] =?UTF-8?q?fix:=20=EC=B9=9C=EA=B5=AC=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20API=20=EB=A9=94=EC=86=8C=EB=93=9C=20POST=20->=20GET?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/data/source/remote/api/FriendService.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt index b40436da3..9a957bcb5 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt @@ -27,7 +27,7 @@ interface FriendService { @Path("friend_id") friendId : Double, ) : Response> - @POST("friends/search") + @GET("friends/search") suspend fun postFriendsSearchApi( @Query("friend-tag") friendTag : String ) : Response> From 0a98731fe7539f022d3c1d98a8168e4a20f49ef4 Mon Sep 17 00:00:00 2001 From: comst19 Date: Sun, 31 Mar 2024 16:59:20 +0900 Subject: [PATCH 100/301] =?UTF-8?q?design:=20=ED=8F=B0=ED=8A=B8=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20notosans=20->=20suit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ARchive/app/src/main/res/values/style.xml | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/frontend/ARchive/app/src/main/res/values/style.xml b/frontend/ARchive/app/src/main/res/values/style.xml index 811b5a8c5..13282d03b 100644 --- a/frontend/ARchive/app/src/main/res/values/style.xml +++ b/frontend/ARchive/app/src/main/res/values/style.xml @@ -14,7 +14,7 @@ + + + \ No newline at end of file From 4955bfa474fae16a19b87876d6cc7fe9b3340487 Mon Sep 17 00:00:00 2001 From: comst19 Date: Sun, 31 Mar 2024 17:02:46 +0900 Subject: [PATCH 101/301] =?UTF-8?q?feat:=20=EC=86=8C=EC=85=9C=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EA=B5=AC=ED=98=84=EC=9D=84=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20string=20resources?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/ARchive/app/src/main/res/values/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frontend/ARchive/app/src/main/res/values/strings.xml b/frontend/ARchive/app/src/main/res/values/strings.xml index d213d9c37..bfe7a8c5d 100644 --- a/frontend/ARchive/app/src/main/res/values/strings.xml +++ b/frontend/ARchive/app/src/main/res/values/strings.xml @@ -41,4 +41,7 @@  공중위생 등 공공의 안전과 안녕을 위해 긴급히 필요한 경우 ※ 이를 위반하여 개인정보를 수집한 자는 개인정보 보호위원회에게 전체 매출액의 100분의 3을 초과하지 않는 범위에서 과징금을 부과 받을 수 있습니다(「개인정보 보호법」 제64조의2제1항제1호). + 커뮤니티 + 그룹 + 친구 \ No newline at end of file From 617a673bd304cdedf8aeb1cb68ac3d893c003f90 Mon Sep 17 00:00:00 2001 From: comst19 Date: Sun, 31 Mar 2024 17:03:53 +0900 Subject: [PATCH 102/301] =?UTF-8?q?feat:=20tablayout=EC=9D=98=20=EC=95=84?= =?UTF-8?q?=EC=9D=B4=ED=85=9C=20end=EC=97=90=20=EB=A7=88=EC=A7=84=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=ED=95=98=EB=8A=94=20=ED=95=A8=EC=88=98=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../droidblossom/archive/util/BindingAdapter.kt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/BindingAdapter.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/BindingAdapter.kt index f1e30e05e..da626ce61 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/BindingAdapter.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/BindingAdapter.kt @@ -5,6 +5,7 @@ import android.annotation.SuppressLint import android.graphics.drawable.Drawable import android.net.Uri import android.telephony.PhoneNumberFormattingTextWatcher +import android.util.TypedValue import android.view.View import android.view.ViewGroup import android.widget.EditText @@ -17,6 +18,7 @@ import com.bumptech.glide.Glide import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions import com.bumptech.glide.request.RequestOptions import com.droidblossom.archive.R +import com.google.android.material.tabs.TabLayout import de.hdodenhof.circleimageview.CircleImageView import java.text.SimpleDateFormat import java.util.Locale @@ -183,4 +185,19 @@ fun ImageView.setCapsuleType2Img(type: String?){ } else -> {} } +} + +@BindingAdapter("bind:tabMarginEnd") +fun TabLayout.setTabItemMargin(marginEndDp: Int) { + val marginEndPx = TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, marginEndDp.toFloat(), resources.displayMetrics).toInt() + + val tabs = getChildAt(0) as ViewGroup + for (i in 0 until tabs.childCount) { + val tab = tabs.getChildAt(i) + val layoutParams = tab.layoutParams as ViewGroup.MarginLayoutParams + layoutParams.marginEnd = marginEndPx + tab.layoutParams = layoutParams + } + requestLayout() } \ No newline at end of file From f93be4d9fee78cd388f6a41622768245e5b95173 Mon Sep 17 00:00:00 2001 From: comst19 Date: Sun, 31 Mar 2024 17:05:47 +0900 Subject: [PATCH 103/301] =?UTF-8?q?feat:=20=EC=86=8C=EC=85=9C=20=ED=94=84?= =?UTF-8?q?=EB=9E=98=EA=B7=B8=EB=A8=BC=ED=8A=B8=EC=9D=98=20=EB=B7=B0=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A0=80=EC=97=90=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=EB=90=A0=20=ED=94=84=EB=9E=98=EA=B7=B8=EB=A8=BC=ED=8A=B8=20-?= =?UTF-8?q?=20friend,=20group?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/social/page/SocialFriendFragment.kt | 21 +++++++++++++++++++ .../ui/social/page/SocialGroupFragment.kt | 20 ++++++++++++++++++ .../res/layout/fragment_social_friend.xml | 14 +++++++++++++ .../main/res/layout/fragment_social_group.xml | 14 +++++++++++++ 4 files changed, 69 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/SocialFriendFragment.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/SocialGroupFragment.kt create mode 100644 frontend/ARchive/app/src/main/res/layout/fragment_social_friend.xml create mode 100644 frontend/ARchive/app/src/main/res/layout/fragment_social_group.xml diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/SocialFriendFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/SocialFriendFragment.kt new file mode 100644 index 000000000..a48f98778 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/SocialFriendFragment.kt @@ -0,0 +1,21 @@ +package com.droidblossom.archive.presentation.ui.social.page + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.droidblossom.archive.R + + +class SocialFriendFragment : Fragment() { + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + // Inflate the layout for this fragment + return inflater.inflate(R.layout.fragment_social_friend, container, false) + } + +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/SocialGroupFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/SocialGroupFragment.kt new file mode 100644 index 000000000..2ec52fcf0 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/SocialGroupFragment.kt @@ -0,0 +1,20 @@ +package com.droidblossom.archive.presentation.ui.social.page + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.droidblossom.archive.R + +class SocialGroupFragment : Fragment() { + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + // Inflate the layout for this fragment + return inflater.inflate(R.layout.fragment_social_group, container, false) + } + +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_social_friend.xml b/frontend/ARchive/app/src/main/res/layout/fragment_social_friend.xml new file mode 100644 index 000000000..566b4ef07 --- /dev/null +++ b/frontend/ARchive/app/src/main/res/layout/fragment_social_friend.xml @@ -0,0 +1,14 @@ + + + + + + + \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_social_group.xml b/frontend/ARchive/app/src/main/res/layout/fragment_social_group.xml new file mode 100644 index 000000000..88e7c10aa --- /dev/null +++ b/frontend/ARchive/app/src/main/res/layout/fragment_social_group.xml @@ -0,0 +1,14 @@ + + + + + + + \ No newline at end of file From 559003e9f48eecfca6304e56135376f159728178 Mon Sep 17 00:00:00 2001 From: comst19 Date: Sun, 31 Mar 2024 17:06:42 +0900 Subject: [PATCH 104/301] =?UTF-8?q?feat:=20=EC=86=8C=EC=85=9C=20=ED=94=84?= =?UTF-8?q?=EB=9E=98=EA=B7=B8=EB=A8=BC=ED=8A=B8=20ViewPager=20Adapter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/social/adapter/SocialVPA.kt | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/adapter/SocialVPA.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/adapter/SocialVPA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/adapter/SocialVPA.kt new file mode 100644 index 000000000..0f4248a2d --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/adapter/SocialVPA.kt @@ -0,0 +1,20 @@ +package com.droidblossom.archive.presentation.ui.social.adapter + +import androidx.fragment.app.Fragment +import androidx.viewpager2.adapter.FragmentStateAdapter +import com.droidblossom.archive.presentation.ui.social.page.SocialFriendFragment +import com.droidblossom.archive.presentation.ui.social.page.SocialGroupFragment + +class SocialVPA(fragment: Fragment) : FragmentStateAdapter(fragment){ + + private val fragmentList = listOf( + SocialGroupFragment(), + SocialFriendFragment() + ) + + override fun getItemCount(): Int = fragmentList.size + + override fun createFragment(position: Int): Fragment { + return fragmentList[position] + } +} \ No newline at end of file From 1e2873cb6758c748ea10b693a362bc643ff80605 Mon Sep 17 00:00:00 2001 From: comst19 Date: Sun, 31 Mar 2024 17:10:27 +0900 Subject: [PATCH 105/301] design: SocialFragment Design Update --- .../res/drawable/tab_background_selected.xml | 5 ++ .../drawable/tab_background_unselected.xml | 5 ++ .../res/drawable/tab_selector_background.xml | 5 ++ .../src/main/res/layout/fragment_social.xml | 74 ++++++++++++++++--- 4 files changed, 79 insertions(+), 10 deletions(-) create mode 100644 frontend/ARchive/app/src/main/res/drawable/tab_background_selected.xml create mode 100644 frontend/ARchive/app/src/main/res/drawable/tab_background_unselected.xml create mode 100644 frontend/ARchive/app/src/main/res/drawable/tab_selector_background.xml diff --git a/frontend/ARchive/app/src/main/res/drawable/tab_background_selected.xml b/frontend/ARchive/app/src/main/res/drawable/tab_background_selected.xml new file mode 100644 index 000000000..4eaf8d351 --- /dev/null +++ b/frontend/ARchive/app/src/main/res/drawable/tab_background_selected.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/drawable/tab_background_unselected.xml b/frontend/ARchive/app/src/main/res/drawable/tab_background_unselected.xml new file mode 100644 index 000000000..cead1e45b --- /dev/null +++ b/frontend/ARchive/app/src/main/res/drawable/tab_background_unselected.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/drawable/tab_selector_background.xml b/frontend/ARchive/app/src/main/res/drawable/tab_selector_background.xml new file mode 100644 index 000000000..cdb552f97 --- /dev/null +++ b/frontend/ARchive/app/src/main/res/drawable/tab_selector_background.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_social.xml b/frontend/ARchive/app/src/main/res/layout/fragment_social.xml index c74a82000..c2af9b3d6 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_social.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_social.xml @@ -1,16 +1,70 @@ - + - + + + + + android:background="@color/main_bg_1" + tools:context=".presentation.ui.social.SocialFragment"> + + + + + + + + + + + + + + + + - \ No newline at end of file + + \ No newline at end of file From 9de575bf1b9b65e45f1b8a648b4655612c96ef39 Mon Sep 17 00:00:00 2001 From: comst19 Date: Sun, 31 Mar 2024 17:12:52 +0900 Subject: [PATCH 106/301] =?UTF-8?q?feat:=20SocialFragment=EC=97=90?= =?UTF-8?q?=EC=84=9C=20ViewPager,=20TabLayout=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ui/camera/CameraFragment.kt | 1 - .../presentation/ui/social/SocialFragment.kt | 47 +++++++++++++++---- .../presentation/ui/social/SocialViewModel.kt | 4 ++ .../ui/social/SocialViewModelImpl.kt | 12 +++++ 4 files changed, 53 insertions(+), 11 deletions(-) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/SocialViewModel.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/SocialViewModelImpl.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt index 3240ad9a7..96779ef11 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt @@ -34,7 +34,6 @@ class CameraFragment : FragmentManagerProvider { override val viewModel: CameraViewModelImpl by viewModels() - // private val capsules: MutableList = mutableListOf() lateinit var arSceneView: ARSceneView private lateinit var session: Session diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/SocialFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/SocialFragment.kt index 070c617f6..d46146526 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/SocialFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/SocialFragment.kt @@ -1,27 +1,54 @@ package com.droidblossom.archive.presentation.ui.social import android.os.Bundle -import androidx.fragment.app.Fragment -import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.fragment.app.viewModels import com.droidblossom.archive.R -import com.droidblossom.archive.presentation.ui.skin.SkinFragment +import com.droidblossom.archive.databinding.FragmentSocialBinding +import com.droidblossom.archive.presentation.base.BaseFragment +import com.droidblossom.archive.presentation.ui.social.adapter.SocialVPA +import com.google.android.material.tabs.TabLayoutMediator +import dagger.hilt.android.AndroidEntryPoint -class SocialFragment : Fragment() { +@AndroidEntryPoint +class SocialFragment : BaseFragment(R.layout.fragment_social) { - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - // Inflate the layout for this fragment - return inflater.inflate(R.layout.fragment_social, container, false) + override val viewModel: SocialViewModelImpl by viewModels() + + private val socialVPA by lazy { + SocialVPA(this) + } + override fun observeData() { + + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + val layoutParams = binding.viewHeaderTitle.layoutParams as ViewGroup.MarginLayoutParams + layoutParams.topMargin += getStatusBarHeight() + binding.viewHeaderTitle.layoutParams = layoutParams + + initView() } + private fun initView(){ + binding.socialViewPager.adapter = socialVPA + + TabLayoutMediator(binding.socialTabLayout, binding.socialViewPager) { tab, position -> + tab.text = when (position) { + 0 -> getString(R.string.group) + 1 -> getString(R.string.friend) + else -> null + } + }.attach() + } companion object{ const val TAG = "Social" fun newIntent()= SocialFragment() } + } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/SocialViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/SocialViewModel.kt new file mode 100644 index 000000000..27d3382a0 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/SocialViewModel.kt @@ -0,0 +1,4 @@ +package com.droidblossom.archive.presentation.ui.social + +interface SocialViewModel { +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/SocialViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/SocialViewModelImpl.kt new file mode 100644 index 000000000..ce8c0d7c5 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/SocialViewModelImpl.kt @@ -0,0 +1,12 @@ +package com.droidblossom.archive.presentation.ui.social + +import com.droidblossom.archive.presentation.base.BaseViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject + +@HiltViewModel +class SocialViewModelImpl @Inject constructor( + +): BaseViewModel(), SocialViewModel { + +} \ No newline at end of file From c4219ee9d8d8dce7505e59a21f4690034a833e2a Mon Sep 17 00:00:00 2001 From: comst19 Date: Sun, 31 Mar 2024 17:16:02 +0900 Subject: [PATCH 107/301] =?UTF-8?q?refact:=20with()=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=BD=94=EB=93=9C=20=EA=B0=84=EA=B2=B0?= =?UTF-8?q?=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ui/social/SocialFragment.kt | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/SocialFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/SocialFragment.kt index d46146526..517e99604 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/SocialFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/SocialFragment.kt @@ -34,15 +34,17 @@ class SocialFragment : BaseFragment( } private fun initView(){ - binding.socialViewPager.adapter = socialVPA - - TabLayoutMediator(binding.socialTabLayout, binding.socialViewPager) { tab, position -> - tab.text = when (position) { - 0 -> getString(R.string.group) - 1 -> getString(R.string.friend) - else -> null - } - }.attach() + with(binding){ + socialViewPager.adapter = socialVPA + + TabLayoutMediator(socialTabLayout, socialViewPager) { tab, position -> + tab.text = when (position) { + 0 -> getString(R.string.group) + 1 -> getString(R.string.friend) + else -> null + } + }.attach() + } } companion object{ From f685318bcb19493e92e0947b30b93690b006f09d Mon Sep 17 00:00:00 2001 From: comst19 Date: Sun, 31 Mar 2024 17:16:47 +0900 Subject: [PATCH 108/301] design: corners radius 18 -> 30 --- .../app/src/main/res/drawable/tab_background_selected.xml | 2 +- .../app/src/main/res/drawable/tab_background_unselected.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/ARchive/app/src/main/res/drawable/tab_background_selected.xml b/frontend/ARchive/app/src/main/res/drawable/tab_background_selected.xml index 4eaf8d351..dbbb4af20 100644 --- a/frontend/ARchive/app/src/main/res/drawable/tab_background_selected.xml +++ b/frontend/ARchive/app/src/main/res/drawable/tab_background_selected.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/drawable/tab_background_unselected.xml b/frontend/ARchive/app/src/main/res/drawable/tab_background_unselected.xml index cead1e45b..2212ced20 100644 --- a/frontend/ARchive/app/src/main/res/drawable/tab_background_unselected.xml +++ b/frontend/ARchive/app/src/main/res/drawable/tab_background_unselected.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file From 2b314c79e8e868670462705ede772084aac31672 Mon Sep 17 00:00:00 2001 From: comst19 Date: Sun, 31 Mar 2024 17:24:20 +0900 Subject: [PATCH 109/301] =?UTF-8?q?feat:=20ARchiveTabLayoutStyle=EC=97=90?= =?UTF-8?q?=20tabRippleColor=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/ARchive/app/src/main/res/values/style.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/ARchive/app/src/main/res/values/style.xml b/frontend/ARchive/app/src/main/res/values/style.xml index 13282d03b..9a7d11377 100644 --- a/frontend/ARchive/app/src/main/res/values/style.xml +++ b/frontend/ARchive/app/src/main/res/values/style.xml @@ -97,5 +97,6 @@ 13dp @null @android:color/transparent + @null \ No newline at end of file From 5575f8df2ee4fcf6e214b0da00ccabd2d901b793 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Mon, 1 Apr 2024 11:11:48 +0900 Subject: [PATCH 110/301] =?UTF-8?q?faet:=20=EC=B9=9C=EA=B5=AC=EC=97=91?= =?UTF-8?q?=ED=8B=B0=EB=B9=84=20init?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ARchive/app/src/main/AndroidManifest.xml | 3 ++ .../ui/mypage/friend/FriendActivity.kt | 29 +++++++++++++++++++ .../ui/mypage/friend/FriendViewModel.kt | 4 +++ .../ui/mypage/friend/FriendViewModelImpl.kt | 12 ++++++++ .../src/main/res/layout/activity_friend.xml | 17 +++++++++++ 5 files changed, 65 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendActivity.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModel.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModelImpl.kt create mode 100644 frontend/ARchive/app/src/main/res/layout/activity_friend.xml diff --git a/frontend/ARchive/app/src/main/AndroidManifest.xml b/frontend/ARchive/app/src/main/AndroidManifest.xml index d4d609f5e..aa465d6c2 100644 --- a/frontend/ARchive/app/src/main/AndroidManifest.xml +++ b/frontend/ARchive/app/src/main/AndroidManifest.xml @@ -32,6 +32,9 @@ android:theme="@style/Theme.ARchive" android:usesCleartextTraffic="true" tools:targetApi="31"> + diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendActivity.kt new file mode 100644 index 000000000..39842b93d --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendActivity.kt @@ -0,0 +1,29 @@ +package com.droidblossom.archive.presentation.ui.mypage.friend + +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import androidx.activity.viewModels +import com.droidblossom.archive.R +import com.droidblossom.archive.databinding.ActivityFriendBinding +import com.droidblossom.archive.databinding.ActivitySettingBinding +import com.droidblossom.archive.presentation.base.BaseActivity +import com.droidblossom.archive.presentation.ui.mypage.setting.SettingViewModelImpl +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class FriendActivity : + BaseActivity(R.layout.activity_friend) { + override val viewModel: FriendViewModelImpl by viewModels() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + initView() + } + + private fun initView(){ + + } + + override fun observeData() { + } +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModel.kt new file mode 100644 index 000000000..8bb07d5ae --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModel.kt @@ -0,0 +1,4 @@ +package com.droidblossom.archive.presentation.ui.mypage.friend + +interface FriendViewModel { +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModelImpl.kt new file mode 100644 index 000000000..461241649 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModelImpl.kt @@ -0,0 +1,12 @@ +package com.droidblossom.archive.presentation.ui.mypage.friend + +import com.droidblossom.archive.presentation.base.BaseViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject + +@HiltViewModel +class FriendViewModelImpl @Inject constructor( + +) : BaseViewModel(), FriendViewModel { + +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/activity_friend.xml b/frontend/ARchive/app/src/main/res/layout/activity_friend.xml new file mode 100644 index 000000000..324f4aef9 --- /dev/null +++ b/frontend/ARchive/app/src/main/res/layout/activity_friend.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + \ No newline at end of file From 71faa80879907a706b93b7a40d0dc703c5e778ca Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Mon, 1 Apr 2024 12:13:13 +0900 Subject: [PATCH 111/301] =?UTF-8?q?style=20:=20tabStyle=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/ARchive/app/src/main/res/values/style.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/frontend/ARchive/app/src/main/res/values/style.xml b/frontend/ARchive/app/src/main/res/values/style.xml index 9a7d11377..c00de7544 100644 --- a/frontend/ARchive/app/src/main/res/values/style.xml +++ b/frontend/ARchive/app/src/main/res/values/style.xml @@ -99,4 +99,14 @@ @android:color/transparent @null + + \ No newline at end of file From 95af2f79e1daf8285b0076efd4a5fe108596fb8e Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Mon, 1 Apr 2024 13:03:41 +0900 Subject: [PATCH 112/301] =?UTF-8?q?design=20:=EC=B9=9C=EA=B5=AC=20?= =?UTF-8?q?=EC=97=91=ED=8B=B0=EB=B9=84=ED=8B=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/res/layout/activity_friend.xml | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/frontend/ARchive/app/src/main/res/layout/activity_friend.xml b/frontend/ARchive/app/src/main/res/layout/activity_friend.xml index 324f4aef9..2cfaba8f4 100644 --- a/frontend/ARchive/app/src/main/res/layout/activity_friend.xml +++ b/frontend/ARchive/app/src/main/res/layout/activity_friend.xml @@ -10,8 +10,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 2a0dcbfa55eb339fe04ed673d0562702ef0084c6 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Mon, 1 Apr 2024 14:28:15 +0900 Subject: [PATCH 113/301] =?UTF-8?q?feat:=20vp=20fragment=20&=20adapter=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/mypage/friend/adapter/FriendVPA.kt | 24 +++++++++++++++++++ .../mypage/friend/page/FriendListFragment.kt | 18 ++++++++++++++ .../mypage/friend/page/GroupListFragment.kt | 18 ++++++++++++++ .../main/res/layout/fragment_list_friend.xml | 10 ++++++++ .../main/res/layout/fragment_list_group.xml | 11 +++++++++ .../app/src/main/res/values/strings.xml | 3 +++ 6 files changed, 84 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/adapter/FriendVPA.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/FriendListFragment.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/GroupListFragment.kt create mode 100644 frontend/ARchive/app/src/main/res/layout/fragment_list_friend.xml create mode 100644 frontend/ARchive/app/src/main/res/layout/fragment_list_group.xml diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/adapter/FriendVPA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/adapter/FriendVPA.kt new file mode 100644 index 000000000..bdf1691c5 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/adapter/FriendVPA.kt @@ -0,0 +1,24 @@ +package com.droidblossom.archive.presentation.ui.mypage.friend.adapter + +import android.app.Activity +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentActivity +import androidx.viewpager2.adapter.FragmentStateAdapter +import com.droidblossom.archive.presentation.ui.mypage.friend.page.FriendListFragment +import com.droidblossom.archive.presentation.ui.mypage.friend.page.GroupListFragment +import com.droidblossom.archive.presentation.ui.social.page.SocialFriendFragment +import com.droidblossom.archive.presentation.ui.social.page.SocialGroupFragment + +class FriendVPA(activity: FragmentActivity) : FragmentStateAdapter(activity){ + + private val fragmentList = listOf( + GroupListFragment(), + FriendListFragment() + ) + + override fun getItemCount(): Int = fragmentList.size + + override fun createFragment(position: Int): Fragment { + return fragmentList[position] + } +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/FriendListFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/FriendListFragment.kt new file mode 100644 index 000000000..59b288ce1 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/FriendListFragment.kt @@ -0,0 +1,18 @@ +package com.droidblossom.archive.presentation.ui.mypage.friend.page + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import com.droidblossom.archive.R + +class FriendListFragment : Fragment() { + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + return inflater.inflate(R.layout.fragment_list_friend, container, false) + } +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/GroupListFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/GroupListFragment.kt new file mode 100644 index 000000000..5a97e9677 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/GroupListFragment.kt @@ -0,0 +1,18 @@ +package com.droidblossom.archive.presentation.ui.mypage.friend.page + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import com.droidblossom.archive.R + +class GroupListFragment : Fragment() { + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + return inflater.inflate(R.layout.fragment_list_group, container, false) + } +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_list_friend.xml b/frontend/ARchive/app/src/main/res/layout/fragment_list_friend.xml new file mode 100644 index 000000000..462e558ad --- /dev/null +++ b/frontend/ARchive/app/src/main/res/layout/fragment_list_friend.xml @@ -0,0 +1,10 @@ + + + + + \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_list_group.xml b/frontend/ARchive/app/src/main/res/layout/fragment_list_group.xml new file mode 100644 index 000000000..1d0a028db --- /dev/null +++ b/frontend/ARchive/app/src/main/res/layout/fragment_list_group.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/values/strings.xml b/frontend/ARchive/app/src/main/res/values/strings.xml index bfe7a8c5d..acab3c4c5 100644 --- a/frontend/ARchive/app/src/main/res/values/strings.xml +++ b/frontend/ARchive/app/src/main/res/values/strings.xml @@ -44,4 +44,7 @@ 커뮤니티 그룹 친구 + + 그룹 리스트 + 친구 리스트 \ No newline at end of file From dc40c05c766292d1397c119c5e71a720db2a707e Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Mon, 1 Apr 2024 14:28:43 +0900 Subject: [PATCH 114/301] =?UTF-8?q?feat=20:=20tab=20,vp=20=EC=97=B0?= =?UTF-8?q?=EA=B2=B0=20=20&=20=EC=B6=94=EA=B0=80=EB=B2=84=ED=8A=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ui/mypage/MyPageFragment.kt | 6 +++ .../ui/mypage/friend/FriendActivity.kt | 49 +++++++++++++++++-- .../src/main/res/layout/activity_friend.xml | 4 +- 3 files changed, 54 insertions(+), 5 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt index 69c18a9f7..e0cba75b2 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt @@ -25,6 +25,7 @@ import com.droidblossom.archive.presentation.ui.home.HomeFragment import com.droidblossom.archive.presentation.ui.home.createcapsule.adapter.SkinRVA import com.droidblossom.archive.presentation.ui.home.dialog.CapsulePreviewDialogFragment import com.droidblossom.archive.presentation.ui.mypage.adapter.MyCapsuleRVA +import com.droidblossom.archive.presentation.ui.mypage.friend.FriendActivity import com.droidblossom.archive.presentation.ui.mypage.setting.SettingActivity import com.droidblossom.archive.presentation.ui.skin.SkinFragment import com.droidblossom.archive.util.DateUtils @@ -79,6 +80,11 @@ class MyPageFragment : val layoutParams = binding.profileImg.layoutParams as ViewGroup.MarginLayoutParams layoutParams.topMargin += getStatusBarHeight() binding.profileImg.layoutParams = layoutParams + + //임시 : 친구 엑티비티로 이동 + binding.profileImg.setOnClickListener{ + startActivity(FriendActivity.newIntent(requireContext())) + } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendActivity.kt index 39842b93d..64925e2d8 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendActivity.kt @@ -1,29 +1,70 @@ package com.droidblossom.archive.presentation.ui.mypage.friend -import androidx.appcompat.app.AppCompatActivity +import android.content.Context +import android.content.Intent import android.os.Bundle +import android.view.ViewGroup import androidx.activity.viewModels import com.droidblossom.archive.R import com.droidblossom.archive.databinding.ActivityFriendBinding -import com.droidblossom.archive.databinding.ActivitySettingBinding import com.droidblossom.archive.presentation.base.BaseActivity -import com.droidblossom.archive.presentation.ui.mypage.setting.SettingViewModelImpl +import com.droidblossom.archive.presentation.ui.mypage.friend.adapter.FriendVPA +import com.google.android.material.tabs.TabLayout +import com.google.android.material.tabs.TabLayout.OnTabSelectedListener +import com.google.android.material.tabs.TabLayoutMediator import dagger.hilt.android.AndroidEntryPoint + @AndroidEntryPoint class FriendActivity : BaseActivity(R.layout.activity_friend) { override val viewModel: FriendViewModelImpl by viewModels() + private val friendVPA by lazy { + FriendVPA(this) + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) initView() } private fun initView(){ - + val layoutParams = binding.tab.layoutParams as ViewGroup.MarginLayoutParams + layoutParams.topMargin += getStatusBarHeight() + binding.tab.layoutParams = layoutParams + + binding.vp.adapter = friendVPA + + TabLayoutMediator(binding.tab, binding.vp) { tab, position -> + tab.text = when (position) { + 0 -> getString(R.string.groupList) + 1 -> getString(R.string.friendList) + else -> null + } + }.attach() + + binding.tab.addOnTabSelectedListener(object : OnTabSelectedListener { + override fun onTabSelected(tab: TabLayout.Tab) { + if (tab.position == 0){ + binding.addT.text = "그룹 추가" + } else { + binding.addT.text = "친구 추가" + } + } + override fun onTabUnselected(tab: TabLayout.Tab) {} + override fun onTabReselected(tab: TabLayout.Tab) {} + }) } override fun observeData() { + + } + + companion object { + const val FRIEND = "friend" + + fun newIntent(context: Context) = + Intent(context, FriendActivity::class.java) } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/activity_friend.xml b/frontend/ARchive/app/src/main/res/layout/activity_friend.xml index 2cfaba8f4..59829006b 100644 --- a/frontend/ARchive/app/src/main/res/layout/activity_friend.xml +++ b/frontend/ARchive/app/src/main/res/layout/activity_friend.xml @@ -14,6 +14,7 @@ tools:context=".presentation.ui.mypage.friend.FriendActivity"> Date: Mon, 1 Apr 2024 17:23:03 +0900 Subject: [PATCH 115/301] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EC=97=91=ED=8B=B0=20init?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ARchive/app/src/main/AndroidManifest.xml | 3 +++ .../friend/addfriend/AddFriendActivity.kt | 22 +++++++++++++++++++ .../friend/addfriend/AddFriendViewModel.kt | 4 ++++ .../addfriend/AddFriendViewModelImpl.kt | 12 ++++++++++ .../main/res/layout/activity_add_friend.xml | 16 ++++++++++++++ 5 files changed, 57 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendActivity.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModel.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt create mode 100644 frontend/ARchive/app/src/main/res/layout/activity_add_friend.xml diff --git a/frontend/ARchive/app/src/main/AndroidManifest.xml b/frontend/ARchive/app/src/main/AndroidManifest.xml index aa465d6c2..402738e11 100644 --- a/frontend/ARchive/app/src/main/AndroidManifest.xml +++ b/frontend/ARchive/app/src/main/AndroidManifest.xml @@ -32,6 +32,9 @@ android:theme="@style/Theme.ARchive" android:usesCleartextTraffic="true" tools:targetApi="31"> + diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendActivity.kt new file mode 100644 index 000000000..d7b686c91 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendActivity.kt @@ -0,0 +1,22 @@ +package com.droidblossom.archive.presentation.ui.mypage.friend.addfriend + +import android.content.Context +import android.content.Intent +import androidx.activity.viewModels +import com.droidblossom.archive.R +import com.droidblossom.archive.databinding.ActivityAddFriendBinding +import com.droidblossom.archive.presentation.base.BaseActivity + +class AddFriendActivity : BaseActivity(R.layout.activity_add_friend) { + + override val viewModel: AddFriendViewModelImpl by viewModels() + + override fun observeData() {} + + companion object { + const val ADDFRIEND = "add_friend" + + fun newIntent(context: Context) = + Intent(context, AddFriendActivity::class.java) + } +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModel.kt new file mode 100644 index 000000000..6213011e9 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModel.kt @@ -0,0 +1,4 @@ +package com.droidblossom.archive.presentation.ui.mypage.friend.addfriend + +interface AddFriendViewModel { +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt new file mode 100644 index 000000000..4b059b04f --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt @@ -0,0 +1,12 @@ +package com.droidblossom.archive.presentation.ui.mypage.friend.addfriend + +import com.droidblossom.archive.presentation.base.BaseViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject + +@HiltViewModel +class AddFriendViewModelImpl @Inject constructor( + +) : BaseViewModel(), AddFriendViewModel { + +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/activity_add_friend.xml b/frontend/ARchive/app/src/main/res/layout/activity_add_friend.xml new file mode 100644 index 000000000..373ae60d3 --- /dev/null +++ b/frontend/ARchive/app/src/main/res/layout/activity_add_friend.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + \ No newline at end of file From b0987b213bbc22873a6cb1632432d6b849efd61e Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Mon, 1 Apr 2024 17:39:08 +0900 Subject: [PATCH 116/301] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20nav?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../friend/addfriend/AddFriendActivity.kt | 2 ++ .../addfriend/SearchFriendNicknameFragment.kt | 18 ++++++++++++++ .../addfriend/SearchFriendNumberFragment.kt | 18 ++++++++++++++ .../main/res/layout/activity_add_friend.xml | 13 ++++++++++ .../fragment_friend_search_nickname.xml | 6 +++++ .../layout/fragment_friend_search_number.xml | 6 +++++ .../res/navigation/nav_add_friend_graph.xml | 24 +++++++++++++++++++ 7 files changed, 87 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNicknameFragment.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNumberFragment.kt create mode 100644 frontend/ARchive/app/src/main/res/layout/fragment_friend_search_nickname.xml create mode 100644 frontend/ARchive/app/src/main/res/layout/fragment_friend_search_number.xml create mode 100644 frontend/ARchive/app/src/main/res/navigation/nav_add_friend_graph.xml diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendActivity.kt index d7b686c91..144fecc82 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendActivity.kt @@ -6,7 +6,9 @@ import androidx.activity.viewModels import com.droidblossom.archive.R import com.droidblossom.archive.databinding.ActivityAddFriendBinding import com.droidblossom.archive.presentation.base.BaseActivity +import dagger.hilt.android.AndroidEntryPoint +@AndroidEntryPoint class AddFriendActivity : BaseActivity(R.layout.activity_add_friend) { override val viewModel: AddFriendViewModelImpl by viewModels() diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNicknameFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNicknameFragment.kt new file mode 100644 index 000000000..c339edad8 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNicknameFragment.kt @@ -0,0 +1,18 @@ +package com.droidblossom.archive.presentation.ui.mypage.friend.addfriend + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import com.droidblossom.archive.R + +class SearchFriendNicknameFragment : Fragment() { + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + return inflater.inflate(R.layout.fragment_friend_search_nickname, container, false) + } +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNumberFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNumberFragment.kt new file mode 100644 index 000000000..598ace6d9 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNumberFragment.kt @@ -0,0 +1,18 @@ +package com.droidblossom.archive.presentation.ui.mypage.friend.addfriend + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import com.droidblossom.archive.R + +class SearchFriendNumberFragment : Fragment() { + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + return inflater.inflate(R.layout.fragment_friend_search_number, container, false) + } +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/activity_add_friend.xml b/frontend/ARchive/app/src/main/res/layout/activity_add_friend.xml index 373ae60d3..43e28d271 100644 --- a/frontend/ARchive/app/src/main/res/layout/activity_add_friend.xml +++ b/frontend/ARchive/app/src/main/res/layout/activity_add_friend.xml @@ -12,5 +12,18 @@ android:layout_height="match_parent" tools:context=".presentation.ui.mypage.friend.addfriend.AddFriendActivity"> + + + \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_friend_search_nickname.xml b/frontend/ARchive/app/src/main/res/layout/fragment_friend_search_nickname.xml new file mode 100644 index 000000000..77d9ef65f --- /dev/null +++ b/frontend/ARchive/app/src/main/res/layout/fragment_friend_search_nickname.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_friend_search_number.xml b/frontend/ARchive/app/src/main/res/layout/fragment_friend_search_number.xml new file mode 100644 index 000000000..77d9ef65f --- /dev/null +++ b/frontend/ARchive/app/src/main/res/layout/fragment_friend_search_number.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/navigation/nav_add_friend_graph.xml b/frontend/ARchive/app/src/main/res/navigation/nav_add_friend_graph.xml new file mode 100644 index 000000000..f4360cc67 --- /dev/null +++ b/frontend/ARchive/app/src/main/res/navigation/nav_add_friend_graph.xml @@ -0,0 +1,24 @@ + + + + + + + + + + \ No newline at end of file From 6b24401bd82f3ae58135e4ef42e431bd587c0922 Mon Sep 17 00:00:00 2001 From: comst19 Date: Mon, 1 Apr 2024 23:26:50 +0900 Subject: [PATCH 117/301] =?UTF-8?q?feat:=20SocialFriendFragment=20?= =?UTF-8?q?=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=A3=BC=EC=9E=85,=20ViewModel?= =?UTF-8?q?=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/social/page/SocialFriendFragment.kt | 21 ----------- .../page/friend/SocialFriendFragment.kt | 28 ++++++++++++++ .../page/friend/SocialFriendViewModel.kt | 4 ++ .../page/friend/SocialFriendViewModelImpl.kt | 12 ++++++ .../res/layout/fragment_social_friend.xml | 37 +++++++++++++++---- 5 files changed, 73 insertions(+), 29 deletions(-) delete mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/SocialFriendFragment.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendFragment.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModel.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModelImpl.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/SocialFriendFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/SocialFriendFragment.kt deleted file mode 100644 index a48f98778..000000000 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/SocialFriendFragment.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.droidblossom.archive.presentation.ui.social.page - -import android.os.Bundle -import androidx.fragment.app.Fragment -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import com.droidblossom.archive.R - - -class SocialFriendFragment : Fragment() { - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - // Inflate the layout for this fragment - return inflater.inflate(R.layout.fragment_social_friend, container, false) - } - -} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendFragment.kt new file mode 100644 index 000000000..7c8435fc6 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendFragment.kt @@ -0,0 +1,28 @@ +package com.droidblossom.archive.presentation.ui.social.page.friend + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.viewModels +import com.droidblossom.archive.R +import com.droidblossom.archive.databinding.FragmentSocialFriendBinding +import com.droidblossom.archive.databinding.FragmentSocialGroupBinding +import com.droidblossom.archive.presentation.base.BaseFragment +import com.droidblossom.archive.presentation.ui.social.page.group.SocialGroupViewModelImpl + + +class SocialFriendFragment : BaseFragment(R.layout.fragment_social_friend) { + + override val viewModel: SocialFriendViewModelImpl by viewModels() + + override fun observeData() { + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.vm = viewModel + } + +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModel.kt new file mode 100644 index 000000000..05c20cdf0 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModel.kt @@ -0,0 +1,4 @@ +package com.droidblossom.archive.presentation.ui.social.page.friend + +interface SocialFriendViewModel { +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModelImpl.kt new file mode 100644 index 000000000..91e436eac --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModelImpl.kt @@ -0,0 +1,12 @@ +package com.droidblossom.archive.presentation.ui.social.page.friend + +import com.droidblossom.archive.presentation.base.BaseViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject + +@HiltViewModel +class SocialFriendViewModelImpl @Inject constructor( + +): BaseViewModel(), SocialFriendViewModel { + +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_social_friend.xml b/frontend/ARchive/app/src/main/res/layout/fragment_social_friend.xml index 566b4ef07..f5e341eb6 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_social_friend.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_social_friend.xml @@ -1,14 +1,35 @@ - + xmlns:app="http://schemas.android.com/apk/res-auto"> - - + + + + + tools:context=".presentation.ui.social.page.friend.SocialFriendFragment" + android:background="@color/main_bg_1"> + + + + + + + + - \ No newline at end of file + + \ No newline at end of file From 3430b6d4f42e531867c675f75cb084b7dc872906 Mon Sep 17 00:00:00 2001 From: comst19 Date: Mon, 1 Apr 2024 23:27:31 +0900 Subject: [PATCH 118/301] =?UTF-8?q?feat:=20SocialGroupFragment=20=EC=9D=98?= =?UTF-8?q?=EC=A1=B4=EC=84=B1=20=EC=A3=BC=EC=9E=85,=20ViewModel=20?= =?UTF-8?q?=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/social/page/SocialGroupFragment.kt | 20 ------------- .../social/page/group/SocialGroupFragment.kt | 29 +++++++++++++++++++ .../social/page/group/SocialGroupViewModel.kt | 4 +++ .../page/group/SocialGroupViewModelImpl.kt | 12 ++++++++ .../main/res/layout/fragment_social_group.xml | 29 ++++++++++++------- 5 files changed, 64 insertions(+), 30 deletions(-) delete mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/SocialGroupFragment.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupFragment.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupViewModel.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupViewModelImpl.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/SocialGroupFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/SocialGroupFragment.kt deleted file mode 100644 index 2ec52fcf0..000000000 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/SocialGroupFragment.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.droidblossom.archive.presentation.ui.social.page - -import android.os.Bundle -import androidx.fragment.app.Fragment -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import com.droidblossom.archive.R - -class SocialGroupFragment : Fragment() { - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - // Inflate the layout for this fragment - return inflater.inflate(R.layout.fragment_social_group, container, false) - } - -} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupFragment.kt new file mode 100644 index 000000000..5b41c110b --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupFragment.kt @@ -0,0 +1,29 @@ +package com.droidblossom.archive.presentation.ui.social.page.group + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.viewModels +import com.droidblossom.archive.R +import com.droidblossom.archive.databinding.FragmentSocialFriendBinding +import com.droidblossom.archive.databinding.FragmentSocialGroupBinding +import com.droidblossom.archive.presentation.base.BaseFragment +import com.droidblossom.archive.presentation.ui.social.SocialViewModelImpl +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class SocialGroupFragment : BaseFragment(R.layout.fragment_social_group) { + + override val viewModel: SocialGroupViewModelImpl by viewModels() + + override fun observeData() { + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.vm = viewModel + } + +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupViewModel.kt new file mode 100644 index 000000000..2939d5dd8 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupViewModel.kt @@ -0,0 +1,4 @@ +package com.droidblossom.archive.presentation.ui.social.page.group + +interface SocialGroupViewModel { +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupViewModelImpl.kt new file mode 100644 index 000000000..6febe1b76 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupViewModelImpl.kt @@ -0,0 +1,12 @@ +package com.droidblossom.archive.presentation.ui.social.page.group + +import com.droidblossom.archive.presentation.base.BaseViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject + +@HiltViewModel +class SocialGroupViewModelImpl @Inject constructor( + +): BaseViewModel(), SocialGroupViewModel { + +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_social_group.xml b/frontend/ARchive/app/src/main/res/layout/fragment_social_group.xml index 88e7c10aa..6aec867ad 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_social_group.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_social_group.xml @@ -1,14 +1,23 @@ - - - - + + + + + + + tools:context=".presentation.ui.social.page.group.SocialGroupFragment"> + + + - \ No newline at end of file + + \ No newline at end of file From 07a0c779e9ec814d207adf22d260b0b8abe4b34f Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Tue, 2 Apr 2024 00:32:12 +0900 Subject: [PATCH 119/301] =?UTF-8?q?feat:=20=EC=9D=B4=EB=A6=84=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=B9=9C=EA=B5=AC=20=EA=B2=80=EC=83=89=20init?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../friend/addfriend/AddFriendViewModel.kt | 6 + .../addfriend/AddFriendViewModelImpl.kt | 18 ++ .../addfriend/SearchFriendNicknameFragment.kt | 2 + .../main/res/layout/activity_add_friend.xml | 3 +- .../fragment_friend_search_nickname.xml | 239 +++++++++++++++++- 5 files changed, 263 insertions(+), 5 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModel.kt index 6213011e9..f104c1398 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModel.kt @@ -1,4 +1,10 @@ package com.droidblossom.archive.presentation.ui.mypage.friend.addfriend +import kotlinx.coroutines.flow.StateFlow + interface AddFriendViewModel { + + val isSearchNameOpen : StateFlow + fun searchName() + fun openSearchName() } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt index 4b059b04f..c9b5a8f20 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt @@ -1,7 +1,11 @@ package com.droidblossom.archive.presentation.ui.mypage.friend.addfriend +import androidx.lifecycle.viewModelScope import com.droidblossom.archive.presentation.base.BaseViewModel import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel @@ -9,4 +13,18 @@ class AddFriendViewModelImpl @Inject constructor( ) : BaseViewModel(), AddFriendViewModel { + + private val _isSearchNameOpen = MutableStateFlow(false) + override val isSearchNameOpen: StateFlow + get() = _isSearchNameOpen + + override fun searchName() { + + } + + override fun openSearchName() { + viewModelScope.launch { + _isSearchNameOpen.emit(true) + } + } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNicknameFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNicknameFragment.kt index c339edad8..d5fd937c2 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNicknameFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNicknameFragment.kt @@ -6,7 +6,9 @@ import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment import com.droidblossom.archive.R +import dagger.hilt.android.AndroidEntryPoint +@AndroidEntryPoint class SearchFriendNicknameFragment : Fragment() { override fun onCreateView( diff --git a/frontend/ARchive/app/src/main/res/layout/activity_add_friend.xml b/frontend/ARchive/app/src/main/res/layout/activity_add_friend.xml index 43e28d271..cb7e29abf 100644 --- a/frontend/ARchive/app/src/main/res/layout/activity_add_friend.xml +++ b/frontend/ARchive/app/src/main/res/layout/activity_add_friend.xml @@ -10,11 +10,12 @@ - + - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From d3de6b3822cf07feaa940a6fcd479945c8a6a4ba Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Tue, 2 Apr 2024 01:13:04 +0900 Subject: [PATCH 120/301] =?UTF-8?q?fix=20:=20=EA=B2=80=EC=83=89=EC=B0=BD?= =?UTF-8?q?=20=ED=95=AD=EC=83=81=20open?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../friend/addfriend/AddFriendViewModel.kt | 2 -- .../addfriend/AddFriendViewModelImpl.kt | 12 +------ .../fragment_friend_search_nickname.xml | 36 ------------------- 3 files changed, 1 insertion(+), 49 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModel.kt index f104c1398..5e81218f6 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModel.kt @@ -4,7 +4,5 @@ import kotlinx.coroutines.flow.StateFlow interface AddFriendViewModel { - val isSearchNameOpen : StateFlow fun searchName() - fun openSearchName() } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt index c9b5a8f20..1d9f0bdbc 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt @@ -12,19 +12,9 @@ import javax.inject.Inject class AddFriendViewModelImpl @Inject constructor( ) : BaseViewModel(), AddFriendViewModel { - - - private val _isSearchNameOpen = MutableStateFlow(false) - override val isSearchNameOpen: StateFlow - get() = _isSearchNameOpen - + override fun searchName() { } - override fun openSearchName() { - viewModelScope.launch { - _isSearchNameOpen.emit(true) - } - } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_friend_search_nickname.xml b/frontend/ARchive/app/src/main/res/layout/fragment_friend_search_nickname.xml index 85e2b3275..98da2667f 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_friend_search_nickname.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_friend_search_nickname.xml @@ -46,7 +46,6 @@ android:layout_marginStart="16dp" android:layout_marginTop="35dp" android:layout_marginEnd="16dp" - android:visibility="@{vm.isSearchNameOpen ? View.VISIBLE : View.GONE}" app:cardBackgroundColor="@color/white" app:cardCornerRadius="21dp" app:cardElevation="0dp" @@ -94,41 +93,6 @@ - - - - - - - - - Date: Tue, 2 Apr 2024 02:53:57 +0900 Subject: [PATCH 121/301] =?UTF-8?q?feat:=20icon=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/src/main/res/drawable/ic_check_24.xml | 18 ++++++++++++++++++ .../src/main/res/drawable/ic_plus_small_24.xml | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 frontend/ARchive/app/src/main/res/drawable/ic_check_24.xml create mode 100644 frontend/ARchive/app/src/main/res/drawable/ic_plus_small_24.xml diff --git a/frontend/ARchive/app/src/main/res/drawable/ic_check_24.xml b/frontend/ARchive/app/src/main/res/drawable/ic_check_24.xml new file mode 100644 index 000000000..c203713f4 --- /dev/null +++ b/frontend/ARchive/app/src/main/res/drawable/ic_check_24.xml @@ -0,0 +1,18 @@ + + + + diff --git a/frontend/ARchive/app/src/main/res/drawable/ic_plus_small_24.xml b/frontend/ARchive/app/src/main/res/drawable/ic_plus_small_24.xml new file mode 100644 index 000000000..b90dd457e --- /dev/null +++ b/frontend/ARchive/app/src/main/res/drawable/ic_plus_small_24.xml @@ -0,0 +1,18 @@ + + + + From b9eb7c3e7692159ead92066726889032e2c18000 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Tue, 2 Apr 2024 02:54:31 +0900 Subject: [PATCH 122/301] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20item?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fragment_friend_search_nickname.xml | 5 +- .../src/main/res/layout/item_add_friend.xml | 104 ++++++++++++++++++ 2 files changed, 106 insertions(+), 3 deletions(-) create mode 100644 frontend/ARchive/app/src/main/res/layout/item_add_friend.xml diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_friend_search_nickname.xml b/frontend/ARchive/app/src/main/res/layout/fragment_friend_search_nickname.xml index 98da2667f..351464e5c 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_friend_search_nickname.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_friend_search_nickname.xml @@ -97,18 +97,17 @@ android:id="@+id/recycleView" android:layout_width="0dp" android:layout_height="0dp" - android:layout_marginHorizontal="12dp" android:layout_marginTop="85dp" android:clipToPadding="false" android:paddingBottom="140dp" - app:layoutManager="androidx.recyclerview.widget.GridLayoutManager" + app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/titleT" app:spanCount="3" tools:itemCount="30" - tools:listitem="@layout/item_skin" /> + tools:listitem="@layout/item_add_friend" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From a0bc0ab79b7fbbea20a72331700ef3a918ea5811 Mon Sep 17 00:00:00 2001 From: comst19 Date: Tue, 2 Apr 2024 20:29:14 +0900 Subject: [PATCH 123/301] =?UTF-8?q?feat:=20searchview=EC=9D=98=20ui=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=EC=97=90=20=EC=9D=98=ED=95=9C=20=EB=A6=AC?= =?UTF-8?q?=EC=82=AC=EC=9D=B4=ED=81=B4=EB=9F=AC=EB=B7=B0=20=EC=A0=9C?= =?UTF-8?q?=EC=95=BD=20=EB=B3=80=EA=B2=BD=EC=BD=94=EB=93=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/droidblossom/archive/util/Expansion.kt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/Expansion.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/Expansion.kt index 9ce4949f4..96e6b8db4 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/Expansion.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/Expansion.kt @@ -2,8 +2,12 @@ package com.droidblossom.archive.util import android.app.Activity import android.content.Intent +import android.content.res.Resources import android.os.Build +import android.util.TypedValue +import android.view.View import android.view.ViewGroup +import androidx.constraintlayout.widget.ConstraintLayout import java.io.Serializable fun Intent.intentSerializable(key: String, clazz: Class): T? { @@ -17,4 +21,17 @@ fun Intent.intentSerializable(key: String, clazz: Class): T fun Activity.getStatusBarHeight(): Int { val resourceId = this.resources.getIdentifier("status_bar_height", "dimen", "android") return if (resourceId > 0) this.resources.getDimensionPixelSize(resourceId) else 0 +} + +fun ConstraintLayout.LayoutParams.updateTopConstraintsForSearch( + isSearchOpen: Boolean, + searchOpenView: View, + searchView: View, + additionalMarginDp: Float, + resources: Resources +) { + this.topToBottom = if (isSearchOpen) searchOpenView.id else searchView.id + val additionalMargin = TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, additionalMarginDp, resources.displayMetrics).toInt() + this.topMargin = additionalMargin } \ No newline at end of file From 79eb7ee51862184dba024704705f4f4a2dd83759 Mon Sep 17 00:00:00 2001 From: comst19 Date: Tue, 2 Apr 2024 20:30:17 +0900 Subject: [PATCH 124/301] =?UTF-8?q?design:=20=EC=B9=9C=EA=B5=AC,=20?= =?UTF-8?q?=EA=B7=B8=EB=A3=B9=EB=93=A4=EC=9D=98=20=EC=BA=A1=EC=8A=90=20?= =?UTF-8?q?=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EC=95=84=EC=9D=B4=ED=85=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../social/adapter/TestSocialFriendModel.kt | 12 ++ .../ARchive/app/src/main/res/drawable/img.png | Bin 0 -> 639901 bytes .../res/layout/item_social_capsule_close.xml | 107 +++++++++ .../res/layout/item_social_capsule_open.xml | 203 ++++++++++++++++++ .../app/src/main/res/values/colors.xml | 6 + 5 files changed, 328 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/adapter/TestSocialFriendModel.kt create mode 100644 frontend/ARchive/app/src/main/res/drawable/img.png create mode 100644 frontend/ARchive/app/src/main/res/layout/item_social_capsule_close.xml create mode 100644 frontend/ARchive/app/src/main/res/layout/item_social_capsule_open.xml diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/adapter/TestSocialFriendModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/adapter/TestSocialFriendModel.kt new file mode 100644 index 000000000..7c40e45c0 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/adapter/TestSocialFriendModel.kt @@ -0,0 +1,12 @@ +package com.droidblossom.archive.presentation.ui.social.adapter + +data class TestSocialFriendModel( + val id:Long, + val capsuleTitle:String, + val capsuleWriter:String, + val capsuleContent:String, + val capsuleContentImg:String, + val capsuleLocation:String, + val capsuleCreateTime:String, + val isOpened:Boolean +) diff --git a/frontend/ARchive/app/src/main/res/drawable/img.png b/frontend/ARchive/app/src/main/res/drawable/img.png new file mode 100644 index 0000000000000000000000000000000000000000..e27dbdd486b5b0f9a69f591657a566bdab8a1d62 GIT binary patch literal 639901 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
  • > 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%0DWzCMud zwsVR&kR)=Dm zX6G!6U?h@bkRH$siC7#L>+WQysaGqz(}Uw2A~~i_Ewt)JN;vTriDJo#^&_M-pxD*gAb6z5y4NB4wla}mcI+-Fy+mF?^ZrnwIkNQAakc@FbE zsKE-%KxyFlRM3+}uO};h2cy<6cfCH`^Ln;p@$)P6+oQ<+$?*P0+l@Kc;%s<-e{gFL ztZe`@FhxGT6s&Ieo;?UWdf>OD%gm9em94zRdnv`k$i%jwgoe0u9a%3)=w*H#i!B@baE3|TNEG1Z|qml{v?CxqS6l=#%vJ9YND&n-l>w~!6> zfijac(3_i*2*>H{^Ygy@-|yM~>CpQ9ip3ZAP-ok$sTo;qYg%r;l*Nj$votom z4*cC5q0SEG0EF1t@lfbM8z=LdCU>tH>FeDx(7Sf`nx40vMIsBG%prz)x{+Z>ouU*< zL@WdXYHde`B3y17*UQs(hctWRaXaJ8GDTo_ZERgWEkghqXo&5tjqa>u?oKM|vjXHX zM%@+Y#SzB*WZ=mGYI82&#RdDj_vwS3&RbjX@4t-w*B68rQ_k8Vqp1e>r(3v-)sRK4 z^Ru()i2?X%H+-ri2<8mx)doEcP7(=7O$IBI{wpIsjb#>XRiOzC58WjcrMXt+x@JhN zh+$y7#&NgbdwmGnU+Y*SH|Q$~ijQ=h?5w20f{|_>;s`v{)+8!2B!+`_aJ@q$`^se$ z9@<00#-r|l=Hxh>i&YH5Ka~}!%Up!BV^%z?B0Z@sx1us7U#G8KmBl#{zPTRIDQlTB zkBPy18Q*dM;IvozZ`aA356Zz z*`1D(T8FwN#_(mz;djOIMvHhQD5(N#v;Lctfyaxa7mpRS0}*}RWDR*jmrp$%q1q&#cS{Pbse{452fHU1N{Xy5TOk^-78e+NKLnz zI}CsGf%M%MQ9TV-Nn(fJUV7GDi7`-?!w)ahrp7VlH4YPpv0bYaO{ZIHzv=VG4uAN{ zZ&iDzMt!@yJgjd}+S$R&NjCbh3;Dlqe1Cmm^B?#0etl~C>vOCBI5+&y)4RH+e%m4; zHtql%G)KP~j=$f=J89%C6cbZW*CRYZYP6Tlb%23526;IeTn9IA0vythix1$ye|uf1 zfa_c$u-WK|tIuN@=tS4u0E%$H!~{lXQaorL0dy~s6oUycMSGag0-WJ4W+FO_9`0;) z6RZvpU+%L$KM_6I!MtDd{GU%qM@=S4Q1Hbp?rMP8o*C$V4J^oD3z$J$%_VDXWz7Y`iLRXP^08=bQg72XACq-=K3-k)GLn&<>X4$9og1 za?z{Z^tnz_R<^G<6lA36P4+-W8X)IOiOF1R62d?(!RyRQSvKnJdF7ik_3;w>=?Uxc znP7Tcke!K1V897bFL6{9Ka!$TP>7vfHji!p-{TD^FYm-CdX`Y+Vd-FcQ!gcvP*Q@L zS)lHouy@Xqw$Ia-cM}`BiP9`DJpH<+JZN$_vP5B=8Vd%i11i%!%aSoA$+UQ?ua7n8 ztH=;j0^-Q-43tT1gsF^cpCk2Zub|WykQf+qFDpQXxgenWT#D1tT+RAG&QJ}#vk;># z=Ok18qQl%-Xpf5G$kuLdO&2e}n%vSIH82!AIVB$$kYy-BCZ@AyW;Nv{lAd}E#@i3& z?8AeRWsy9hFT&lz-qOI3&7cu!$nt?~Wg|ykOi-5L$w}^ETniN2jKno3&!4@H?r)EYC$rGYP1k?=Cg%JsYJP#%(@m(Z zfTtxpCMJN*b+1RyAZO1*I)?3Pnu6Q=>A7_twNu!RBXN9vV9RRa?0NRkYRudQV_^pO z;!5=PiF9hncY56Y!Nch9eyUhmjz8Zi(pkO9CgD^aqm-}zXo7Iu?QzuUyx;D!(FU!` zbP+Od6st_vSKwFYs0Zhm@4iv&?X$GQ@H8pd89@$LS`4=ttxSfBn3k@lfC6(b$rSME z!MgfjeCL+o4M2t4#Dtq-y{;3Xcc}2&SxL}5B}@=!o+#HZuCpj>HmPc{Y3~UmMf^5n zuriz0ujQ%=oyw{_dwSsm?NA+gq;t)kO@L4k;oxE#mxz}nqxb?pBGv=}y~V`YGck5t zoFf}yLk%+Vu>|~Rx60;-|HsjJhoyR@Yk%2$?}`Ob5D)~VDguI{QlyC>Sipw87sQIa z_uhMNd+YAK@110)>`5|7W+s!&oMe(OQ!+Jk&Y5$~$DZq2e{cbRz*_6M@B7Dk-v@0I zhN||>Y>S`XFE6iVnS^DeE@z-pY0~RL6_1xdMus?JLTyYRZ=x@yHXn0st7fG)S3vPK zy@7-RxmobcG&r9Sin6gj9_(0cQp}Zx9vh>!yLc;|!o9)dYjcz@AK?G;F5&Bwut!6V z8+!1|+w|90IIAP1u~B(zS59+lQB74IJC;DiAi@G5bZnHjl|!U|pqNS(Qe&NglbxBX zC9t#v=D0940g2!-qG(tX$^twhtS&FJCR<*qN|VOLM0j|cil{~^f4b9uHp6&!nmXLX zh>HO6Q9vv6Hk9Irnv?RR;e&Mv`vdIlZVEpdEc7KFt|yy9&-zrte1FvIyGcKLM7^=^ z`_=oDAAd&s;s$w6kF3uOnCrmrO)+M>qwX9lKYCew&DT*cq=)_x7{_*%EtiTi|U0nsclt>LDUJD9#Ad2%O55&_t43k%FU2 zdOP7TXD2Tkz@Wmat7J+ARV2U+v}YEkMisf;t~qLQW>j9MbV4NB1}MQCOCyBbv)BF0x|Xxq{YrjMNYIFyTmfT^malhTX2 zQGb7e|JOaA|G8oN_e=YKKMMT)k?X^D0F*TQ)n?&gS4NW{V7GyKpr=Fu&>;=l>y!mr z0$PN5MM;vS8HmD%+S&js8<3C`MI!j>HS9HmyfVROq5yZ?6F*sv>n)=tQJp2y@I)yx zE)7kSN1~H3b^%sN9JHJhA)$tHG5$HpR6}lZWd>@woAzLbzR~YCSP$-Q1pVd)D?E;{Yrn+ad9 za{9&n?8`OogMH<}3}bpA_+Xc~wHI@8Q)C!JOdO?jt#j2i{&OpwolVa4B=-8H@ZyYr zewwnmLpSyYx3>G$HMsWlLdIrsE61XVwfG13C690LpIpe_y{wqli_1lnyX*aZ6^gB? z=DGgTxo&lX2KV@&;{6-OMjfZ2gzaDrCdOh;&QdOKtFGN@+u!d#+8x|jYSyYzS_NFj z4ak?mie&Bzjw$+?A8k<$O+nf`m)c6-w#M-O7WT@Z^!loNd61ct9jGe~>1l;542I6O zhPUaUvwh-P9YxIc$d&jlcQICtkq1NQTdU~1yO0ka#((j;qrX{_LV;ubJ(_d{G7g6r z8d|1G7g3}5L>$)7KRN^!=H(lU!HJnkrr_Dxs+gEeonK=vY{wnk%sRPWKDVQ49!%<+ z*N(2_FYjs>mLvze8T-rPyZbpeSG8je!dg`<8|x~I3+pdS=i$8NEO;EoMo4$9%)u6` z;1iuGZPj#*3`NFxQSne@m~)Pry*J;LE2eo`0#m-`;@o+7KQS4J_ct%k!?(9FdIv?v zSM)9I$!c|&RN`NdkIl_P@%cW=bbPr^l$W0B3V@i*gj#u!d?RvGvRODf5s6dDB}7Un zTZU>HPS40k%d`llIw+|CDJ_XeO!E^+e5vAx3aDr zUtBRZWDsg)I8%CCZzFHEg*VGVVLX_r@w}X8jv1*OCJ;)%?lF616>|43<;8Q!=?VMV z_0)G?6ny%u_``eY?>wa6yGdP{Wr`CWV4i><7fMA3MuvvCJGjF9Jjr2pe2P0M*eWu} zT$JEjS4qk(3dtyNQx@51YOJ+2pr*rZV3b%~iZ-(Xz7Q}l89z7^QPbwqI}*^->AJf$ojt;KBXfBnZD%QKu1mTxlwYM` z5<<;7>Qh?FQ}}4;UVH0kzI?fY`|-N;XKP7kos5wJB&SQMOY?an6IC_UnL4dJiAfCg^I#HiZWb1vmR8ho6djGu6o~wse5`?0 zSb%@9mm|c*+JpgExL-mn(bn7&0SU}W<>m_Lr3%*JOy^)lDcRenEG7Qodeftwluz!d z+Db4a1nAad7DgFe^~B~4>Y`K_8;=cOC-vh6wP(<}LpcK9ou_*xC3LPHodB=_o5 zq7l}~w9rx|FFpbhk4Jgi0R|;BLq=0b$px8l5dol1#mR_9sELseD~#G`0mD-ooX!`P9y^79W-;mOHZwfV$DW zZ4|q+owPnln{UMkDWJZ_t)$5_wZw*3;au1kQ8q%SYlGqn!eesbv<%<6e$L23hM6m< z?M+?2QKf1?rB}mh`#G{KOixcI=|&gWQ!0$2aqk ze_jpx*M;vpJ>btL@PFTmeBK7YJAK4YrnvXp>6dN1JXQ$W71Svam1%)VBpVkqfDboo zs+Wd`+7a*`JRwY163a~pj)mBa((+L7VTOOuR>LU}nW#wSSDiTLX zbn>uqaj|hW2SR#8bbw1j1T3GQgmnQ&M&ei31U(wingKes;FDw0U+;x~ykh>R2Qf<( zAdd!2FO>+vaHY#ct@(DJ+r?^BLxF>gW1BB>y_~}Q_CD>wvcq04I2-hOxl8`wI_aI8 zQJ34{Q{9f!Jx;5W(AGwurA4NR;`grdw=U)VE3C!S^cycKX4VtBh6%l6A4g9)VdQzfeGRuWUv=p)I z1o0rhz;0qbW@cS7G$UAEO`GWFbd*t!XS7#VvTL*fRk;C+!}0gdlmkuh<~-m0B!}*@ zxWh5Um8s~jhZw*k#Y5A)%?1k(HJqmtA6wK>e6o6$&H>mqkL$# ztZy)9XT8B#&#%oQ6(>W^2eJlosXaN=qQtN!dCYjZs3;{uLJQ0hL`-(58VX{oa-xTu zMd}o29L^~`#10wi=HqRyOivo^>Ci|;j3@*h6M%346kqF#)cBl4T!|9h-4t(Pk!C%I z%ZAun0zqPcR)yuVy|^q$U4^oyD7!$FZmR0|h)8-^BtI%y$x8)}3FJg(cBoJt+xd|-lBeRg>gFT|Li7Z zW7&Uk!GC<%c4N}~!~5aif5w{}07HXz>ucx_Uub^zykL4L^mLE>;wfkEFf6YYbWR5i zj|X?QJKVWRSy=QpMY`ElYWqmw$@Tc_=hUuBu-A*YKNDZc1#>mnIXz)(Krq#!09rt$ zzii1*9<36zXOj;5)MI7jjgHvKTG;b#;Yfv7i4?rNowv3u2nqv8B4`~T&Mw4tcLvT( zAznO7fB(b$lOwKL3MP7j@82d~T!ot)_r@iAq&G5=2K;S-$v^CYRj8+Dfec$BM@RZw zxtf6lre9?(RhH|LR_w^jvCM1m&nb5jDa?%RY+WwZ+76`3G3^7a!Fg(Bi)(qkZAY8m z;v{oiZ$rM^z%>}5fS3675OiYK80o!_25h^Tlr*5BoAZj)c$>n(0& zH%{ahHD$Os0UsyJ5ML*IGeAXo!o5IyBelLha`U+Q;BI$WZ&H3IJHI2bV^TRWu2`GV zj&>=Skq$@~(3~y6*n-`*%Jbgrqt^IyL*!Tcl3yN(4x5mZxv*cI7QNYK?Y9KXR}ty{ zAe(DF*+wZ4T6R|9CP%d!>s=~2n@NeNFUm#wxeAyOO=THeDmEqp72@eBOBOJRR1?j+ zSbzy*&hsnXbKRM4mH_VO@9XRn6&9hB^Lwgfi@owY+lJZtiv9xaYHQ|9BkiMWncbzX z`ht++ERTr+#Pj>=>j(MO+Q`am;@*Vf);#V0686~ z4*Kvq<`+*GbH<R=Vb$3i4!Bjxv|u~x~Lml`5VibWhKz_J>H|Mya!uxmkTkoJ<*-|s4Kf=r#nq+ z)A}l{peZ+1CuT4sLl{If8XjOO|Ku2KPfeaF+P3GW6>;h67=oHbE0Ky7@d>`>R!ApD z5jjc`N3T=oup_AWfN&G7Mg>E2cu9%`mYcZ^34w~og*Pf#CrwEMV%T^=T5bZ{B!-Dp zyrDd^w@I-wscLJYapIjB@nC2+Wa${%v*0zhg%;(2^kS#BnV5q1AbFKlL7R6)2RgG5 zDv<=1YePT1qx$&+;rO7xq1QK~)>c{PR^5ZG>83(yCSfd zyPaD%fGh67uiPk~-YOfJESaBZNK1@rugETta>TKSOes#R3ZN^U2`XQ-0z%9}CDn6d z4I+v`M6OIgDo}z7MnWb!Ne*>(1uLW8U%r>Pvlyz#0C8y+&QZXf4B)A*t|{KG$$q{Z zuMm!t9~GozI_h$*l|1n59{JrDgolq|FW-sS-SOOBM>Oa>hU##mwV`LdQAf1_qdf5K zV(|Z+Mct^ez1!&W`}OeuJHmjAJd0hm*|glYHPzS8447tC>FyOx|D4%}#^bTD(1a zDJo=AsLmNVkw_F^lAX%rsL_1FgerKw0B+#eHpnBUo25Oac%9Uvp_C*OhrwXhu5M~+a=b2IHi2J7=CNO|H(A!%{ua*_t5`(g!o|H?ark0 zXP30E9tqyRWM5lGKHtaO-w68XHvVWfB-kF9GTQe~Ea%!C#y{a!vSef^%WZ`|E56f?M@8(FNZZI+U0j)Q~ggQK$McD6h{C_O!d z5oeR539W94G>$VnN0LXTB}2o>oqFnId(!4$#@SL)TPdNZBD`2;cDh6#*L$zD1b3*M zAFb~*l(t{gCs5QN4zEX79SvXfI8!ZvnC=yIdP^U?%%3`(@r!*Jw zc;sM0h$A-CgA@hFw1|KEG$ zKVJC#^c?!ZpwsQvjkwKyTt&OHaT2<+ z8a~u(y|e1EyXtfEj5^fgH8SD9yNkJhhQF8(-D!iqyPUpO9k-?DUzx~$c5H0ci36>H zLCu`47EDyI<{Ma@g@_WV&A}LTq6OYs6}-EZwzaRU?~DqGHZf(?+_K>OEdABW|Wrj?S4Gbny`Z!g}F zOCr)TL)7IysfE^rWFRkcHI8B1y72N;SV9cc(i)W33MQ77onw@NXU9qV>&mU43xlMDp-idHx7Lt%aFVMwFD1I!0wvv)N6J z33R+|Y_MH_t~|!wyf-Uux;XN)z5GvBa{2@|U+#)7+OYG5gq=oiDjXQnyb_RBF`)6f3@lCntTMOj%Hs_-W^wrhala<(8`{IR8hKg-gljTsI1qSPE zpB)QZbm4W`fpWLi5yI*eXQEFcN`gs~2+6VNA}PI4 z8cW3lxZ44EfUmo)H7?Mrvm~oIlh<32W>E3fOk%YvEty0J_wYuzx(8dEiAlH&7OhCa zmok|CR<;UuVxd%4lbIRl;NWIu6X@dNXAN>WI5iG5@q%=0LN+HRg@&Tx{M&2Qo%PZ# zJ)lg9uV-f8mq>aL0o+z zR*i-wc}5BSqZ8d)4A?L4GJblG`Rz0QU^hHc>JC?gLxjEoVt-FD%tHkCP6-JV28YT+ za5C409m(zM(wSkeSC8l`%PxmU!T0ae`a9jT1?HkCP|E`st-d#FeV#OUJ+1;D_xdfQ zfhDEQ>u&gm-4O7i1F@KCXAn8otKi9G=O8ywmE*TJ!&x7t&keB3ila5TQElys>MV?z zHL$e?OpNPb8Lu~oG+h|AP=sW9fX)J1M=2|VWy8RL6c*IgREJOxM6j2Wy_HGXO+p#z z3!0Td2Q8RiAI9Hrvo}(}R}=n!*@yk@Jp9)i(C^Ps-yeJZ{=(-s*MtB1A@kP{qc;Z3 zH>X{$ZU?Rpf;ZRv{{DI7o9mu4_28>}^nZOq`1?nYzkU+>`2)`%KcW8cCiaJ)$9(vJ zus95zG$M~CN%h&zY02I>N=&-MsmqAo*=6W^0>V=OIRj)@I8Cp{83rR0v(3d-F1anf zbxmGv)vl9G{x=unuPq5?TO(GD@XbEN))@Igvc6fn$XZ-dq#e)flrptbBel++CVblLcwavpk;kzB+1Q0<0fyu~r*l zwLyttpCyksLUL1rBr%BUJf4a00-P+pZOo%XARJt{ zfE=#R71V0jco@)W31g$m#RbjMVt#kK+)$m|-y*0kh#u`z>vHK?3X-EOXlj%+H73)^ zK$_@`aDDr?$yh&E3e+Qu3E%F_X-cO~m#1ws6mSDQ@b2#XD3XUsgOGvw5{}z{?rEl@ z!XknKV7}hiFnC>A$z*?DR46n(naL%F*JVmMNO*QUCmw}Dc-z4PEFB$zM1rl>r6!Vt z+0pJvF-|2Kcyl$QLMu*Unqzze|nAm{=LYr-(fsCr+Ha}nqtSnw+EAcb)6m!ot{&ZfrC9Z zBLik@3vSC39{O@HH4MFa82kK^^k5Tx&>3-mDr35cKT?u5RG0R}hsS5fLrc?*#sZ;1 zK@;IEi&Fji>tnrb!Pz=zae_ED&eI#&nsVg&dBNgQR{u16eT%oZ&3W=b{{DMe@4c@% zyb`~8M1Ar~`1o1O@|w-%4flf+*OiqJV@r5}Dn`g=Spai56c&XJdrM|k7D>#g`dVR0 zRa9jgMx1ApkOBBPplQg%lsJ`}!fh5#E7hW2)M zu<)|CMZ*H|=%82%WN0Fq-6vVg5Dd}Vzg5O(f>~E=(Fk=I3!F;#u zU@>EPKI7Trk&U^!2A$NjbK)c6DKuC~W_$_<oK}G}x%v9?tseaqdjLYm*u>R85}m#4q>4KYNh&=#V|w6dVNu*dTDaRFlt- z$m2z51Q=eN{oEL1ZGnCBX5|O(^}YMJ{_(Z+^L^Cps9RZ~ZG8=VZBH_}DwtW$sBa*4 zw}l(3d}qg4i%ar?dTLf>l)jx-TMq9mgP%+!Ufv52=@8(k3ORiGD-GV`j!g95H`fD(`&_CU?dMnNHO;X6S{PsBMdI6&gdUg#Cm78FMgkq?{wM>qNF!1yjht%NLMo=Jie7K$xYK1wea) z2saR94l0Gd3l+$VLAEl+(hq=s5&AWbk+> z=!Zl9zaDu0Z4>s_E#x1!A-`I5de8%2PTKv~so(dvgMM){=;^-e^Fz;P=l;v1V0R9@ zf9dwq&oTe^IqZ)wJfAIs-#>tVf7kxUXO2I-^!(;|!1H~F(;0BGV6if0zcPxSXd|3W zC*0l0h$8?B9>mi@p2lryDK?|P!jA?pLXcKynO|wg&j3joKw1E*Titp)y_b3eX6l?K zOYArG@R2;1Z9R0RI#3=DI8n2ydC0;C`zr2I~*L$e{z8~<9+sMb0@bfO8Kiwhx z>C*3y=gz-7b^gNx?5!F9{xa7lEgbC*bn>LyqI3#AFwo1|-_?>IgU?K%W+cHSOt-qc z$cfIBqYdrOx@LQ$Y@l0}DIsaa3U zcnS{MR4rs=h?YmrPsZ~x-c9m^$>OXSPZzY4I~|5m#V3@=rDQ0?*9t(LoUy(ExIj46 z-OmIBd)EENB!v*Q$5{E>`qELy{NH1659|Y3UBNY;C9NsC78>}yh z?QdbKMG#4RRF){!$pU0ZS-I+DM4&??+^4g;SSRP_iWqV(GCu<=OZC+#{Y#aBTCOu4 z0(411`MgLn+&vzTqN5?9j$qBmt`&fvA7H<`7X0IN?>`)Sez)oT?V{}u>(1Y-JO1TP z_`@aFm#3ubyWIWFgpIA}$InI29*7P$$SaG{ds~#{N$>Y=6F$2g_LtZA?;b<{^fdV2 zui<~X>-n1tub~a)&_5IJhY+1Nn2}CP-}LuPuN)x+1~Vf z^IpQuL+pHCps^mBDFWxqadRz!`5aK5;?J^pchEDax>}|-(tI5kd6;Iw9>KWtZlp$v~p5@%WA*`;qEvd20 zC;(UQ^7oEOs~eEh3&@Q-h#R+Qb+z6OmLOe3x3vdWwg87hL_ps0yjG&9h#8TwQ>bgZ+x~Qx6Nkz(KRb$v%$OfzEak z1|`hhQppUTt`p5RNV-a72oEb_NH854igpD_5ia6L$HGMaM5Kuw6(3xwKORgg7X}F; zY?Sm+w1=6=q#gbPMV)n`rV@S{7ppH-rSph|s;K5F_E5Y0V5PP)msDFoZOEsm@!gv{ zLr0exW_G~R5g13gHO(%!=qNgV^U1ps~vDL&`eZ5TlMYrSgQuBNHR<{csp0*&5tDqO1alKhc0TKXxmUm^k zTfG`ml@2BNn6f`gCk>P^K!06uzai4z8hCmGU=37SYI;UQ48<`uAwXR2#6(k8S8k>Sx8|hh;stZhW-C>7WB70ho24u zzghLaY_oX20DZj|ac$h`)sE+P&+$jI;PI*N>&t+j-}C?aJ>MUG=Ks|LyYKGW|MQvG zAFnz5@zV3FUF+ZMfxq2z|J#eeH^)vdw)}@G!PboH%9LMog-cHjyix78(jEQqs%U+l zbiS8yysypH`m=;Ko{<2}1(jW)U1KPHo4>IWGHCQ$8-pIrA?|JB-|W+#jfY=t@R?J9 zKU}Eynu#VRbFl+7BIq&%KD!x znOw)MabcMfzB_8XGS_ltKJIrf82@$6`>%JL|8>XpZ&%!ZxQV(&j9CeTusC8yN{6x&rDrQ7E(hQ~#O zs*-5h6gt}3!jLJQZ7LNLQCJW6C_ji!A}LqOq0Y7@%`&Ck5GR*tSh%O9v!{()jx=+m zr5Ebttx8NrdU&F|ylF@z($~w!0Z<9vDM>-BBri^~tAOW{tqc++x@Ad%iQzz+04-6b z)#jI?f*^wUSd$IJN8^<#Y7meuEsjR>(IWCpsp@_Gs5;$2|eF`t^Yeeopj-P_4C z#)#$;x(RAih&~dUN3qPWqd1xoYF8j)nm{TR@4ve`@bdmnNk-g|Ubs2IFHktXpApl2H)UpH)ZdSko zxH#K|1^Swn$q@jkyOTVTq~b-1n8?^jA2i%GQ_V1T$?^)Ktt|mQ!ZkbnGD@eSsKrq}&On}wM6Vm*t}qq0*V+x^`4&om!h zQ7yNUyR_Wla>=#%w2>OStpV`K4fMB9ldnyNH>)jgZ>w%zDR1rN^sI=+4<#c@v}(O? zI2Ob+LASx-a4GD`mBg{u@Jcm8o&-}$aeM)K_iWraRG6v3WGI5PG8i2LY6@Z!V%_;k z(3*o&X)&$d6sq2g?T<3{p~fu*QbfUzacV>kJpo5tz_#G-O*ma z&kj5~Og*1x)#XRm=wzC7xk+%4ZyR03bhk>bpBWY>(~_7#ndZe8TK0@bH+182OK1f; zW^pNMYojnNh16MF#U^3na6T|M&|8DKvdr7=hMqTCd_52Seg*sfIQ;PzxwFw}Y9!*} zGp){GtI^wwa&6giE4tJyRs>SBtx6gL%A4T|oil<4D%((Py|I(i@|m%m$w75@Tf%s+ z`2Jb1gaaAulO`p*9-q`NFK1_~oinsvp&XkKflrt;AXMTH7x_bl?l6&ke45o{Ib=X; zrsII>ax7VchZWERb7y<}EZZAaxni1hnO&O46agkn7LIfg)K@Z&h-T zH7HDts>s9@DFZudBS(99+U!^}D-ejZj^hUKQ@s^3&uo!rSw^tF2ultCr|rywy=OT-Edyu6s3DWG$sVZly7NH$+LQ0|XVKVBw&cYylak?Wr}%zry?ea8Tr zg}V0UBc$!-DXo_{>6%wkk2LfL7O~8k{JquT9|6tQ$0lgE8@713^f$ z9&4_;wKY<&vi(21I#X=jUWMJCRYnHBy?ZLy(WU*h(C?Sn)dI{z2 zj|m81lOnTaOpO935_rcjEVz6-h1{{R3dN=YRccVBmflp9Rwz>x$Z|~Bl+NSDlPIPK zRXNI9jVKB4EROMjy1l)%+Q}NYTY@~c&Fc;2C+nhXM(q8`3wQjSG2JrK9@Ar?ruTJ}{_BdbL41V=A zp=S~?amd|!lr^{)S3L#G>#*w@^Uybg%1UtS3h{E2JXjl6r=_t8!8|fH(%*(hwI6Qb z@nZaNfp$th6&>U})LOGV+}fxUj<(8f9~GT$s8%Lb^F!Lvc1?W|SHShggaMVnQG-mut^HncXYmm_Caal1zegMF0x zYUJWFcW8)%3ILG-t`w+Sc^WH+8Kt5VyZ{hTU`l>SZE3_{52>vl4|M``3_Ml9GU;&; z)S8HQ>TD1cqzCWJN{zMQiWKuwO~`1UY;LBav@$KPG(A%zvU3EXA)bWrV7Ra6+pAZI z=$LQ_3l~C0_?Wy_n@L|<)^v2nmzBmOXxnf!*RtMKJyoe~k|i(Il`R{p2FtX50N@-!lZ2uShs@;VZZtIo z0T3Gs3`OBhC5Yzoi0~jl#+voEONTmB&bQM08t_VP$YNhZCXW&51V+2Xi__`66b}n) zz(@%Xi*~njd)s{?2=g`+km5m6CA7ac`s#U>TxoNDwd(A=yFf$z?B(!BcUzP2=H<%R zw$l9Ddk3>!?e+O-oi!3U7m_avQzrRISe|K2+oN&T7q{~+rjl!9fkUO~L{HFLg1Np# zd$b$!)g%19d5@V&V9WxqE=2pMX)|ZazEes6xn%Z=bpA*-x1~6_lDRyK*qTAEPq4&H zn>-0NEdgz723*}hY(iLFXLe;%QXI=6KLf?an`b8lnDp7%3goEphT4=?y|lAeJHOVj zvQcq*YIyx}b=a5}iwW>D15kU=QJhnsBaI633URUvcCaWcNOPg>u?OG&ml+9@TWJ@b@l z)df0}8jKm@?``pJFJg|{EZ+>f|86GiipJ7}F{2fBJ$jdSpQ_*eK+`hjR%WypT5T!?e|hCTRNRh>nh!uCCa=9%^SZX}UkXyPkjNtbKk| z)mjrXHu}0?dbExT z%_gBsIfx=UBA*nN69LPFg=K}HwZTLkj8hhv-x5^Y8CqBuq^&>{R#HEBv-QEdOV9SJ zKImoc3T?-kE(4svVywLe3v>){b&7SoAXpqt07ngo{1_1F1N@zVY1vGorcQ-sNnwum zz}FvS=HPP*qtbKnIVDlTG&e~yc(BU+V2im~<6~ffH-ll{Ek}Gg7xK*kZMzvd)=17) zM=9hiO=?1|f?t!Xn6 zHU~oi=+}pK)*zco{90>*`g;l06+!KdxVj4e-d^w1bJqE}s-hY#Qu)e?(a}mTgw!cG z319?Pgk*C}EXc@p86P6-4q)HkqJMWm`)V)j7b_tT2E1QvG3)caTU(RbyE7_F<2sBf zqiY%JGScGm#NbG8S|M3ojT)HaT|6jVJ`jy9#u<$8jV;mHX+>Q%qaZJ8cu<>{n`mtf z@X=n0Jd~-18$RemINz**syb%(XZkEb2oxn*)D+Q{5`Jnka{E^;55Jh`;%yZ+akQb6Gz) zRBq@>Ke^K}y_PjGqsmg^-ZqmX1jC%c&CROe`lJMGpqNZ*(W-h%a`O0$JQ*jMOF-dV zGqu$Hv*y$5EhAHk@o9x|Fh!>)^h_!A2DwVY^tUt1PT`t(RG*_#aboFV0g(ajCK*kO zk7+2#GLeeyf4ZyK01)Wu+HgfjhFMJL=+(g7iBWSAZND-NT48&oW{kXAVAJ{ z5DM&4Q?0csM?)1NN9I?oCNgk7{x)Vg{4{<{f-^ApcX2`n2AK4^EL+}Fr+jcydA?l) zhuRW}Xo$Bh2?{RzxHk-hVX<4U#9^Z;_?M51yP9IzDPH_+gsv^7rY++6J^ANni0{_D zzUg%Ttjy)U%6^OvetJy$?PJ7pA2`_Xz56h-cgm{DXx1?aSwD{3+Kt_ri`wW9cykf) z`IUh0u6TdB<#DFBSSSSD3h-pX{b$GSKRk>0@m=0dpJ!L2-K{(F^)u<@p9O2BZZ$MrQzcZtV*S3*}s{8C?TX9eHG98~5~M2Rp{W!09b%e3m5 zzV7L+mMlTc>QMPycZSJyyBaucHLO}4DOVYm$gs(lLkblkP1RAA2JGT-`ioE6-+Vdv z{7wI@=N+5ZD?j>w`%k{yd;InOoiFCQ*Tn1RiiQqo<YG*{`nFZf=S&&>dx{A-0yM zsJM|7xFeq!!ix+lk#SODkVzC|N))U>7&&g_8f$0~9)JRcS^{9gP$~@-OACFwtN=J$ z0)LOUzu#iR%{vS{O?KqMioR#KWO1jtd$7>c9k63CctoI!83?em6UD``u*f)Uh?_MS z>``y;W-YFym6tIbY=E!3C5sx3frkY6y1)=FZQa>(i+PG1I8Ev=ET!c&@eM}BNVj@( zpme>t>Y%0RKwo>-QaxKFso|qaB|#k}xak&ppozeeumd@g*@ohpj6?>?(FDt;4W>xN z_O}YhdL)A#!uqo4v7Xq2wG0&>D(8k3sYt8S*^9G`i!6$xgl%YO3WX@)vd%&UC-TqQhW2m&Wp$8eSM*eQ!!mliA0oLG|aM6%NQO= zKRB&_`0mu}U#xAMl#VaTq;lu%46k&KLt8Pit%fX4w%=LED#^loH~|iez^22xn$va` zbX&7|Lk4kluw{go6CHsll%%E*3A8YT%YP0>2kIO2Ma8E2rJ|ALYGHdp!d!!J-5`@; ztR!f&a7(~~zwOUs8nHn@NdVv9m;L!A`!{!BKfQ;2wS)Gx0J(C3l*dHFJhGLn5*;H1 z4vanc%^m*4Tzs<9NmdMn#el9w!N{gUkl|))50+-sb>-qnMBv!KP>7dBwc7pF9_5#( zp?|)F|F{|WUz4F7R3Hif36WrND*W^~YG&T6Yt*4{#&2o`S84E2mYORnEYqv3bnR|c zZH`^TJ`=OBvB}`MMa0--(86-~(h6a42;0m}N|#^9&T?w`$te0dbFjR(Q5AROrs;00vVz|Ks{ zKu$z`iuph~_;?BV<0<^#*8~22-TQwJ+&&++f3M$nwZP)E-RIc?i8mZaK!x0pQSJc3RQDtnFN=>=~?i>}2j3i9WC-;xUO->~a_OY*>HMcgTD5W6reMr=~)jGC4GvirE5~4`-+& z8mqIen63;T4HG=w*VHOp+H!p7YEd7ZW`Fh|ceu&_a#z@?k1{b_|A6-P)3Lp4jqQWU z)%EOT7AC~aSwv@xX@uqO^pgqgVrywzZVnS3T$d#m)1#fufTh{nv$t55Z&qG(-$>5b zbg{lwespzU@vyaHtgv3MX2+sTr6^~{2Uu9tWvb+HvH61JFgIJ%;1H0?q`2I4o`VI5 zL0XXz^`xe4b|{eF6gX{ zY%0VML*G_AJv=bN$6rKCjD|(n|HrDW0SIt+FvZ)tg5MR;|u@pNCmzhju2uAZN4X|2_5PK}SXG#HA~rrNV( z!vQN2um~Vq>RqG5HkRYF(%q+rlIKTcXGf(&3kj{0r2Zvl*BtxGi}tDGveE&5-K_fF zSBK00pQHB_?@totyId?jp zyC?Ka_srbMq3`UZdv|ufy}SE;_rv=)yu3wKJ-_f&)l=_{P2SOUuQBhw(jJ`hmsV)& zYvk<>_}&KU=CNq3r&!ELM8yLZF_A~gs4UJF6rssPC@jv8N5IIb1bJzSQI2QA<1x{Z zZrTeBxQ2qKl$TNyBSX+>so7{aA~VU)7j(3Vt{-*eWCIUh&^uPYuwxX;;Tc(;nQ6Xo zXh=$QAS@~>GcsCCD6<3d5NgSoXMwRK%3flG?u=?O9J0rmF)>B;fet^qO$KHgJP zSBa`pk^2^mZCw?e?fkjk+Qs(z<1YJUPyeb?dCOtE-Xpyj=Y9A<`}9)2v%p;FD{f>Z z@p0ize9Ys+k)4S)Q$061#?9J*NsjI`k?KV$rba|}2X4So&~Iafc!7K@xOuMJ+#o-F zEPZq#SspFkUQ}G)Gd(zI+?mVy@-x{tKdpH56n1fu{`Y?!zj4Z4S&#kXi#lFKa%?Qn z)W>$2VN`;rdlC-VCF3r6o0>M;Uti15a2WDOZMemO?6JD5CmNV5;kOIwEW(ouC*RlkQSXCm%z^}U=xUjs_KG_tgEW7eSswU^+Zwl}4)Os=Sj^Q?Z%y!DKH&cCVb-6HqyPO3_op4koHR~?0;^-l<%OiB#qhO;p_svesJYpM>FLn139r)|Nk^y9?Opu& zG2dy$nQGHb()2++ajuIn-U*%Phpa4Qou9K?Y*4x^s<0*{yBdnEg5W9x*!9s8QCwa+ zXyOMRPG8kHt5HBW8WdKZmD`l-cCJK2E>4R^C=*f22!bkzq75i(@>W=*+eWizPZU!} zmgQSRtCy~&y_OFiPk(WL_Hw23Vy$y!LEUac&#yoqJ|aB6mvgoU1K*CrFDHB z#9CL<+F*7iHyWM-{x7_}df8KOW)#`3U!m)u3N5fWPmC{dzI- z7YmTDC&T`F3-`}gq_2;lk5^M}ucUAE``_J+|I-g>Z*L~L!Om#{jRLUVpE;n9ZL9Jx z&j;O_gh69guO2#L&FnJ7wdCbB%lFLUQ9K^YRAMWsbkOO8?m#*$?g&eRntalQW{S7EG>{ z+9sHeQR>zj<>pzr!HgbXF?5a^$JV<%L&4gn@G&GQPDn6|sieNXkrg z2T!OkNQ?mta#$HN0+s@@QU2vDY_+Jk)l}QlWvCHzi}1*BPr%K|%?OQPp$Wve6ebc= z2u*VH?X10Z!e;7fR^T!qj3Ru5KR_kLXT>E10N;|l;>Jn^E)7+HzS``N9v0yi&dDCY zDhdA8W7fU-C^;J#YeHw*nQ4BYFwL8s5f1SN5;EN&Q6QqP8W!krbuc3)#NXY30(6u` zgV8l4kc&LsR>Q5M_pH2?nv4lQ;0hi#SV2g6$xs~JT02o!xh1_lU^$fvzp_3?5QSwYKIj+ zyvWjYXX*Mg8(pw^SN7mpW5WnDvJmthsm5+vhC1zVd; zzrI)c{H~=_&&tgVj|&2!o*+5UyBM2A$%Db6p=5LlCL!3}+o&XO896)E7eM3uV!fZzFD6`rTEgg$!syQ)=18whYD+8bxItao|vB<%PPXL z3UKTKR1P9A2M4yds%K|~7!>gH1lWw^Z10162R@d0kFI_(m_W zJ{maNGndgL+uL%tjzxV_8IFPQ(h6{XE>>&ed_%z6ZteVXtG73>x8u7kX%PWH)8PB+ z1@Fm2+N(#E&oApYrs&f>#MXwe+EOswnl##$*{4A(n6YLtda6yrK|`!H)IlA7P#^y8 z7UAtl(!I6F=O-DDFL9Umi?{9yZhvim^0SeL-wm}c=9L+vT1b_Kqg4{^I{p(rDd6wqSHnO_lK-f#Qr2aE5&nKT=SEjq-?tV~gfiwOZ9p1|Ah zD$UT+S~EXk+F#MVxo8p{q8aOXWJ=1-#~soLm3yv^omoJOwYZgjE&XE)#dm;mw#`ZeuQvxJPSc zJCm&SA==6yW!jbRvf*t;)QpR&JOft~%4m$Os7fX01c!P9H^iwV z@ty4`tpmYpPG&SAnZ`U$JB4Ex^jlz}(>-haJ= z`f?|JstMTZfu#WqwfRpuf?r-1v^9YatzWwqB4Buw*-f?NiXny}Pe{M%OGmGC?p>A#{ zrR*wmzQY7rtf8WtX$66E7{mf-+UvLJ3g*ck09!Kaf@ z$vj$aQlvjTCMq8W_XFM{wxH9{R)9nbDS6NUa8)ci%cENo{PWwq|MooZukRKA_^GT_ z9!HD@s31Vkip0fT{j63jspFKz`}t);B2#06BLcnLh~~D!Rs76B8&zK$Yt|?CnBdFp zjneFH%d3m%UYe0d<~tMXiTq<;9QdTu^PSRXpRF5-zpiVHwfDfrD(&ZCLQPj?dj z_7?ZsH|X7IpRRT=zXX5rO7N?n@PGXc`Q_8tS1%HtK1{iFl6Zd~aeq1G+nbsHd`WzB7OGPSxHTQQsUV)5mw%kqvdB9WCj0 zb81gZ#GU<;-6`ze2>N0y_uf?A`&&g%w-^(B^o?zu(tzw9qHiARr|0B1&TTt$`YD^D zM^S1NrrYYE6E>_(4XNW@v#GK=8gQ-_(zq*UdbD78fU4AF(1d|??O7@pLN$y_;ewV8 z$;z94v7>-xNOA4+2KT9J=OwGNsKK_70ZZieH2QEJJ>C_!GMP8tM=s6)T|lD0_v4m8 ztQO|zKuy23Apig@IyOHK>JNZ`p2IFiCx!zyDlH?xBO}Hu4-3gb#TU4lFd`z@`6A6?m`&(0q21vA1G;qiJHf#xcO6vBC&M|A=6~qQH2xEEYAQwKZ#F zLuj|LX+_W+G}P@TgSD?UKBcA>CT1uC0u- zY-m~4S~{8K{gupdW7hh3=FSH0y?5#d&)Uo#g&P}9lVjrPQGT-$NkV%{Sa2aLI|mc5 zuIDUGwA?yyO!i5(R?U0cmhoZf{;vA<3)}EOp+pd~GFLAXL5)qhrl#z`YaqY}kjUO8 z5^v`+X>>Vfav^tiiDvE2Ddq$>XmgLw`-_W%U0q0vG1$=&@vC1n|I2TLzxjQ|?|;?! z(R0!J59?lD*1va8bMHWMw7@xC5Dy#aCJ}L=v$|W!N%sdP33YiZ|M7prNxkL_#4P?hdiSj5M3Fer0GVD<*_pkg_pp*q$*Hp#idz zJf~V}lAxW29J?hq)CbJB1^)A`;P*%9Uu}o}$D6RfJOTglQOsAn*WO=E{lioCukUC4 z$LEB)aGpJy>sr#Ug?cX#oGsk{^{rE z?>`-#a1`zK!(T5Gzdd5Tou*c!L1{W@6~c0oz|v^s(RQp`^48XXE=Tyv0^Y3j@3%pX z9ZB|4lw9xA)E2q0!;?3~C8mSkLG;N5>-r_>Zja5P&4bS9X*aDWF7gSVb+OkQ7hKh7sXWjC}9I-KwFP z0!?SCSeGcbVp^tHotwg`yPAqF1hxv4XadBRNJTe9+nbTPGo#V= z4*xb?kdOrsFi^?_GQOXir`aXm4VbIpj%M80 zSgF>WF||_C)(h_(q0byO4eTjuyK@v>S?w-@-IDJ#QB?v21>s#$6yVU{(9z&%5$_nFHMX}O~tF5rG&;XsFbYrkfRbI$M z1zfJRSk?K?7V?<2&>)X#Yfju)FBVZo;wvx`E&oj%z2cMJaY4)P~Qscwz6&Z&};eYqM>1U6W zeRV;$YVR2r#0BgihO*Q)-@QeHixU5CrK!kz3ZI^3wb*ww5pCtP-QI1pqETHzgsIM9g5& z;6zkZvIk(LL(_u+JRa1EaTP37E)sUN)an5^WyPi3JO(vc*Mx2{6BrdK1XkSGj(PW< zb8@3$Y)N9W5ws?(&XC{H&NS%@Yip9vu2;@2a3}?F#Rb@MW^oBAn?@nvP$`(K1Xz4n z4VUW1@phSTZ@@8SsF&wu7+J_AQ{{A%a!^*g*VZ{#uW6%ltfht1t*8(0m0#b^+Fs6b zIV93YtMD2=E63&g#pH-kax! zp}t}&2@H0Zx@=`4Zi-u|Ny}r@idaKyY@Ic-cMZRINU`-@r8@YMV0J}_su|zo;;gJx zy!TxE#k-1kZ-sa7kly={{^@6&58ra`T&F(1Re57ivOY=cYDQ?Jsd8RgZ5g_vD9s=w z=>-&sC*bG7&6U_819G$_v0df4wv2lCN-}>!wayWSc18R5J5Dd`4riHbwp`wVDwC#} zTgz)JiU|qfqXwA}>o=y1f4jqew9PnOVVKmZ{dRJ@9$82PT{i66x_Ei3a&C=x>yGT; zNL0uOP?$NpcUI>Q=Cth^;CYn?P7DKOC7Fn1PhxsxzgaccWma&h)_O@*aV{n**w#?p z*&tu&ceu5Vj|a$t`U{vSVp`bFSj*Os^x>hxWzTf`es%`KEG6E>C8O5A-wTea^sm~??J`fMY*C6&DJLqWx<82AW>&r zLwB^UJIpZ>yK|I(ev9qwhb=C1AH5lIE}6>PxJYR{NCjbefMW>N;Nc|apeJpuJ8G&kxI+OBM~Vk)qknWr`OyaA+b!%#dt9>n zb)29r2juo#%7Z=P+;F@}52Qjs&H(~8P}W6>_GdbCX*0RY4ctn`;2%vX2lP|kL&)tKq(DUga|1KcpdL~F~rS6GaO zq9B1fNtuL7DuKgsF%V>IWb8Fi&%y9Wk(CuG3I(E}5us2a6?Iwq`s~3DF()svwSnpC zsH~O~I09IkRU{PV$SO)$6fBpWJu}rl+^=EM(rnhs8@I;qJegS8Zr<87y?wDbGu+s! zVXlsvW}HoPBgU2{ftyDwIaFx0zfoN!uPr4LBOIcH)rRml!_aRxQvU5e^bgmg?skJc ztG|v9$XS312hK*~Tn8;40C(euTB-N1uQum>ghluM6hJ^A+V@1BTS@ zy5JFY!h#;Z*Fk+SQ~G3?JJkZURt0oc2Tism+}$s*>cIAV(WA?z=`mKQHz34;=Nq)& zKW|)a0zW-W{rz+FoBOca4+}0|H_q?SuAiVjcu{P3LMB(smUi@AE@4j>dAT4)RM|!=js&ro;LcR zm9a3$dwgnsdeZjsh56Ye?UM!aPj_fm9%$7SIEGZar%rtf)88NLEO(qOcOH%zTKK4* z8e$V0VwEF$b%aD;kmznU#5)#%a#9*MFAbL-92N+QC~?}>5?6&TTyg~>95$fdy@(UC5iSekY5O`?dShsF-#n#@&sjkk6kMsBN21!|o zz#Dk^fXE17F&C83u1OTxHF}z|mDxTh8dLwPdjpkxkzi4H#-eJofo5=5S zkTt?3Nx9dEkbwKCp4QG>(O=#(bY}pcBGm+!v@wd z6GsJg%>jfWpf)89PBS#E$Vz20ngF)8`8Ut9fAVF;;#6vAAh^6>fA&uO_G#7eCSAk< zv4KF&i;%El&?#57s}h1hv?owvKS$nOjrinE z^^bnq|NH;8_w{$HnYVRXZz>(=9=`Bi9xENCb^WD$;A=ekuEMl z3mN$27@(@7&km_wdKw)Lc1F@)oadhHpe!vO3)5J88>CAg)niWS?#?!u(;BKhdwbFz zzOy|1VV~=uqGhh=^3#sPhc#n!=#jznDjwLI%m40E@$G}OA-kJap-1zGt^VYD%Q+vM zv!9*j4_N)jUGV84u9$(22m+WQ2v?3TDNl<@0K&3Nbwf^5OSY~Z+tHoBxhq**D;%DJ z_6|pN4n|Gxl9z8uq{f6iz6Xj981kz+2hDb`R=bD2+jndtsYV96Iv{5!)vrF8?U|R= zcjt?(NL0C(FA8MvfVc+(Mp?mK%qT&2Xdx;pEx|uN8qlZ-<-80=ae8zV@bbRO$X!lS zzPm1ZK1(_8%XcZEyc}SVAr#z10U9itVfW_{U!F78T2g1Va2-EtwwG!%LUoP79W9}Q zozNa_hQ2&*$5~dB>(NN^>XyXWDuPb>ai1PhK0hO`IAR?dzm^8CT0YRpgIiV6CRI?A z+(#)6tS@QIMf2xx-2rxo=KKcov3d})m1}{3S5}m0|_BhT@;5leawb2)J1n1(w0Xl<4(*# zf7Zk_{`9(VbBEDljhbFS9v$Q?&Sh-w7VqrUTP+QHCmTz52IWJ=OiLQelpru9zW3$) z-c}c{7~vL)Tq=ym%^(*>iukZfUdH0EqSsiF7Vc3-CPoLjp$S9<1;mGiM)_ZJgMJ?0X&+o-G2 zUvA4NDS*!kvvlz58%x^T<&=DsoX5PiHtAH0n?>Xr2C9;Qq2nRBY2IZ8Q57Yz&`3~T z5~;7tBW6ZN`hv_fe-<+fkr7ao6I)x6WoW>T4)8YD>qmylM#k9tH|0~SBy(HP{wCt) zamu6Hsh77BcMhRxV|jw{FR9+|56{o^f`IczT1=Y0uVa@#`z9`Ptfq73J(| z6P=Tpo)MN1;m>CmRyD9)bEdfihsrK2t1oWosFG;8>W(V5gybCvlu9&{9xALNu*%Sp zkw8{MF!xj_%nT|kqe4Q)5uk38PK5xQDSK&(FRz08`GKIIfU9ztHif0F!NU`{?NkCJ zC?)_X%h>}utu&XgX*CSjmTp+8w|cZG0oU9!wp~=#S6MM2u7&|nRRC^p5I=fCynA$2 z)yr*nGLqula^G!6tHd}SEkP+kxQBz1I`qOgeIj3m$`PgTkib&Xlevvt+t zv>|8d=smZapQ`!Wd0xd@0`N1_uVVW9!Gx@x0D zR9D7@Mg+^)u{wDiL*gT(Q5k;$H`Z!Sco2QVqfYDF<$l3m6`63`JK z05m&<>-(*hvgoOC)WQs^Nf~A`!A7T0+GYDym* z%D21l8+(;KF4F3Tbaz)jI;hh&NJ?_uP6G>z4M`03hekj`fNui0M$3Q|LKCZt3oKG0 zDFHIp+A`c>bklJuJ;S7CcDIy8dI2#R{OBR;WR zPq1-fzw>ZrabEG@mSU_cH{J^rBT#A4p<)63^6~M^n#<;>l2>AeJ2=S!KuQJo_sCCg zu-9j@54Xzq)~XIyZyrl_)=Kosz|PL(x#ME%22nAe!ykl|cSrW_5XLv~O(q|E zSKO`J{OJ{V&v2~O8evsO4lCfBCVD$Nr7!~A8qc5agevP2WpYeMYn517RK!Te76$Od z$zoYb?=X*BnG_5IXhx9Sge#LrGK4^DiEitP?j3`+45S#G3FEU!XIK1mKYF+`!P)|< zt3XW+s8axi+SAw^I_yfF9m<;OgIk&-`&{Xh3%MifRF)>nj|d_dLEucA)6Y*!_XmWm zNHL#K#>$cku=)5%5jQUY0Gl!2Sn2=eA?-)Il=EJ~lmXqVAyzVyLOcOF5%ks}4tw!m z-zfRajEpGHtHn9@TYBrUcc#eiZ&N=z zoFv^DPoQF5v!BA)BUKf<`ivRRArT)xHd?rieXd+)+l1T?f4E; z29*l>=ULO zNVayxy(gE>9kXVEZ#k~it$~w3H(B)y3oJxL9*_wVPkfHFYz+Haanpm>yKe zqPTVCN}pkWrfGAsVrh)hZ%w&z$Z64qiG*NbEw{TLURVffC0Lh5Inr))KVC|J=cNSF z(6LP-wy|1D!{O-Jh_=R(W*vc79Vt-92^5fGL9oU|XlA+YUTW1|NL6hgS*2UYuf#D)A_xYkqK&(Hr2-m_y+*Bp~yKRS=-lLKJB~na$Jf7*xq??>qGZzI0mPCPM# zAD?Ca_A|+|3;f-!ga<1j|Kn52zuZXua4zgGPxC(7@cqHPgtN84-G!i!AK~BLVJfPU zg1msOA#+5NrKSYb5rQ;@Fc~f>4+09)L8q={+(92~Nw{1mT;D*fPQs=Ji))1;stWLO z5BtZ*^j8zfSTE362!4E-{q!Kw;qV_@&A$ImH@-vf9LApCR$p8kZ7J_|1#2#nN(OB+vC~t$rGk>?x;ks4+ND+L zg=~wQn-Y9=>Nh>ohmKFIASbjp5sb=ALqlc}-V>GN9Tx~PQvrz>9v=dj#Mr@R1~n6y zWT`FnX+u^@WqBxx3^ot><73D?0vI3iIzLT%`L^)>%aRX%WP0*p%a=dsUtJ^3F2|kS z$~r#B?Cg=+ZD@@S7aalAjaZwLvAS=ZSur!YxNBY@uMj0~5ycV10uf$idV2eO^VHeE z;JUSQQa8Ef7+*3jEh#E1p;;JT4zaQsxaPLAQ6WBFpjMcAzFFVh zj7<&)sYyW|9>8pnSleV?9v~{p&+Cf$a$8gl3(j6Sg%$^Zs))GQEMGCpoQ=P}GaQ7D_OXI?+A!$#$$gC@C)|BW}$hGDCgFXDl8ghLN^WHo4`=2)N zobs%W%({jMg$mN<#9cf$z5LK1sD z8OJUSpO}zrEH}IVsf1rxK!y2*gNR5VttLtg1>+~C;X>Tqw{_#W3FW;}3b7YxnV;yanfx*1diGr0S_E2ALe@}j$ETzrJFsZl^ z*8nm;u$+uWCrA4NPgpE8KP^i{BEiE0vC$!Q#YMRAPyqqE*k>8)sInbvh>x z>#hKqi(o@1mpy;;Y@%1OnQ7< zymG5`{KPhS-r2L%xPINLvz7>Juvk*4M43x1O>vL7TkdSElux>7ZrG=yvdBav@B#x1 z))!x%{`!xP-hD8;xhP$qWv`Co<_4i7j_AGBB9|TC*;-&XGkV$zzj&?r`)?h8{oT+v z-*jx;pa`u#w5}+6AA~;`Rx=!Cn}gOgdkE@*qBVr60Q6eWY>Sy2DOxnM)kKJqn(V5~ zda@|GxhQe=3MBHvQeJLbyIfkATPlIJc9zU-G*mXhBH(}^j4!H5x6W2{&vPBql>Rxg z$pP&c%;KqhX;nbk>_0P!xw*r?wL$;vIq%jXdUY{VtMODFzsrpTOYw<0%@2C6*uzb7UT6cwN)JC4lrIGz$Jhtaj;#RT+Q<}t5Zh1@l7?s z+L{PObp(SA=mHOQ^toG^+g?evV;cdGjb=ba-D>%krd}4Ah=WrG%^eiO%Ay#OcP3krl8~F zP_Z$BLXz8DPFn2?E;}nPR>4ONYOA;UtP(PEx?9~;LxY4}nJ{y6+VKIsyC-sKg)p~B z8XCw`R%hv}xH1|e>6!wzw?rb!NNzvRQ1f zf1)HkQG zk5@b{S3Mu@N4$3(emIK@@&tjNfPw~=8e}mP5F-IT$}cA+2$KR#D(vJS!)k&YuOi-m zKwh1SUY}0bolU*7iT>y`@6Bq)Z|_roa$fLao3uHWeE%kLb}qPoH2(OqV)Lv*-wx55 z0)~g7s|!T;G~4QvtElPy4Tys-!rO6@4hNnt4}5feZ9tiExyU_UlQdMuvx@@LV?lcp zLn|jNP77S!1cwc)ZivUgf!UZRmFGn#0z5v5N(i820x1%hkYG^|bZ3OR=qM;F^cOJF zGZF(xXjpqgeMfzLX*MP;0Q75whvOEl;0kfvf?dW;RF+3KSI2843H9ZPJW@C(H<(58 zm2jdOC23X6NUJhq+?wmqWw^}vS{`JoUznQ`*4B5n4Uy>hZmPnSFGV~h42=~fmS%)O05T^s2^x`- z5*;1s;~uQmhRpSWq6rJU0Cts_lva=@ht14lR6Y?N7a8i~0Yu!=POYIuD%Y?DJKg>B z&AMf?T1iH8v5`Z4qUp(6DJP?h2(8S|b<_yvt?XfQ+WL6r<^=TKYT?U$xv?5IKcI9g zo8@uAq_beW2faF-vo>AOV?r(U(XEY9+Y^QJE~>UFgGP*TD}LXr-3;CsSW*rqV$4~* zx5jR7hHS0S-o8{H9}BKuSI^9q&#jjZEs)QislNF2_|LyzeelvWzeNA!T9hmoBs!XjR1Apq2BrA$k+#|@KI5t)Qhb!IO3+*_i1PqE9Gahv?9>SL za>~iJqgkKd(@&kB&Hv`L=x?73Uv4MdoduulgMYk+{P|YuI0xJ?M97lCgc_XBBCapf zpPtsPxUeUSd9Us&Lm(Hh-2qq`>0CcKY#qz15=;+M!sU)}NvDDK+@9n*gT) z*qs^6eP#GyFk^(i+!K`-fRdbmI0y)j0T>(%h@!LWvAe*BG)ii@bkUvN4UC9!TF^DQHuA zu5^SSwFm!lkMy%u{M*r_XEV^_+0@Z#G_5HsL68nC<%CpE00{tsI>#?p9gSwL??=%y3;(FJ>^Ge#o| zm>S_I>JaRbFoigwxhDN=wYjbw@8;>0=%5Hs;MC>5IjWdzPfYX$0&)~5FQ~2z;tr1n zQLwB$tb!fLEAgS`xLq=KxR)ZUj?!7+txm$srg(L$rlTX@P1xlXNnBA{6bwMH0A1`` zSQ2MNLe$VS~VO z9L7FBQ?B&$R6GPW2B^w1tqSDUJb88`)oJ#d9Z2mkCytHqZRXm|t(l?aX1*R5O9TQn zQm#h#O-K)(_UR{uytYClHzteZPbl=4$)Lirm|8))Qh?M-ay8HM++mYB0?!0f8%Um zW4-#t^M+49Q@?)I`1Mz=jTM2dg=(l4l;swr1bb025QQ+?ATR1^t#Jd2yPwGfcw<#z ztDaiUPeNeaA_4%M(^W zhsK3v!Z3p=V`Pjnv%p^5;-B7c+`ZjkpJIfhUY#w6W3K6{^JjbHY)1U}vc@uB)w^Bq znik$Vt9f^`;M+T}Uq6EW{BH8EpAqiO#+;94R^@@?arBD|%HCSoXfOEqe!_Q8v!2ZQ ze!ZRi+jI0Uj-dZ?GvUKs@1H)6|JSdIe)hJky*@D51GptWE)}?p2yI1dsz10&26_W! zCBmXZ?kq4*x5<-z{;%%kPxt%JbYFY8mh@yU?p9CGkT<+px0{|%ow6Z)`>*A**ssH@^*~y4`se^EP znZGt6cL#!8n1f08kMIMug6KA5ZhtQ&BmnT(L30z83p2_J0WKpIRCAJY;()C@{)UzE zu$8ziPuMUM=R0XqT3i(enHUIq4Jx~$UR+q!$kX&US2zB4)&nS4v~^P(Z87C@uyA!^pc2 zs|E+rTU)I8wIaS4EYD>>x+8q=J>|R4bq9w9(+f$R1D-3#n9KJy`wvVz7wud=tt0^j}L)Tg<^%m;*dPo0kvun0q+?WZ^1~h(vsh_cY z-m<*c)MUz&YqBdFh<e9e{Zrwzjl;l4)C;m^dIjyOEUPZ9Mg-USTTpPto8xiOU-q^68wmU6K1S0}AYi3x5qARq+nj|k6a zB}2`GAw4De+EpP>GA2GbEEp0H%qt?Zh&TcaQbEtA<)#oaL&~UdXar#A!wN95JRZ(A zSY;Y3sc3`t@5m?4we8E?#u425vzn>161og%TfzG;;lKI;>ATN)r%Ms%qu?K35YOlR zdz!({EMjgH)oMxV>BVbgk-hr-7;g~c39^&G?ak7QWx7)p-&vVBtf%X1xo&L{=mk1- zwN{lB9UYkJ>nSND+I7OQ-UgGFIW=D2(@k5RDSmNKyzdGcGk~8y#{T!O^Zw6|ivRM8 z{@xUNr82s$01T`A-oGPiZ-j{GV8MmEzFPSH-SYdl%3GbdnwE_Ae!|8_lxx)aQVGn~UMl71Q*&WW4YZ+wYNbBUF~GezF^&!tAK$2TPU4k@ z;EtZS)osG9+oijEjNWbxmFyAj2i8Ut@9x0P*WukA0nP29)@hV#BDJ*3ugnhA(~EJ+@j8?$OA7iV3Z)%kWQdYMB`G5s z0B&w@Qx&EpH#+#g3N008`FB?)FV=Z~e+d8MDRRja>Yhv~KA@#qLdImezfeL7b{KM1 zjNoQQzjJwJz$^pP z-C2`8C4-&Hqy4!yhoF6c%aTHb@|0RRbY-<}=T4h(ge9}&7)Q7)MOtxXOlx;um!r6{ zJd;-f3-baLd}>oQx3y6k zWO#KUUdv&+Lv+^JsgN+HN1E4Hn(y7SE-ljLW^*6iSMP7tC}i+LTx32nt*Khfrz7G* z0U8FP!vLL}=<5OCZoQEh?tZpiFUZ6C%OvS#rC}yrwn~hxEhSc!mbk}PF69z%$&3>4!mxRiO>;X*-1MO|i-t)Ns2ncu8`{L(qS>UO9}`W}?ll~bXIQ>3Yi zX1c4rX1uG>*Ap;Bne+D@V;4HT4Rf+x{LLNQx0gvD-AK8&m-zggIBW^(Ylb*=Ni_^` zx`sPFNjz&SXMM1kve)K!XDH++Cqdso3;FL~ru_V6=IT&leTDbYhUCMi zrn`rf_wO^FT@>yw;%vG^W-d^aC#400MF;$1m8hx%qkTa;<5BnLQcv4M_7$E#oIrm* zioes1TWHOw7Xg_Z7`ws-rr^h?QhR4+b4%pVILy@-J81xr1$9vxd0-*}(1$7A_PzOnXP(^fsK=j^w8+sjJU>K$`y^p7P z#-8Ffc5EkcNxYlp6mODEvWeql_q&_@iR|xr&R7E2<`G!1Q;CvV=d_HS@j zFBC4GDm$_5Dy~hb>LfesC_+uVrB$k{V<=rnvj<@;kH{$ieUsYRlNFk(3{xXfV^3%t zfkXviM>-+FO{u(t>;Ef@ya`KGK$ZoMb-) zpkXmG4y~rNpv`S{tMYov+$Gt9M!lg*nIpm=D)MDct(ce=Mx(@)D1}M}J%`3Pu8UI0 z+Z?wi$ACT`d1EYl*h_9GLrl1e6 z%D#C|_nYq-zIaRgug_)gUgd2KBrkOf{NfK52+77}SaM~kumE-%oRg85iGk>4G#ng~ z0*#0X09-1}>yuj=af2r{+gF+#K6Y_4N#ltd*`nWjxAek&O-o?w69v=p*2GQDhqnHq7=EKnd+}R@k{H5Z@ z59Lp`>8mZUvwe&;XPP_;G^oJIA+l2u?aqsTw5PkdE^4yI?5`KxdQf-wsb^w3bLB+# zWEa=2MOHa@VWEJ@ht)ObYqf}lx#F>5?NGO_)~PTmY0cI776WZ~ux@R6u)Cqe4~dzX z5&myBv@7acxn(u*#`c73ca^VRnIGTF8)yzMl!0<1sItZ7DuBfWUp`|TJIU=?OZQGe zn@_|J?P97&qgjPOWDAhHfXNr?>? z)W;L!fHm*VK<^Jg*d4&36~m-OAUgC0h20W zd0sg)&GR;-W{ZytBSa*CD8FV*0V%{VA}gFK3>4;v3vxno)zDA~aFmnEE67p-xV)>n zdOGL!wW6JE`O%?nXEA4>j(l>|ur*R_l_SUN^E(}+Cl`!2cNMKxY-16{D2nN-p|1=| z2kWt`!;Hz+l<7A3jh&piZpvhbbakp}X0+L2Wr!3}y#uQ0Nwb&%HWuycr(FVVsHK*8 zVy|NTa%113aeUq|Hl`PF<0AtBJ|+6NcnFWwQf<|W+2YI$8I?>yz>e#~WTdCZ##9s< z92%9sH(KrGEB%A`cnCc?ez>Z|D0)v3IP*CHuXbOZaP3{J-wXj4~?os)?8oP_tG_G}Y3(LFZTSx5^i#C@h zYi`Rlw4C3wq$=yqE^gp1Znw^l)sJ_$8te1Rn zzKT1&%|E@RI5C<%-kH@?o47uaetr*g?^?#oTZ|hk@cqt!>!YxZmiX)VVd{{mX*;S0(q3w09283tfz>%lSXNXWt%8ogK$- z%v0C8Vs;zCZ?>hb>4TSb5qCQUhn-TVBFycCx@zL))`Y8@IXpI4U67nQC2sGCDRY9R z`Vfa6c&#B#WwAr88Drh}PA}^Gmht?eMTi8$W!%T-9ZO@}*hrAgj&1QNnj3Sqc|;m1 z#!|?d9JF@&#Pef?McPzU6yRb(dl9xdH}vih`^VdoKU~s186rCfV7B&n0kzV=Z?NQ? zKQT}&U?c~Da;>(@>GGP*1NAky_(&y>oXaDabFx{fu?l*eJul|M9OLCBb(tz?Z>DhA zmpR$Zn;loRb;?UC=w^GG+ZkP51_t|LcQ)wNwy=Yfg;O&!yFKptz1r)024`WQJR3k` zK<}WUt~I@9JY#qsRo4$1CZZ}wVtThYn|IBp9~WPETDG)jK7X@eWJ%mQk~y`a^!2fJ z@3w5e(cHJD7+ex;U(jzJ=uRHGmJcchcWkyUw!4E~+r_Qvmn@%c=$|dvz0i2}PS^O7 zVR=)3aA04VH#-XPej8W7g;mJG(G2(Us9x93&l?m`%y3w;Cmsj2fe1xvRTQ zuj9aA{1jwX0@UsARzw_)f}s+VxwuqHQfyjWu>V$M1fZq*K_>alJA-S}hQwf?6(YH~ z<0NH09$y}t9UlzzD&p#bp~($CxX1YNP3p6Y)Tu5}iI!qf<7_$tG6s;6!xKV4Vj#dq zf;2xkXd=$67UuIp5D+ljNq_InhJ#J%<ec&eeclevtb0JCt9& zr~c$lg`S1?C+`33#G?aWRqXetR_9}!&NC_d9K+-M|R zo#mbyJda^8AWapQW)!WivvMRWhi<7;45*Ks{9IgV<& zx0%|}N$={(HXG4h4Z4}0vaRXbbDRBb_2rPD07PU26NfF*<|yRMkTAdzAnKY03+rlM zYwY$m`Nbo}XYbqZoG(!D0R{sInSieh736`T>CBlm%ESu3eLQh!8CB69VyX_;x*#fR znBEyu)d?M*B{@st=7w^%R&tx$aI;&s+K${b957l)v#0D819WXQO6kPWaQFy?WyyuGneR>Z_zJbC2a4a&tA#%J5u}K8<+=zNJ%`8RMmlCJD1r3(O@fz}yFT2B% zUa3nO^fAj!sC)r*s7*W4p6_umlc0c@20^1BNLX|b0FcliL~INR4UY>sKBNeXI=01` z*x(X1!H|blh*6?!3=$rgf(#7{0yGlP zI#Hui!WXC9D`OReSTInp?(32DjY_>8f|4?FwO82En%B{)HtEQm>>S zrAQ^HcbN0JG+7o&#b#k+qWo@EO3$)sRc3_(1BtFJEZrKJfgET1i>uHV=1^(=xUY8? z@i;_(%+-4G>gxop4fON7j-^ReZ+o_{i8nY}u()2mdC)YwS?2Q5EVX#wkZ|L?b85p_ z*(~TAwc6}Lzs>R+JC8}UJn50Jk<{eqEKCAlK-QXtzMj&ax$3HcVso9+P$H@H7?lO= zOm3pPT2$L2?O3&xPpkV+SGfoB-A%d0<*c%L`qW18!QEz88xu(ZYdb9)bB!Zi=6erD zAG|*~bIv|?$~?NLmgInzCiu*3=H`KHXp zyy(U$+5H2?vvaI#3xu;LnCmCv(f zz|z}H%;AHcmgGnGOv8PNoz2jJ)`V6!=&uiRm4cS$_^Qf~t~SK(vV5+SH|!Rko2t0B zYZGyR%aypcSvWeLBNT=vKtU!xiHMCUQnPw|CSR3YDNfSo#QCN1kO#Nco!OiZrnKNU zN7iR^1;SWRoJ|%HaIIz5YLj-n!6Qt^pu5|D|y6p zzq;~!*0Xbhx6f)B@!;~jrOSpMYh)~p>1%43^<9eIaplkuzM&Shw}hRT%ouG?8E8$r zc%{6l1=ZjHZ(U^F+-CAfpt+O!`iqSRF9%lkbRCn#>b@j*U!1!iHg~8v^T7Rs-|l?% z?`MAS`NH)ZReJ{ouRrSj(dW}Y{n_f{4>~VCYk2tSvAABlx>Gl`WSLmd9$cs!8824n;<$KNO`)KG4Th>P*Vk>m zwIaVANk<(g-?!^^R*l{t-VzE;LZ+}#sO;3#%%r50@Gt^AF*6-~oFy?72(xg03M`Pv zwb#Dcq<3t63@lj)62X?t1{NW?kpWmf? z{Zug0fps~POH5%6HOTHpN`EtVv|TyUNPqXT^5g5=mzT1C_Ll9@vH*HOg}>-R5B6X0 zD1UH;x!VpNZjxqNqPOR%dwasG_q4BnRPpq^yrZ*8&u?QtdISIL2=n|p_xed?9wBVF zT)96g`tphKS1W^h53WT#*oFM;h2i3?=)z2kDwBc^1lbwE zT}_NjN7lt8kj_isgf^C;@Ox8ncz1LCjlV9t$$N;T+lf%Pu*TrK$O zuK3@cv%dRC{Of0u`&(Jd1MKo5OtpnHX$bN5rXFX;)p^!&@!)uGg(k^Kb5I9_^buZOD9fc)6MCE+zOU zCXE#8w6jlaST+xAvom=k1Kjo&?AA`s@~*6Nf+xmrT)9%R@ z(`bDj8w+B?05mL+l$wEzjw{KPNf~%VTv&J*2nYeANP#ZE=z`w6=bD+OReN9~ldOd`W_vd*TL9b@2%9yv$ee64Mvo5i`h7H&7#tj0 z&)%$@K2^ASllW%z~?d}le+nw#iU#e0fi-4%Gd9O_cQOJ&Js8N9Da+)*#6va!|DWFnKVp(j;hdlvOi|JTC{qC; zG6KZeaY_b6nhIQ!7$g9$ytH~Fd8yrClVMckxO#Q=prf#_%>3c)l}86f_jffjUjF8Q zqrXm_%Z;fhN$zi>wt6!L``A($sPkslc|=oVwL@JM*d!q3!o)0SbEU$iXI1O6>x)Fu z!9XgF(wT5(8{O*S3WNx;Cx zhEEU6HM)oj53#vhdh(3-%r&2HkXPzS(b;11i=quSWN}%huR}LGQ(t9O5Ml#!spwKQ zlTU>c5Yg0(WTQ2=vc1UGkW*00shKi4do_AHzrMxf^r#pzL{p#E>ElV9SkIENZn;S8 zA~_p1rDf8lPF>%a@xhA|>!+)$eVn7Ct~(bdM!JlP>(*Ou_syQRES#}Vtrl1LSjFY( zEyKe8g&b>LY8VtWxASHfr8}q8D<>qSI&f|KIQz`*DD+?gaeEQ9OpAj7b{BGV;g{$HvJz{k*%;K-#;&g5 z$3g%;*>9ofeN83qV%~VarLQe#cfq(e%sVwopLB&?Z9#pqR&a5!xF{P34+KWJJWt5; zI&Bt3jtGxurocO@oN5tUtH?$n!;;_uSu|KGCPJqWDL8DEkkjPMTk00v-7;BtL38zz z9vg1Lmp$IC>g(0|x|P-{Qbi@Kt~$D=GNjHOJl29azoB~gpl*6ReSaqKFJEeZ@loj; zHwvoT)33eg|NhTU?Om%qeZRJML)yJA>|WtlcH-3bm=g!C*==i6CwcQ^{?=yhtsBmp zmn|DB**k~g#eKnp4?AwX=ziz>>lbgdmp7yq*kcUNWJ)%Gr-9Nkj;=6^K#T|u0(2U* zyi(FTY+?w3$_OWAMT7I{gR8=ju(9C})!Kb{GQB&${fi;RJQAbLu= zhQ;Thv3^)bLm-j=EG>tCM#e+}TuLM=0#qBRkB@w3*Nx{koG0gPOLN6U!ttS+`Z}@Q zN^EJ8oj=n*K4hKf){eFFTdLtV_L$$i$NSw|X}^6!IX@a+X#>7y^u#E3cQtco2DjRu zOiKcf_j!N&q4=*KQ-AYHR-O&c)Pdh#$@$S9_M02z-`-(<{}BJ z=%X)fH}7S9_%iL){pcr`BfkGE=e!r!_c{r0t( zx6cOu@*echk6=GPANtW|(5wBFM>~}59>i=d_|+5c7uR9meZ+l!DzVQ7+N=>p>i9x6 zW_G0X+{u;>uTm|;QZo|BDG8QbZjp%YP>51O00kaLLBla{C?X0tWz?B!&B3^BzDs#} z$<s*&;wZG6;CYcy2+um$%ZPj(Q>m4qdP#IR$NREgEWo59s zR+zOe$lVt1XpJfBz|EaD&YsDw=tb(QqrCmpjg{hatCa^Q%D2WVD=bPnF+DB}>Zb;u zy{OC}>GIm!eWgCHYH7ZryCt`-JbiJRHa>)y97}F$hZcFlWmb^y4w*ld`}W)Q)$Zu1 z7(izMt0Q7UA>p8J6nFJD|G|Cg^&{BP*|-bmlJDG=9$iz{x2Kd=L^U)e z&d=cv_tS4)%D8?m{f%qn+ehhVwh|84;VaY8C)ZIYw~v!4o;~EA+NL*nL3stRxq;FI z!RYwWLK3Jl zVLg^~ArTPbfrx~nB`2aGz*RtRbW6na6u*{qn)xzriXRQ3A;$`ZgoPktkGJO?=lJ+% ziwFSFP>=vQzT)TCuz=$h83b5aDZV;=RT;mu059i8pyPqffG&_HsKim`QVd@mCbuWB z3&KQ&;o0h7nlgx22<7QxvJFs1ei%D9SSAaob7YjOQS}BADF#$(8Oqb+_dK>rJqH420cfM28;FS2SGoKf2byJJVDTU?a?%Be|9&KHNWoL7) zv+;N{8;6*vmyr5>W?E`sX+9;NpNdKVxbzrt4n=LwE^jXAo~dy)=^OfNma06u7@KS4 z$yAKO0->0lUTw~=wP;ZA;NwHAK_Dmqq$c~H9+)&@IXkn;P}p1T#v_Bw8p=d>$>M0) z)D$j1`?C&=|dEE2-$XbKZO7NyprV=JDIT7cQ0d4kXl9 zg1H{pW;bTL8MRRxe!f5ITpu{q3V!*B`0e}Y?;Zy2P6R)^qFtTSME-O86*TCWQ#oYN zQ4PE8w_GdqjS14QFa6|HeqsQ)yjfFSm`+Lnb!KdJF)j)MIQ*~@OT4EJr7HlvU4+d^ z?od6#QV6Qc!+YD&TT9X_`=;)?#LWf#&KCaCRYBimO5-S@W1hcv*ZJO$r+)gI{r5f` z-Pq09J5)FNQWvHrbG^*DUiS97@x-)hs!y^sqG_q(*V;MNc3!CtrxQh1>7u9I(C71< zA1+GYozYKw^?tU11OhglkdhLMOHHgSGOAeg-Wq3TjTI6CNW}QGj0gfbhCoT6WFqB4 znyo;cokfsPF+CMI!!>df57}7AoNmbNsp56iDe{%*VkcFmLPSD9nKQGiBeT9dzEloo z+Q={NR-9WSe))v(o9}UNTw)fN0aq7(=U&Ta-(1+g)6#H4!LdXZHD`E7`28pJR~`+Y ze>|{qR5^1>)i=Xv>PnA^1!KeMTbo%MyJ<^1$&<^d#ZBh+DZ`n=;!D?6hnFQ|(^N+V zlEww{JalD+!fBDlM*toz)laTMNotjsm!pPpIjMXqxkxH9^C-j!Fw>BG_JqgpygjAb zm;himsQjNEXE6l8B2pG{#3&FXz-J($VxYm{kbpoxR*(?kLINZ$80-!eU)^wZSChwE zx`W{V<>b|Oy>nOZ45bx`u|!-BK3F$ZH|zkW;b->+%^_D0%gS7q(C zKz$x4Fo3b%#Kr#5pS&sf=DBdY8T`k);y=He`iGZkA77+CUCw%I41L-GUQEJ1+e!cN z*|e{3X1%kW{EHU_KmLLDoe#?g#zM}WM||{>^y(q(>3#T@A8|i>n>XDER$Gv_7qj0w zjXxLx`+eZm>GYc?($5XY{OMiZJNw{h6x^7PS#GAJ_zij>xUeZ%lw+-=Y5dy|h0+A%6Xk@yR9fhX=TSd(8an zJLtcEMEd*=d$kjeive)~KqrG21U^t8w)TwilKFttHS}1IN*Tvn*l%R#hfT z9SCzHb!^W%xUH-jBdA;A%0@D|>d^L~{HAVsEE*7mfF=#FIAMx>kev(Wwj`_T8EdOa z7cLS{og%lkC2nkLC#F<(8^Wvw%4|^Y0;^L=H_l|7-%Q+|4?D9FeR&^tw2N4uiQZdF zKDh|nI+5saCl`9q|6v4GR4X*@N8W)PZudRMe*`MMP-Vw zis04}^h~rEpCrV_sWRb{4aN1goZ?)1G62;%lACkG#f+r!Du#{*$~6gPhBO%)6(0q- zd{k|#%GMz0UbL=X?yu{%Y;G^!^}`i8QO-#zR%Ru&1%ZT~Tc^8Ko|( zQmf3$&O(&t31+%$IvNy%T{-Iu*0~YGSf8$|MXuE($mHP)MUeO3615*(kL$iOB0JJvr0nWzL#TCy=$)aEG}>L^&6aqjK( zwshFKCTlCZ%$w)?HZFE{uR9tijJhJ4)l5D9$+nv}=g(alTHdLfU9GC}%2fsU-m%i&$r6W` zUf#kc3&7ZF(ax2cQ&(%=`f%dXy(&v3XzqZnPqLbg0aZNk(RSvapNn2@2Jg3k-@JkO ze;?u>tb(1fa9>%#+MLQ$&gRmSU@<_5A@a%9!ruw>NSNv?;{}6kl~twTsbMmUd%R{^qh|-3GtZr8&`673L4!%v5Y@bZS~e zMtXEM8Cxjit_-$m#93)6;rV$CXIZ|%Bos=rGVn>>$|6^Bo|utg(~Dcn4Bn!gcmO(F zy2YL{rzR7R1PXC_HVX`CvX!}sFn51(+7%?A!c+G=<1E#*rx0s2*+oc_3a$F8Yz(^8}b+u2GOqosAOzV zAgJ-k>l^a(6})a=V~f*KtydU1nC+peMvFWa0EarKufEc4Qv0=0N_=>H5YW)LrP+#n z8co5V9gFFpNND(R#(!IG=CoH{CIsuP^afMB&xRo;084(nS(n(>%I_P>H9OP9x>I0-p75cT?i{M)w~KYNJ%@DO=vC?qow43~gkJs|!3 zcEaCYXMS}vlMMllD)9P(=-W@!fBBUC)!W!xmk=vku{FJ5{(x}hwsvPndGBuPqc=uh zzF4?&y?1ifnh*~b2NS+}CHu*HoPYgDv@#7D>j6J{k@lZoW&GnS%x_-AJUR(poR9zT z7Vg3fkR^fFJIP;PPy6#@($8{3h>7VsXxCR_0tE0yIX>>CgzEuqS5YRuS06n zvT69lus{G$0DLYiUqQ4QL>8IASD(ArvUc8Gb=9T5c;5(ko0# zl_*n6M-*!^trn`q!l^7XQ}F3bO1j?&g$IH>X?%Yd%k7L_SyhftvKt%X9TfqFN>Jbl zn%WT#Eb|d8psh`j`{(JLV0^p4(?hXUMK!l%G*od6Ii!exI;*H40FMFHE`?t@@2%Eu zov<-UpjaO=){mW^NIKkt-@cr7|8~~+e2lgRs2V`aN=nNDvTXuW*PHAeC7bJ#Mpu=C zqnzpPw2cAcfH!q~m~W~~$y*@I_l7OpieT(fm;iM^|$nrWfTl!PUN6AO;T zRace}Os*H+deFGEr5EaKBm0%u{BTHO%R*b2XUr{&Szkj_5Px51Bfy9wYtV_SE=V!wCz5eMuT0401-XszhVqn+dCiFl z1C0nOtXu&L9vuvdv?4A8&0-*n3OSjCNHGIZl$T{t5RNl(OEOe43Xhf+9R^|}0W{(` z^(z_~la7YRMg*iHAvkn&T5_~M6e2@`|5iA701X4B!XtPo5I!cT!kAvykYR8`q@_XX zs`$ccq{l)+Sc9MFgV~D=y8q>*^0EpG9!1eRkt*)-rguEccR;T*@rs~ zT}`U!a1b5>65~L9wYa5Ln-B(+JhYG+Ioz4k->xVtk`<{$YDp#o4^4>&>F}U@AuSV& zLc~Qdh}gtHkk6)T#5_44r^`z+8GF5q~@R_gUt%XP)iFv$K<@1AAFOH7)`ubWM_qJx) z%oGA4N|{I0=_xcuxYmfBUbbzY_f)phx~61X7t4F56}v~3ox?n%Ews5WadSs7JDgp> z0xvf3|NbEJk57~T_5%6Mjj%tx5&hk(_@6yOymg7`E&^gMm>9JT4;2;~;vGei>vOE5 zQ(T7$aI=oXCL$O_2Z7aL#?4cbv9|cr8xpr2*4-^=>7kZ4q%^maT#nd^lJE;FinC+f zK6~^~6SlLRWGPRsYRYP?BX_kjPaRq-n$sG`n8x~~t((ph=L;(OFo%!b4?gU?{&xNT z4de3yqBPp;PA$~Mm>ntnTu56-;rhw;mHpa{1IOx$ysIuv zf(2e3e{FIgJQ$F0(G+4RmmMk2j+NvZYjlI5vEyw$w6e9K=dHIVuz+C4f>ZwyvJ)ZKM=iAq}m_;pw#Q@zj#qq)fr_ z>Q@Rph)>HXFELpRYItn8A7ov2oq$IT0U$m&z@f=2(<_i6z_0A^@lm)KNSmwFD5T)x zKy&~=LO?tS(r`p(sU#WzXecBW5+5EE85ayvL%`8w-o1s)!$H_;7o|`Lx+}3m&9v&W z43`tv(asnem*j^vT{87`(U#|o=l8uA&oo~++qAN5b6XwSz?4?EU(|0oe>%RuTG(bEczq`J^K)@;F9dykBlUmZC;$Bs?2nhDetiV{%_H$_ z17x&CIMHVr>eP>RnCdLD5;-+5JB39Iiwgso1OSTwd?MB)$o6QJ^;*7`k(A4Wl@?JO zn-$~3cDG811i&tUU0H3&6JU!B+4+35R-ER?H?@Mr;*fz~sfPt8Cq^a395ZW~gT&_o zZ(TxPH%cZ2RtKSoW(h6zB044n`9;aqjiTP6e6N?EFUQ!;5|c)3 zPze@hn??p~PAjXbLfF$+5*!MG!+_0+x0EEB3_(ragpTHfo*qP7A6#DpoI}vglWCQc z(27As)d)Vf3fj3Uzx1qeWJU}P2AjjG9tSfi5Qy_)cm=Tq9;(_yRd{L6$sF%IUs)Y1 zuSAsf3+2Xyw%**$lXVVPW?lhAZ;2}`MHe`TY#p-5%Q<XC=5_V=G_HZ$2x&utLfz{FI zy?NCB66(wb>ikX$NJ<2hw7>#R+;kc3{dL2chKvS2M2!vglTe|AT%}5L<;Lc4fHXT$ zr;a!2;5u1!sWz!ZA5rZ>da4N4lJs&XkIzE*@s7hxFDwwKb&TR-vAtN_(^%BwEiB9< z$3-5OM23U_SS(1255Qp}$r-W8!~kgI@rz&oN2v&aOaRCvKqrTYIZ(TaC1c}{_i@1D zbZl}W0JX)8$q8jeJzDRAI$H@UYhrm5qo#x1F)HpK=j)9@%6w4n#w^YhOPNsrKc^xQ z$dF)I08nHm4|$7vn@jvaOiPJzX$93*mLEryl86cm$!5U&!;Z&5<>k_>wrs1Fu(@Iw z=n<4UMO7Z_)Npray~|gj)`+n5)L1`7dhNPeOFk7F%fzAlGCVFM3>p#&2@3G5fgo^P zCpj$qxL}i3C$PJ;>BKl+cUjM5gQ`fv5|NHG?)$$M1hRQ)id>}MgHDW?+v?@zMa(Rh7u7W8X*@Q z8wL$L&exNb3eOXAWIU!{R7M5>Sa6`4!;TF+&X`|n#P#(mYU_9cSsYs!7YPCQ)Cie? z%wUtKtSoh|N-mIAyPexR^A@*4Wn!3%Wo$OGs$NiC$t=@kHJOz*rPOUO_*~_6j*`vA z&XIu%KNI`gU`MmVtO*MMS~auWE%!F$x+~cYZPM-$-O8zoz01`tL+qS_h?ZWSpYZy| zXp5Wd`2}jdJE^}m@kejue)BZ*+n31io<;rFL+J0WhW_SZ^sir0&dejcjxeS0m|3;j zz}y@#K7=|rNxgi?zI92xzN9NLqs@gWb>*qEy~w+V>~|h$Z(mk5G^AI0@NRE%O*^Kx z8LQEP$pQLOFJrnE*Y8NFHpeb66;!sc+Q(FjD|SyUqq32ybtUWTFd}=D&=Pe1&6=Yp zu7!PBT@SjpH`U&p*gj6VaohU#8x;f9$ZCCXgA3N#MA54u6b4W^;apvm*~MxfFqOCQ zb4z0hOi<=V4>ap-+U&Wp&SY3nHYb6@4pGRXRdQH|Td!h}@UY|cBxgZxW2G%LFd#HI z#9nG1>1~$r37Q;=t&k^XpmY+3Kjv{sQG}%E)R<5aCB3{*-QQR<)7!;LPtQc8sD;d^ zFrblv>1OzgJ^Jq-azDF}G3o)nx{ct9Ern13lH+1S|5+~`9R`vCSZ+-H?56DMe9DPVvXTjWj^q=A za$g-QG6F2lX@`bIaabTWAxP3#ZfxBO)n4LPXPHy=Nt=_hjZtA* zFt|Mf{`xB8FV9oHdx8AZlbFB11N-}j=+CbO|Lv2k|NK7hU)~@+UW2bzf;Sf#?;i?Z zo)z3)=R7*A8tOsyb%%cZlJ?~*>?d~mR`q~JOH@2}WHQx+O_ApfuP&tD0T_H8S89lSPwpzZjAX8_g z_Y64DDIiBil1Z~lie&?Xj#H;QrsrIhb;6(+KoddLmULGGqufiaZOrQGW@|LSToT+n zMmE)fo=w8g8LH<5x@LkPEsJbl&s(})Id#fZ+nfan0&9Ja6SXC2AwVjL)7$VhtvMN@ zuq1gP$pW?Xl1o|%IB{?s4iuMVR@bqMic;we(A~$Ztj8s$frJc@%!z~&fYRjuVJ;y% zlAniSG2@ftfH99)VNPBigk4@u`s9x2a5|~o2{uNOPEJJZFF?*LM{P|7pPGx=KLIhfMom(6S(bz!%c8~Ou>K1mBOS)!;`tInS(QGEik1m7 ztvXS;N$S4|lbRSDc)Yfkm=KVLjX@^|L&AmzA-H%5E+#^oO=_!AbT&)c+XO0o0*w>w>rhvCC2V1+*Gsv1wPtoyIN2v3 zZYva15rq<_f9m=rOh#OgpJKbKbA8n!wLICBPoa||NQ?kZPDF_-&F;YY0ou>#WI{-h zk>1;-ld!)FZ~*xucEL@_dMr0Mh*`J`l8(#&7heKR7Ml9LBLzj!UgA zwDU)5p>@U(JufAnoyAU%*NR9A7M@Q@qo$_h@_DK7L_=-&(98U=tf^jg}(HK#SC) zLmILIJcZoaqC6%B(N^VbZ?F_-nW5pwNezp%Ih}QGP8Pb+WAb|PU9M~<54Nh4cp(~53?nc{cVryzq9%qVEnb2m>sT7B3ZfnA)CkF-sLIzyOqr`>-CJ9N#MDd7z*@Vp|LWPtB z9zHIQi;oEaBxGWVl%E*~2CBl}KcatqTl6m%>6iLKzIRFS^paJ`0)`^Ey`C|=YR@h} z@e6UA*GBj6PYs_eH+ad&EWo4yzcSNlaH~6=)$Qt}G!PUBQWAm{?3Ah!exZ^;r^NV2 zO<$rgITUmu($}XSTd|HV7tWuwoW0SyvRi)p(a6OsU7497Tvh^u8j}$1AN{<>iW;ll z$R!`o1^<-6K!R=3w6op(?K=EG0U;BBTm7tG-za{%n00@izuPXKu27!rs3J!PLIZ%D zf#o0p27q!I*z8Hax=cAaif{K&lj8v?1sGMy7IV78iZ3oewYG9?HE3xegp~_t2x63C za4?+y$$Zo=HX`o!g3qqXrn@i~H^tw6V*PM0?)C`y(<9_RUc&$LY2<%BgZ#gj&~M*^ z{?{jI|M4#2t6PM-i`dua@~=-&FHNzwXSsdt8H2q{j|ZbF1bw5(<%Nj*7f>%RX58P( zI6afK+=+8=!K?#Z?ghWRh5zjv!yD`mp2@lz0tVVRlY<58t0ikI>W&s9S<-kxC zsHqHfb>K8^Ook}hSd~kcWrQbz$OND&pjS1TKseybQhP_O^CxS&Cbi}5?BetV7?=Mp&2?`ANu|!#lzjq z?Gebqe8Q>O_?3a!#eUe*aN2A?c4?F}(Ss}2fadb3l|j_m`Sj!ZTykQpjR(sLIR_TCO(AC2+LvzumnLOevpI}sNl!5NP#?h5RVlS2L*XbB8!n0A0N)) zkvSqVfdMZpm6OTn2tSF20#*h*1s~5|ai6wH9uTjmabjX5d0&;s7KD zL_~n_V6Zk_++9n{Wd`7r{ANzvXf-g=i8<7CX(mdV8Ch*1_O*+<+k_HP6gxXqppKRp zV^|VE;~X1YTX6~u3g#yrCEAq6Qtays?t9C&wz85APiuwAEF`Cus<~O{NFod>BxMxm zi#P-%51*XFrl6rwNnxQ`X{nfmI4PUy2h9v5JT^2qAu$}!;6YnFfi!1CEWH9q1vJ4ZevT*-*$r2BgUI1Sp?8c$PA zZF5capto_r?j85(%J$UWZOUy`rO zVY3fbSGww}a@d59T1~GmOvex8qihXU`)?T^23d&+TpX8of=WNcB&hD`8-ja1~8POtX_qFAGM){_vnqSW2gIej&`lCHw_N@nEB`| zkWyRb?GCqeHa5hnJbiVJg|PhDCD~6;+kSdB?8AVUrzpQYUv#Sv{O+FPjiWU}02&ne zGfiH63h>DBTM^+0C#ybrqW)le?p8NybCBKNDn+J&hFVFWvYzKHw#yVS4WVSoLeexVhtbz$b>dEJ5Z+o$};Px>>^AlzQ$s>XThQzutwhj(e0 zj<@QD_q)J}065V=Iud3C zIbgLGe0G=l`{!}LeG&iXx7dICu;`l)l-CYr?VkeA-Yl6N#*FpK&z)$8Fm-RYWZ!i5 zj~;3N^q%tD$DDsYqWt3l_MgvF|MDE|A1^Wg`#Hk@Jf{5MEcnSC{+Dl*{`qamOGm(G z4@v+0k?gM@ivReC^_yFHzq&|&cZqN#k#(_~d|^U*ecR{K!rc}QlbQjI$I4_v#9~0> zg316a3J6xC!}XZPDxpJ7cPSZGZra5u-3#0Hgb@r@5<^DBauel`x2=D?%ztw+>*GVX ze?64^?P1aXzGZyvDD8*0YhJ$5nP^uNxuAK#=?L+>P0Z0HL)Rd8eN(%*#50xxUloWX zaOEMKJ&+Y_&4>>mL%j%RBM8K^t#$CkoGUsLEb$h1&qd=Sfx(43cSzz07Fw#f?gl|! zk0LZAZ5$O<575inF}Bt$YYgd4pyDI6o^k5H6sdDGYx4~M;xqQpDB97Q9UYTQZB#eC@l z*c@WrK4Q9ivh?U8b906`+m9ScV4}h7GBZ#W1EUgXOMpfWYCPGA+U)k4jN}A@PDCaE zAg5+pC3Ip2pyP6VPD?%(CS<{@DhfOvvZ;(HH6Xcspps#1Mk+iN=rst7CD&?2cJ~`v zVx{g%xzQo@H9ErGmE}Pr8k;FEpNA~7i)22#=ij|W=9yzUO}^tj~E z0oy66G3W|sVbOH>Y0ctGF#xkywkCHOcB67B0E3r%Y4h5su#q5sq=cc5As&um_CDO}n z8{)O~Gy4XaQ!|BgvxSE?P5Yqg}Jfp7>Jq4Fwj@yGSFcR0DgS4^_Ne$-`=AB>4y62CnVoK zS^UKl^gle{{`eZ4uBs%>AtwCunLmXYZV&q`q+?D zSNiXYu$<(ft=L+JyEPCPi-sgrEF}F2$Q&dLM#f^H$BaSbpx|&c0uFu5NlV=~InmWv zFC=2~GtpIOqM-)BEHX}C1+T}_m3j?~0j3@TMVB(S=g zYP(wkY6+!OOI64aRW;n5!_612b#5H=CA!7y+m(HTg$K6XlcTDRC@0!T8txIaH8V$I zw8pigTDe7SZfHMLne zxgaeQ5J(VC8{nA$0Rz=O_~FA9IXJ*70+s;W-Hhm%=d>?SO6x$w0Ag~#Xl`A$zGYcm zEsHiw8!JT+S8y2%P)JZ8&8z7|dD?UA38cCfcv{k@rbt)M%1$0-z5j#OS3epHPjFk8 ziz5r-vU*%f78snc#|BG#HUsv4iLr|xoYR^ciCR~dQVsXG*$~@BV3J!wd|vtX`cztS zrH5>C5zRWv)=X~@g4tX`vWbe@u{Ar<-xO-~Rg5QMd#lT1?Q!VU7ik&q>=^4vOmxQv zTLNAby?>*6W3{e6B!OJ6Z`wD#R6akZc>0iWJ%Wz<2sR5goKT&*I^7Z} zHK^z+0ky$n?2lEfOhlF^BP}82P_MGLn;nngHa86GtJaX86brJdTzT9A&=NPa_1pWV zJO}pTi|b*xrv!^m$7A3Ir5F!OPCQmA@Rw(t>!GZ^67bU_+K=|}9<+mB9i#u}QOsu- z700`z2iiTYCf-_CP1q)31Mu#)@~bQQ_t%OJRfCfqx#y>fLspc}N;K%ynC1R(S64GQSja#`0fqr zw@)Fyy_fstHS`Y;!TxxU|J7^amoE^n9Of*HQb&8)!+nK4y~5@F_PuN2{$>5}KF`9T zic?ozC(g<5-7-IWqr9~W6?7tkE@V$*&d(oJ{Pq>?mR|-2qF@;L0+1dOe|u5?!8UJ33;L8`q?*!PiCdb`2fQSiI2)FdJkwLB z5=nT%EC9n~!dVo!*_d5khR;j^bWDmxfN8TJe|OjQ_vf^qJt2K@lNmRFy)NXR?iBv- z1ODfiIIpZ`{P!K&Kc1p~^Cb1-sdC%r{v8;g*~&y z9piabO~4TVo*=Tc5+l+BlN)pl5?j0Ru^#l93({yOD6)bgHzAz{!h=v64A%Fk8-``x z1l`s|)KsHXwKzu`%Rj`c8ZD@w;55$_)=vt2Jrr+KW?eLGbRIpvggkY<D|6yZS`w5}I=cPG&dmCn6+`>V zAOCpcoeu{O??^`bXm%6OYJji^)C5UJ189p=J+AcJT#%m+vJfB}2}pS$7Y(=^Kp_GM zlKA-mBwBnmb3DR}BP?mwvG%Xz%N=g0ze_%j2 zJRlnEWA%5^f>qgFZ9IPki9rKmF#>@E2tB_~aBCDmnKnqo7HU*i8kUVMB!#-yF&t z8WIM=ISp<2_MZH%ZtBE1|JY&s#S4MM2VK{X$G6A)R({s``N+|J_gtfKCLUcJnbxXg zE}Kru&I^0(RW>sOM;Zy!rstVT=#@5^Ra4BsL&yRdkVeZ*uAJ#4B02$;)PJdf#Xu`c z*!cX+K#Q|$CaQ84WxxT8j%cj0AksmdkGVANh3t3BxwV?Y34h7qgz({d$xqJd-aA11 z$xhx!8}Ki0(f|BB@6WF(zIs*h!^?#Kd&l(I4bczJkbn0i;m4Q3moJk4^}g?Uzqo7gwID9@b2lYL;VgaZw)hzXQT9isio|16boGf@oN-Cs~GE-6z zur$bQ372zVex^GdqGxCEP{^=JKT_=(s-flg5B0M&(pO{q4O_vOGixwyK zRc^S`05nCQtOR^;)$rAG(*J#d_4Q5q-4WDkZMKaM<~um|kEmX`Tzdbs<>m?JsZI5* z^W|1ESX@zT?Ybuxt&uKGC{{}6!ti9!8ZBH|wXN*82b&4q6NRH|%0L2D-IY7E$?sjE z_+wyVowl&Wn_dwut(vFCEkPHp#V1}IYZw`Hw)HZCiOiaIgu6A<6i%_#gKz}Qj9}&l zu{-+;KmBy&gP*N#T=359IZxf~sthwBsag>!42+jb!Yr~KX6xcdXEos-vCWxRUMVz{ z2~3{CNW$)R7hgPkw4<$|Sb-G?(tDab$Cu|Ea*bA~2v{A1v6!(~giQk-!J7VXaG=RI z+tqkra$u#WBT?b?>s4p~q8{UTgS)3f?-u6^P{~fncmPaX@Y;pypI+C#cZ7DZ4m`aq zTZ{7gs+dYDnC}waJ?FZ0*45j@s&V0`N3}Pv_Pz4#_Nk-8`?tH>x~&jNFQkIAGu&r( z^(R+t5a~=gI8S-Jp#!z0l;gHVP8{=$r-5zd=))Q=ZQccRF3D>&7Rcy_}W zw&u(Y+SisVhDMaTCu)0#MGK4i=_%dnO6}TesJ5EzwWKT!;h)^e|HD&7HwM9<9!tM| zOZnG#)!%)j|L#M@*Uypu@eJxOcXQv}06#c{*$RL=)0q!;ayLdnUuSMOLQv|##B%A@ zg-H8C>E4~T#p6}0XJRLBCT>4n`Qr2CPhRyM+pyPFP3TU^k-= zZr9GvINPGe8owHf>J$JdIC37E0CDRAT8`U69vgL{5kM)V#oQ%tpNV~TPW0t@^j~k& zzkNz_V;u46HU0m+WccDL``tax&586M9?Aa8ZPM2_u>bc+_^($r6J8*}f<%3$qZ~v> z1mU4VcMH`NM3+Ud(b2rh0DC}$zm}9h1W^?wanwMl1)(UeZ;Tym#Wu&_@4o7J?=|o1 zZ*^b3*P$uHL-JJRCUVuFs-0|YNO!m9*jq8mx*T0I(VQSVI>;^aMNLxzXOz&nAn%!BejxV#C`*PZbS^fQbwH!~yrb!EEGre78cormSlw6*U&ol|>RZ2;P zw1|&ILNZ-pB-?zYci1v69Pa5ICH56Jnz7sxn#*f=w- z*Qt?3f?StF8g8+!?{q$R{lL4QY@a%1?QYL$@1)hYvZaP>xfRDM1~dCA&Ry&68#Tc( zfXD9)$P zT0u+5Le2vY7h!(RaqxhDexY<`Mzyk_=}E9k)xf4hl`8RKE|QaaGk%;+Zj9tzXV$zEU87eN0 zLrCpwtf;qZZ4zFS$2QX)>u;@r6nT4#=Ez}xcMsjD0~!U;DnL~Q&T7eqX8=AmW2~jN zx7^lYNwyvtsVsZq;K_aCOAHdhVNmJ??EY}Ar9@;>3#uzM@?uGV%~4G#oNG zACpVRVW2lUDThtTfu4XxFAi3lLk(`3k`FR~&L(Y)RzVj+qyd*vA?2V}Cj93P^{Bad z5`4}^-1q+JzE96p{P?6{H-SIep10SUbt;ki+KGavcF9kje{ZFV4EKHbpnEyzs+cmxviISG|gNWw|zq}&W( zP!x{$ch&kUgj{yAv??PVo|>Elrs6P6G7*yr8}00z>h9wZC<+=wf=piy**tXc^m^dT zu<-6t=dshl{d-ljlk%A%c~^&$LIW}l%xuF?P51)kMGhS=-eMbF@~j^7t!_E@Z&{~i zi&ys<_pMa~tLbAyiZTaitj->2%WtaAs`sXw<>2C$?yEPwKf8wg`T^<7d$el<;N`t! zf2*N*-o2x$cW-&GZRu|BS|4BYzHp^%Xn?xBrA>^|4_>Ld@^IwD?fyu&tZ!VibG%~n zh-GMod-bW%wP)%VcS|PM744JU`hNVrJ=LkRiYpg2+gtqQ6#-<=?1FatK>61GK)h8o zGGtj=R=0OyW|#O)9SCy+u!KQ*I4xM8JJ=$bX;B=R&^&#y?(UPV`%l=n&zEnU^oBa= z`Wkd`ZN9XLC#c3Lg1A7Jps`oh*seCXXpUf^yPCbD2&&%HdWL+N=mhb5|7TPU~+N+D5QYiusl(&91VF#>wJwyMXOyNRC9#cz{w-o z3Wxz2+atl3PyhsJZ(THwx*wWTW-53*|T2n9d3aVTf1{0*hL0Nh6>}+GGR+{sR?#u2Y3nl{IMTJX+gBt`a>}8G zl22|ne|T8@`AOb4=eU18#s1AU{mohWW(By~mv?K<9FQ~c$&(rYMCD(c(|)mMdVjL; zgd6O$f_XEz+DSg$Q!pA4X=LfmO}y@Y`PjT+d8_Q`(OQTS)z=gcbXz(i?5Pgio0p}3 ze$)2ryOOi*;5Rqe|9YtY&1K#vdxYQI!2jP%%zxb>{^C&P(OPh%1)Lv69q0nDUQ>Sh zLG-;J%w2z`du&;@bleqLHZ$$8!Tp}KlTEQHTmQV~sV7~}+_aoOP}Nk)9qcg9Pge%3 zxbs8$(NNCi3DPev6#u`6*8hIi@%k?7N7uA>_mMt((fP$Ib-iU^f0X#rhT!!hlAZtv z7{C+zxSu_&Jv^OvWWFHb1&eLOp}O3OhSayO@W1__^v^%A|KYLz*LOt!d71iOx5+=h zM7o&(?<@j$HNb=}{?BagPs~1Rrc*}Brp3&w^gB}_$6Sx%U zc7JiBI)A+0(9>cv>hYlvcWbLY+#qAnQ)pBa5|If7IF*R=dmSVK;Bdgqj5`|!By6mf zfjZKz`|_IoPbc%fK8E_!Y4T^6ihgs?`sqd8MjbfXmv>{4`_d}ri%a?6-lP5VInI+) z;K54DZ=O|u@w)ewCtWKmqLD>ycu?wXp#(eV{+8^@aC-X~HQYns>4Cce)!LCiy;Kqz z=7igFukVRJeya3?8znz~9JzkA)8HUFDhZtfMco7RjsfWFSaTgrTMJj$BfNd|z!0l` zlpE}27`?z+kuo?b9-d(>?9V%MMLjtqBIH3Lm>h{Gu~94GiJ1A2vsIeSA+HyLRVo6Z zU{dT-elaE2Z&Za`E)EfwlMU=%k-3_tub^fj0U4JAxv@dvz`wHLU)HF(xO;ita|08)s-2ARt0Po>6I;1i=QAZO`BM;p1;+z zZ?~bjTVW35ctZG!YLbWx$Y~&EKrh(|r>m&fW3=;4C{V0mK$;~DvM~cdYzG<@1~G^5 zeP=-w46x~-P>`xDA-G)JnkrE|st<*f3I)b&rD+T#xtfS20d*;%IcjoQi?GnZtJn?| z!>G(hWF~he^|m{LwY+_W8^#w`PO#?6l2-K)>Xuu|;N~Hy{8q>bX;C#O~ z>en{e3R|_fXc732E#Y(1Xh{Ybu1HB}z}u&_cXx%+09cvMUz?;J8zr3R&NvzcH%GuP z&vF0up!grpGJby(`|S(T-#sn+=mPtvH;peJbB2v5ObVc&F@(&_5;l{G$e?BcAqy`P zvygBQs;P{(G*-AB8Q)J7=fW~si#I*lpZuwMT?{Z83WJy&s_VT^v zGf&h^t`(iU<~?=8w{@ayY*|^?jt)k^jVs1m=e29I^pQSFb0e{*r)Y9Y(;8!&Em;m5 zyuK=ZeW_@Alu_;n);eHm0Pe=Dsvxza%FwJ89EfYSIwb?qf{Pa$hG#_0z2uoqO^F9^ zJh_@^p(2PEm8F@31bP-QLr-@tJ$=Scxa_Qm$=y{fDWc_v3$EW9AMM`=W zkxO9Hkz^W@hybZ7t_Y&o5V~ADnfU1O+Odl0YLS2lifEAXfXGmqRDgkguH240bilc_-^8GT^XFPN_S>s#$mwZ- zfCV&iDg>D_VR~y&Fx)QgZ)MK*6|PNN>Kl~2A~Y22LJ@3kzB(2&WuyRXPBs>aWaSad zZ0h#b+RWs7Jeq=m!~uayAW#Sx0xna)$7RC7>_Y8{^L_Jc{+?k|uu0V1D)&`z5b2=1 zR(yS-{;I_6d_INy*x@6HT~z*LRIuU2p|`9%b(i3DDq5q!GFx>%Xf zo)4xhh_l_Y*SB3qJ9yKbnwqMDwsuiRf05aPaQM(;6WaP(rlHQJlUgfA|D=O@9LCh*)ec)SjNcP;DdbLhw8;K4NEL@)O65Mi_dJbOX;@J`vY zFLcjtSP~-zvqxR=&2m#*8c3AH`YrB|cyif&>#FnSN$dWR3WtK$*`gmCa(HZ{S~ua) zsIuDv)+)feTiUIftT`Xp?MA$R%dwb9*%~I!#Yx9TxgTBBJvuCzOz@)B`EfV+_^J4C z3$f7!E-q>BteY-O6rUR*ef!w*^~=(?&*hyR0SD{B5R}hYmJRjn*~!jW#ij0eM{~@xuko zr^{KN@8aIv#Jzh;y4{+wR0msZLKkO&-6;I^E$-`ExVH~se{o&0@xepx)+o^zg!#hRmL#&KR`*j(jqqX@@W=3VEx5)Ax~-e+ z3xd%Z{MMn8%}pPJ4!U~S6%{!->7deT&qrfv7#xd0s&RYd91f(Hlx%vPJ-KU(ip;Rc z0o;55yq-ar4vZ>pmR*tJ}kq~flvdiHSnq!wzZSq z-@%@3VO?w&ztUdxTvhH5M;QMxOagh-G>L*-q`)I_Kv;~{=;?ZOK9`v);G}5PD3>?c z2RR&-*EUF$>Ri9SsLWl&purptVUY~Y6~q1YGK;Ig;^Y^K@*wL~e7w)Z?~dA98`Z;o z&WS-srJL+BBdx0RVm{CrGY0xqehYjwrYy`)!)7I`lah2e6-Y&C&2@#%^_;F)QD=nP z6)k9su$5B4W`d@$IMJ?g*z&vME{9UU{~lAB$Q&FiC&SHK!{amhXp7EFqx>Wab-;trLLKZ%1Lf_!Nb5nt9^B}rYj`5btZah*(T2h zZ*E(DeM#~0G3qlLwB;u7Xb1J{=QVHKFxR@k@`@_bO7%I@8p|?gItqMB5LAIXD~vB6 zXnu2}c*+8fHi8=i;K%254|l5Lj%2=O5($~f(`|8PAxqA{Kn4?X)2R6DWETzqYAd|$ z%`F~_6`2ZAsWc{m3ZZF#tamWcpZWdsJT+9aFQPiKUOzHw93NBcUr`;HE4*~bus$i6 z9plzF!{dFVv2nh!6cFiv#!izK;-|;z7q+XrrWDmRSf3}`W&rbJ#yjU0TKxtH*QIn2 zt0wifkgFY_+6}uopce76J8hc|o;VO82J-V_H0b#1Q zA*-sI6f_H_YFu+Q1}OE5(V*5&Id`_X>-)ucO8{n%6hs#tHQfb{hFnt(#^l4!?ej10 zuXR-yH1}v4dPNNh(e+EqOG7bY3Ydr~zKv-KBFR1S3ECx#gXOyIQTIBYZ&MAAta6k>LA!`1g|B|a;uJ)A#2 zBpn{NRyGv1bQlX|nW>q;WaU7)I5~Ae!eeu?tfdM?QF4Pa^er3$P9vb{6b$q`Y$k?5 z&(0&KiA1@HcI)bT)6#O?sZ*V&PDFazKf;FJTb+cJF= zFzBR3jC?){uVP57Tw#lf458HLCsfxXxM@R5Pz>6glqZ(TE-bjBHT-acFy6*#O5}Hs zYeR8id#|LYTizX!FZYQ*f3e~Z5ADzHNcXkn%|__wXT>MF!H1_|U%gEF=2gtM4|D$V z6!t$4!*8~MOT(z+WBCx_ol9ijJS4bufWNy`Vlsdf1aO9Fn|CITJ=hFIT|{!){7RqA z#u!Zq4^LUAqZKwWlL$+0v^7dI!d~2sUC&mxz)A#trOdDxaimNA=%n-drZ(ZH2F%22 zH+o}6y)(`kYr*ui6e$H6d;;jIlZ?d`3vI$1(~66|wA20hcUOxa9+E%1CAz!6=)@56 zr#Ga(yq*8`>v{itm;USPgkM~u{pM-qomJF@>73_}73~gYUf#!g;Y!(wO=)`z812bD zxosJViRo}qsVtssX^z;<p9LgMO2bV9JZrrVX;$HmOhf~LoN4vU;+dD}^Bg{~k;PAl9s!@I8 zn#P9W-ahNj*~sDn$B`ZP{%Q3@8z){%f%1PSES8$m$5uUUy?T!qH#ERcv=cn_X=UN` zx^85!Ggo8*h6>Q!foo1=C3@jwGnBby;n1+SwMi`%A!@2=jiF>KT!&Vlo0h>Qk_+if zDTf!WsllbCso1PWw_QNQ3i2SHnVe!VC`AH3R%t-H1H60&vVaMtIDm)4F==V&bkH6) z)Oz`2?Ny18t;|eoY7)hJiwCFly`B1=c4Hb0xP3fPF}lpfI&rY-z&?Fb7q&8*?Fhns zVWPbnYw#c?M!=Q-krn5Wjk48A!(69yYqX3*Otvu0 zP6K`~Hyq-0b_gO(WQ`Kk_;Q*Tb(T*o&)d4ts@|1J^A%gkWp z>az>_Jz$#PlN_mvwMqiUlXW->oiwmq0R@kfW3YATC)LZ3j83$L)&Ym5rT9|N+ z4+!_RE$0t<`|4OO5llr&S5t_Ze5$pOSxm~Ah%}#GhKOK{pM!D;3b!W4LxFM_3=|g9 zb8}Ll>?7eZczHOjgjZ@5@dP-dp^#36YfADOYqVvh0x=z-7v)!)irhw;!%U2|%KQ2i z3+p8(&bdyX2~3R2b75etL%Tj!nuAk`d>TGE?$dpV1EfZu6 zwGAwe&7sn={3i8mi*c>nJlJC$pYonQ9XWN-ab~XI#wyPt1k>%P(RNsGENyLu)8ES> zbAh6irc*KoI(&nZp7PrKfDhl%!ihKJhkf}+mOGz3cSuaiCPS_qWQHrUg0A!i2e`PH z_tmTBKfS>G@;Sj?Zx%lV%!i1}LF|SfymPSPbdNv4Mz@KxUfW;slPl%N#wq>%JaYv> zZpCRl6oL?R&6r{Xx?r=orLDx`K(}>C$|_LR^~7*1qklwp?ox1iiuS}6%fvvAz67i< z>W4a%a!zIuGgB%^X{==p4~Ux^QRj|W?J8hZ0-GVTu89z6MpxEjssfZM9lhNuIo==m z=*IfxwYWu;77ep+-EP14WShS}*BBrw+_{wzA{1kq@{AH|y2FjxJrwQgu}3>it=-xq zCwngJO+&0s%VI2d4TNkya%wIq1*K#Ny;^kJm-04!Y=mZv>L_lZ5(*c``@i|T8T$q4GQp>np7C~LYwOYi=5~f_n(5MU8 zY&eY$Oa`jQLEBjL-?=p06;_rCz)%%qe-Qtuhw||l?QuW({T1EuW@)_$(PkC6L<|lx zuiYOhlgWB2WKT?~?#&2Z+F$x`x5`!onkx#*B*3qOmz#+vk9Y1K^#>y)V+ER90;ZQe zlan??Di~>Izk5ml;bnD25oq%u4vxvrFY-QrTK?S+m4AId|8F0V{_-;BZ_kszzRh@X zK6Ts!PETZCU#C8?&Azh5c2WxH2 zI@l=c^lO!LWTUrmqSM;jsOYH9zjeqmScPacaU4vNk(jcXC_T_G?GMr>;uYPIawGzD zcNXm|mv%Hzl|{+T>)GjmNdS@06yge%MID*ri52#g>E{S3V@&7sd;*h{P6|) zcORAf&+7%xj)AXk7ku}j`R+2<9|fLsx_;Rbh-86vUKmW`vLrTn@O~4PRQZ4UA}Q4q`R}6bZ?Y27pW&?)5eV zr21l%S)SWg$+$M7{Pe2-2WO2Zdb6H6sru!+oxlCz_~Tptd)q~iPB?su6k0k6D8Z>| z`5R{fM~0}YBgnfq?04_C96S=(IN)E~sEI|?V*}=`b<6&B6ORKL8hE_}+E_wWRW7#} z*}-tp>`G;GSZp!i1suT4hb#fUYWCbt&>dzmia<+TG}zDX?Ii?T)62u4cak(R&sN)k zw=N^t0&DBdiFc>uH%OfXhl2^w4nGPMsmS=!V7?vpED<)?jN~&MvS@ zr93Q7ClEmI^%99$Rl=fR<&yl2OyKnJHClce02*oWzt=J#Vrthh%dHHDno{SLhXeYl z@!-Dg`jJ^fYb)2~L|Z*%wUx}5Vhm>5NWXezRy8=nZiwg9MG1i>f~gW=_oF73^!DGJ%dDsqjTVpp~Vu0$z04WAi*;LH4ll-1x6#+fL#lMFrC+QyUxdCZ}a{^OAUi z5R2o3HHcspcDS!(ZJ~T*zr$s0O-+6-YP2-cdxnGX0?0z;%a%;lLvk8--^BSU|=B1s`H@-`$T(3J?&jw zy$x&hlAXa^UleDn$#ME|$c$u}PitLiO{JzpLOpS0aiqPu#$p_(57ruuN;Zd!B}2&S zRhn?ANextARjgK^t$Ja7l^vD>0_DZKn=PgiIAjSH0KE`wPN{kuBxnHGgmieatPG%0 z$vvjIIRJwI{T<%*`3Q7T3K4LbU?55wP2@BMG6oam-F3mIAM`(cwJBc5@mdR#0u%;P z(m*B*(8<{%HWi6>AjY33Fh&h<-)T~rs z(-%2PiwtUxm``HSFmx(XD8L!?JQf4ZVG|fkltNA?6H=3Xya7nmDpoo)muJi`@3)@~ z;jUKaKAR{wR!`_N!zTPiG7Kn0r*LwSMMQSBrGiT$8O5}qncNj1Psf?-5AJ-KFg7XA}=_J7z{H z$1k;Io@*o>|jZ}o(_o(Lup>L!wQA`P#AlE+tBY$ zWdh(51D6PFMADvJXP#)!_}Nv@awYiag!L~EoUcr#KOF@x_JI#~aJQzwqz^oMvgFj3 zWNw_^-;>*(z#255Bf`3{quHINO}AoK`lYQ^0tOY>${AcS5V1jT1k)Ks#3S&-$BfIH zn)wa$!iKYFNFECFVaY?lP%=KMp;9>%FN28Mbd>+X3Gc;W!Vk{t-??Uc`n>6lmtrr! zlsI?JdGBt`%TL$8aMgEsvS=oZiRr*ZB{((C{K1pHAHCw*UcmLm`3-d{967mA$mJsV z>{NRx>ex=Wv93^4f|{KQ74gs<3R)xOLVV6)7gUz13@TnBAMbV*+HC}>5_C6Z;$d47$|4oe8i6-}b64k;SaTI-LZCqc zMNqAuytQoZZ)3Uaz~j#@^`vTSptFti^m*^29r@kS+&d%iS7wMWb>i>WX5B1LxnKpa zv=HBJqk)aK!eOPBX(p(1DJ75}1w%Ux4>0vg1zSYlg)QC#UMLN5P%0wb^k_9hl zB*JQi_yOchDKrWpD+|OD{>A0y`5D*RgyhbS?(vT5tzG$*Mau16>Gf^x!BP6&6#l{t z?!f}%-W<0^4tneJYCZT+9lfKA)f&eJ>(je?^9BZ4H8qHiHc`B_*ks7HSjj{Ri1)j~ zJ;p$*RAt89usgrDChfiD0L5^tKR@C_3`WqK zQ|$gGcuPgbOgHV>%lZT3nC)>{b2%O2ze)+guA&ac%pGB2a}B*MB1LC_bL+YfZU&C^ z;jfL6F2upR`*|-cs808r4O~pJ?@}sg2zctsonjV2%tq#+fmKU%8u1PzYPe5R<%jeK z(5tf{%UkLlUC}y7ye?0TE>8AMjYe1Z^{=mV7m?DBj5K!HsrEeZ2?+Awe$GP)}!5d#PudI7l=UgT^oQ4K69)y~jaG05u32MEv z-fB&MC3nb=oovCkg^=MI1SBZ?ItrWWi9KCneJLgr4Fn~iE>5qHvApHj?t1ZfbFu|) zxxU0>(C)4*6Jf9%09sr&n~1OFb9qD(F)f3I&I)?fO?8Ul5yQ;9XK=9AW|hJ+Kn@aY zZ-smwY-d=0?O4avz3NN5o+FE{wpu?h3bWet9WF*^hhcrSzAdIxDv;^v zK&{AFn<=?^vf|L1b7oL>YD@pbai_UB*|NLB%Fav!i~>+s+Pn{m7d^9l?KoNasUVE{PaxG4qQh*6Vc9$LJSEH(R1&k~J;b@Ih zZ!9cUunS=TNQ$V{6{1Lkz*m`*=qPAS2*$?Dm(TW$bd+-QL4}*%)h!vC)ekOt+s0fC z5nX*qUnnG?;7P9@jEV+Z#e>b5&)%^9>P6NUcggSXf;$u7m$&kMaa(Yn&5L<|dyoFv>zvz{gi~Y4%g2RR&Z=5ENd5%J9xd+NXqZ3Nv3aI@dAF{rkXIuV6 zFKep;94!Zry3=0kf`2fMemn<%X)Wj80`8e3(o@@#=0*^10%PO+QVooR0DX<9^BdyZ zN3;hg1p8(THGUkI4-Re{-+I3J)%$_Pal+yZecwLW&Jow8Tip*HE#JOBa&RYj^l(He zg-bbzYDeL0zk6j+_1^vP>*o#cpHcqkw(Hhej_i_1g$&rD ztLvOygMp4gO?equY0dcMRm+bK30@dSy?3nmXP0dcXE^tVX_trcUOcE;34+}i=9vS= z#a6mY1(%9abeasOGrQcIy}j+ebFa;00mH+VokQ&dLmsz}$B|@Ot9gkTNBg|3byOGY zw-goOp&UVgr4^F$AfD3Vsc7*y4o(k+9mZl(Zk1k9CNFf#`P%ACQX{hNep-hiK?8v1)Y!pCR+rkIl_ATlDK){@G^P=?TLtNBuAEGrqKLI+0*q zoi4q)860ZYM%xWEc8Wp92$!26DOOU5uC$V!T9{jt?og)nhNa8HfxWdMOd2pMVOy(# z)kz;I1w1-L{PQEl3!AI~C%7`LxwB;3SC8Kg;m#)trtLT<3zhI#c)6fV%T@^R6)uTI zN8>O7i2}4H3|9a`4aFv7b~se9IAaf#6TNnl!;~{TAUv|C-#W}%T%ig%z-dNS_{r6c z?7>M_-s{S!+b681%1~e<#ZxRC3?9AktT9x9cTifoE+X}KZ zOt3s{tPA4cFtC5c-ybi57zvvT3gsyk0VWkv3|3^5pSlv2Butcqx3sSzv^+2by^?dh z(jvR8&@AQ)iDYbgicU(Z^%qV~l%KuO(>*HPIThME+L4k4YMYAVJ<_cm$G~90z3c6} zOO@IJAfteVF6l&qy*{ZL?-4|s@*)vRLleHX5!V`H_4Z4{VP0uzHjw~^2UX+!vi%F? zWutV42UIK66$q#0>O8gqi^V*Zz`;U{a-m3WV2aSLC+UUG54XRzW-HX0PwH78}8v$3{>bag6N?O_Y(C2o^!cA~aEp()78 z?yL+C#ah!&A`Jw>%DQ@uJIs`YN?|fU)Puwrt9}yGK39BnLv?=3wzX?o-LN)x5;_Jb zy@TY=o|3M99C` z5qWAVKB$#{L#t&yNqhFl4y#a?$P)5 zd)m@ohgquNi885nDS;5T==+likZrS`UC z_tq_|lO`G#Opd_!cL{Sdh_OLfZxgt8fhKQB_?bsPy9? zVHb}T%rCI_kA%A?jGa@)>?Uz?nLfRuzW-=oZN09xMs~8FxqdqIJC{R z>}PIl#7}0dFHR!2CY4JG^j@Fn`dY>5YHWQja=qdYKL7wifJsC_RCLfiHC|I;5_Ciz zA`0M>fkO$BKD1v0`jeurnzH@53C&H#)AQ8JH|2dJ7^?}^(PGCDL1x7D&PRu@ytWvqrVozVMi*S2V@8dw zFp`v{>dj;_usQh229H=m&&dTeI?-d(#hq#i2GrV=1_5$*to-~+-SUd}`n66fE}Ie` zsblUO*G&$2(=l;RlAEyP9e3z1_Xa1LYKj5ao0{#3*Jf*xjY<)zNXa3ib3nOPw6)Q^ zvzPQ#lrHZEx6Ud$C(7DJjEfhNn-}8`-srgXAb9nvv#$+@&&{^dtu>>zhvYXl%!f0I zXE!68bK;31^7uHdr}L4zFgnuZvq|XC+JZ*4TPuSuZD$sCqG<3 z{`#Tj?;e%?xA$fL@TU0M6wD(A`(xlAzYKl$M6x~zE^nbG`@#G)?%At>2j`ll(SnCJ zWY3=lELISSFv|6)#nI4opX2svZ+pFIX(6zuzhxkRObZ8 z8>;0AjYFFQ!B8nYyHg4gNihu6RLjRkO{>e^lg$pBykLJxyFXR>$#u~mKMuaL!@oL1 znVKTrc(wA?_iHwegclCDH}BbRyi&WeVZ3+l{?RAtpZ%cy>0{^NzG!=cckj0Q;<0gL zm_9Zk9iFge2F!^RXKvbkaVxnnVjt>|kM&p~zA~uDW5e#NmwO-F*}QdQbAP|Pf5^4G zQQz8WHrq-{>A;g_+uCT-DugZpuO7x!WpdKvMFtlrC;n>#g5^*z)bW z6OS%V%lU*n03wyu0wzx>;RpTYYMC%Q&$}>(gwIpR3N-3GmmRB=2fgWqfDjREwM=Zw0o*osAoOIbFgiNrp>AZ6Y3-L#H5N z&`p50jO{UU`kM`nVScv#JsS=^Dz8IECBf%dU^4^}-ZUg=4f;Vm%K7CE`{tfL~v)D-mVKrhUV;o3P4wg+V2~4c2C>lXWW9XVh z@#F~mWRmq{$5LAkvzLL00XdkIY|mBKhPaScnCVFbO$vx&AnH@whrpwcw_dRHz)3%C(bW=Ml-atHE+EKadX4-PbOYH zOwZO~zjtnUzRoxpq$Z3Y;{`u`5lttu zqd4EHV5GvDc!=MSDjS`2TRo+f5uQw45RY@hRTRBC4?>WkHpc7_J=0UPx-D8MhFSv48 zb8scKalyT?ELdCQ%=S@Uzg%^BUc1mt-0WuT_VJSzaB0DM|Il`0UpGBKUYV5+_Hz5W zg|SF!s+KS_EZLgXtxT#LqvVjAC!pp-Yv^q>jdtl*rtLdBl}(+rKr+8$l-}G`8jB&j zQc}MfPX?ePWcM4&ivLgE9~#CYW|_6O(RpdB`RRFUSBy?V02V%%MTGfnyyl3cwU*zU z;zlFn<`!YO3nfy)q(-dNh~vqN%iTPAS&5d1TxoS)ZBsp;Hec=+AC1XQR;|soSgQ&c zWdI8qUM_$af|3HjDu%lyVj&u~Fj6-@8?punfkx5detc{~JJc=OnDrd2#8)P&s=b_W zNLG*!m;{tbncXxl6A~0Mg3rOItt58DN&QWf%@NM|IR3|1760{h%Rk=WeYsci?frs( z{6hQP=Q3&u80;o&uS=74!088_efae?byG6;@lD51K5KsGk>mYWy?^m>!`(}+g(=%e zum0Y7!^uwNm6Oy*-SbCUaEJ~U?u?m{8aeo zhupIycym!-Z^>(S=DjgV`EnEcqb2Z@&AeaTDEsy<^ZQ4FUtHIIwl7+11S{jYD~rhe zE#k%maic@{(+3@2KPdm(Tl}vtv44BY`Te=zZ?B8Ly2k$KvUqobht37f5t&0p(Fh16 zBq-NQq7OwRv9m>s6pAQ zuzW30IpA7n5nm3XQD#F(Qijj^OIu8})yKR&gZX5S{NZBBv%dVt-8pYe7T)W~yI=!H zrjla=Zbyz;FGJ07;n{RtF&k%A^I7DqOfD_Ka@cf%kPU<76(L|`0t%u8oswqJb2zjT zA(w~kH#F)+Vbk8ad0-?GZPN9=mTj( zuv4$9pp;jO?G-X1muQjmM_Qtn)+P>S#&F*hn9~aKNI5wWvl0N9?QETFOYw<_=BA43 zsP*)0%jZ?Ct<>*bh|kR$hKI|7mB?f*<;j!Y@iAL*ZnpLSdSpZOO`Xw38VnoM8Nz16 zK&sv(5kY|koq;r~=?z|4SX$!1fN3{*z=Y|t(YdQfkzgw z@$g_a9~eO`&N0gQ#RWjjC+Fp4dl}|u`!GO}JGgXO8H*Gg?pCbM`JLwc0gW|s%Eejr z_!#&6T6ASq8*qWg*UXP^|>|Km6En2E8z~}(=bl-ZgVjFAWOt-QYGQ94#k_V5%8=Lyk zG49X^wITo^9Y{CwRu(HK2Rs%bdZ5lS)2rHFGVV;t22;eT7I`Kramz7R7gJY7E2uGi+bbt)$p!!^wsi;otrlQgu*J3AHiwbaE4<<$ej>?r2=OkdMbP^JPG)J2QX_JEctuE18D*xrr|Pql;J5ZdPsbCgd_aH$ zbWuS%Totz&tYS)yU)9qQ^j63eYGQY9=*smey$vJP=eerU^?j7}%l4X1OlDHBvSVv$ z!)$K|o<9!U+PBQ5vG+IS*XQ|1{j7{Tr@@$WZPxkZg6G9~;LYn*$1A$ENkw0p+nVAv zMycy#iVq$)L%*yG7WB1iv#lEd=xVS>{IszVnXbqtah7S#tU)y#tI z2IfT4W)_p7kYBtLWSwdj50MrUJ{oPo{36apSOzsg2#tfq%6i)*8p zSanHvx1=J%l$kJU7ahmV5o-x@RW`Rdsv#aV7#1ohtxm+fqw2#o!~U{jeUNf*uY7C5 zP-REskRargw1q3m1w0w9xT)F_49Oe2oT;?ArNh+RB#ed#2Wz#PvoRG9G{;Ofoj}0E zLvgA}DfBtzwKdjq6Q7L9diqSaezsS!GblLCAb)fs{n<9{2TPb2Qw4wc(Eh)ESNZ#2 z2AAfzN;OEf5UNtBrU5}_Qk6^+54T+(K5x9VQ+8ulvXFtTj~0LMdhEO3n*ZZJJ^sZ{ zcK+grb3gcU@!qS+!yA^D--$nYZhY~s<oMz7_e+OVj7)xG&Dh=Of_R685_*1^@Z3@@^+$ zx-vJS1Z8k=yRYas*LYuF!TrZe^{1z_g?g~rTX=I-aBoxe{F?4yi?F|qJ=w+`&te4t z?9_n2y-NAlTe!bJB>vM0?;nqOKfl2G;dRx^%jO%q6&7hOHW#?Gl7{LE=y6#!a!z)y zGmnlh&C3HU1`3`F97@!c*^1X@6@Psq_UHDI6>h`=Cenx>f718X>&*}EH|_05>pO%iXVn{*gXgy+uRm?Rc`N$<$7APD8khI| zS1vVlH?Rkqsj~y(&K5Rgbwm9IsSr?0v*T1TMZj&Rw6)2{$Mrqkf-BczyH{L`r}o-0 z3D2J|uPK(t3sk8bPj~Umu_cf$W!ZotM&ng+q_vXhpkwa1y=yvxVF0Z~Y_n?xBD&jc z@OxA;5#DK0Xcb~4JdaKzLX6v9YyIF-%au;t(^q8X$d78NMUO5~yvh+KtyaxMfV zoD!{HY>i0_Rm^0@w7e68BIxTE+#@aYCU@RxM&7CcqCzknm&UZDN@2;6pL?8f+Zn(o zDYlAGS}w{YrjSd3$H|GrHQt~?BqQL6Fft7uNmwVBTbB1aGPBi&N@ggn4|f_Iu`+{4 zq%(-sWkLu7E-p`=E{sJ?8U`#!M#4*pIC^2ePbDwIV;g+Vn|rHs1FaC&=;hqb#@c{Q z3nAUoXlyWTNQF2M<@gzh9ZEVMA0=?^E+{@6DEKtX4Lr#SKle>gqC;x-i|v8=O|O3`^^W^tup>V=NV!2vi|p34zmDG}O=B zS`**B<+yZ3*E?L&JIU!BGeZ8k+rs(DY1L28d~eU|cVogfGrL^Q=xB)f)r#73qkxiE z9h8P@RQ90Iu$Vix&Ny7wbv@&f4l0|v9h-& zcPdqUA%nd-#<<+iA958wnyq*+7ks*%e7qgLGOxX}WxR7>@2w;GG}!JM<^H7laE5=n zSa!Z`ytwUZPi5=kYXf?n64%jf?HxC@jbtaur-xzIqEp+S z^w`UUz^g>-?j&?m`#Ug~kG=1|9J+L=`tIYVPrf(t-gmkmzg=^D+qH4Y z@aX--$sK#o0QU4qe`!xT+EyY!f!VZqveh`zWqakuY;(-4Bcbok541UoU%ebVIgbu5 z>M~Q(zER%N40pN*Hq!zgZsz^{C+c@Mz>*PseWZSWoOY!imEwYS6}Uf)_|r?nfB!`P zUp~=)a!%WwDRxwW&UtC)D66%zXneHz%0Bz%F7fs%=IIvu{!reFLGTZkDZjcPdbJgK z9?iScO`V8=sWd)j&v&R0RvCp_0^^a1^inLo2(FU~vwNf%B!^l`BcUk7?4bBEBzV3m z`sp$Eduv6Xts{PQjrzYnk^k}a(!YKH`}c24zH@~A{FM3GW!mR=lwZDA|NW2qzw_;p z`}dpoc2ifc54`$v{_Rf}p1;-e#_O#Q?=^02crM;-JiXO9Jyxk;fyp-6cq_jlR65zG z+g+|oC$&Nf#->mh#3Dl(rz8hpV4yuASzL70kMejyph%(gZFE&VNmEbMHK0A6xb`{z z%(1Pqo9^h~RgBn8&DwCEC)nx)MW6tchan@$Y-~OTAPK2?CBREP&GCap<>M9K;5TOLd8%8Tlz*|k0YIFPgyvYikl8PUwZ%h~&Po}7U ze60E9k?hNL{?GS>KRXov^rG_1UDNX}^{S$1nhll&utgE72a*LU9oA^1nGGT?gOvZx z^h6pN$LEu|Y%GscEa1R}9GHlcBjn@=I4~Zkh)GA6V6uU(+QsjkPt5JLp52~kY?U=8 zNKqe5$_3lgo_05GHqKvdl`OUE#3k87BS=Sb5kOFyBc&mskD8B{vCybIpps$Qy1YU; zN@rmzG&C#$RD@L1OD)55HQnQt3w!CY^}0}tT4XFGG7EFQ=^b&ly16+rs32i$ECz^E zAvjCAAg~ZXKTSL~gvi8v&{FNGG^+eMja#LVkg#f6iK7f%sV^}L0UidlVt7Mb*q4#_ zcJr2|OtXESkw*W)#PC{gdtbeyGg)5z%@lhiq(X{;kcma-0m#(_WLPN_+85=P0kux%>efFZ~ zv(IJ^E>u){kR44LqXJDMWK$~>jfCL|&eXJMbwzpO+%-AC98HTihMgB?ed8VKx+;1! zz$nZIRx=!ux&7T-haFaK2IJkNi4Ho{J=kNk z>GH-hx~*CFM5m_0j1T)+(Kx-ePuMpjcSd2hYJ@LAuB>Ao-RfGn5UGe#e3dkZg{tHc zxM(D+FmGk3eSW5XY9`j&s1H?8-HxKB2G0D9XKKvLV*(rk;IdhfRh1qdJP!oQasBnu zav|`DvvUh(s&PNxGyVG=)1U51|K(ZbLA%Jq0i7<6FbA}o;GK3fWEjjMU=x?!fK;-E09E@Enn_{<`jkXvAOP<*vzA_G!o!tU(%8ckH8pdb|H!HU4- zqVDpYz|xMOqaWVgi@9=Qe|Xc~76UVF*xi2mRyX?IlBCZGZjG9*4_YUK3_Tk3`l$;| zj3)=at4rpP280dN(UkfAUg+VG<7`pc902W6%w$I2S*K5@Tm^Yqlj|Ar^iH{rUSeH` zx_3-DKVLrFY84{DaDaV29pNxcQ1ldz7Og@mZuw`^yHl{?v zYJ3{4us|yTi=+0bjo;pJpGQ9a zV&Sb%r#}1n+B=_jkF9aqW*F%?&iF;^wHHlicdBmR4ZZ!eW^-0B-XT~RFtx;KSI=6$ z|HEs4{a4Sv_4?th)z+>GB1XErRXNxp9PQBfP1z9+QYKMAqfoFY z0xBPompx&H9v1{DXcQ1hNF^|EdqMKIub2PjE&fk06Tdtu{rWcfzrUdU{wnzAS91RM zC&Kp@z~=|VKYytGi(AZ-3GBTS``WyuI+%w<05%V($`Lw8{?Lfx=GFR0kRefXb;meNnyxVDrEs7HT&+0K)#U3c54lOv$nEa+SRCUZZ9*WP>SXfaSdEs zNAk4Eo2I?VF>}qJB(rX)9cH$!Xh+V%?VHs#6W&O_*BDZXAxp{!L_RLi=xLp(8(d6J z&bPLAR*9r|mIxC{*v41dhi2mGPEUVVq%&c!)EB$uxqK{8u?tKJJcR&aVe8VuU^JX{ zief}Q#H@KBw;(qUvOONBR4K>zWb8e?l4v}Cc}p_A!f)xpH)m+cHiEVsSiCTIh)AFV zuTP*aqd?eWSE$1}#!qfny|+lZ-A#JXFF0*NUhBXdCSWTrFs}ypYXsYxyhR~c5Wzd3 z|DZ8b#4@bO$cu#?cB_m*L2+3`8V$#w;&?2aOoUa-FiJ@=gcnqD4z?8J!2p$A>W&)f z`zkgr53Qf{hLg&k0dFn>$kYXHH{GnwHo9{Oo z?Pc7W&_fEI6 zMw^+~?4)R5Ghzi?Aml-e2m%(-V14vtZsg*8$Juu6!FIJyRV3!akp)>BC1bM_bvRUD z(jq97Y#l1ZwK6e-LqiLgcp9-Z8;IbG%eYhsjFcii30+(O0GSM$S`2MHp5Bpg?`W06 zfOh$5?Om$f3*Mt6|LCBq+D8;KfQXTkOlmU2<&(>%*-h2Tf%?I-$lZsbgB`=QL+{)7 z(t8Wm_BfSK0&aa#GK9Ty7#Pk_qp_Tcbxr>~fB&NVcwcg|Z)&TjL7;|(WpzeNZRKQt zYj;aUYok0+f$Hy54-IHzRm9`H#KOG0Dnc?i;A%TuQJ!m$5?co??g(dKwz{Fm%+Uae zsc>*2RF|~;pSBz5?7TU@$0ELdY4QH`txE?p4{mJEk2l1tl*x$3s3KT&bULM=Zy?z- z6rnO8P%fkZFybki3&0QBBxmh{FE{nynPolbLp`1r%vPe)nxe8C;G}@DN>+;*6*b_j zqGCu{@`x}k3-6PcQS#b?ZXUT9A*oER7c_5DraPhsI>wN1CY3&yO(tFqG@@Z-`1WRssvMGsMlXJ9USsk zH_E?wIrrg;{q26{7YF(f2e_CZZbOxuz|ul!b+Q>U09U2h5$1W~T$3BKI2+$tZmIT{ zMS>Eu5tU4c7Urup>O8Lp52;=*9fVx8#StqM=M6R&9?UVWt%>KGQOXiHbkwYh>d(JWXSRI~TG+UiCht_ORfx%zv&lGk0sNa1KuSpo3yTtvOX;PEG8U17 zf^#WW#_%*}SOM&tsYg}%~nSnJp7%d#m*MLAi&Gm*eZMB&p>QQxFu62F;V{{Gdf zpWU^+T!8=nk$S=mK3YZo>Ak8Kr-t1r^~r8}X|_Ya%~t8;=7VspDV6bYa;Ik52bWws$K{D6yuFk9 z#z#}{{9yaR`%{lToM_BYAzK#*L2Q;+Gsi!E(Rl4e&6Rt;pZ{|GkN42K z;_k^kdS(I0Ps|LZC3yQ?{0UBv&}hq}*BaO)|s(2!f>1f5M-mm6ND&5_xQ70%*` z#mY>d#bU*lmE{#8fl7@X%v4Pdglc@WYCFL!K~|LuwL)SkEGv7E0EUMbV_t;Ceyd1y|R)r*XzPxYJxS5HM70 z>xOEFFZ5n`ymslWgRaH;G99tKJ9PW;-nCaY@4bEa;N1(go!ZJ;ez->7kuFcC)X?4I z9%;Fp0j)tQpqPzpXq}mXism+FUT#+3qEW?&!eS&GBBC6~2n!2AM>}g{OSQCFHaN{} z>Y-VEpm#!0*N(ILbF5yZP?}p@3^+_Q4p{*4F~nONedet$NkEaeHyUoQ%r@rZ0x;(( z`gGd#LAT&;RpF5tOfo?)q*m2dGMbpJ92fA>Mm<}tVp1q22t*E6LI_y`b8TAL=&x-FUMBsgq;EPGeu9=E7Llt-Zdr zIqQ`xksEiqwsumd$35#ywLUkxv7X-7D_mZ&4-8w9DRp51VA0UH5)ks4UFF%Dfif=H zu9s!Eos~SDl3rJz~riZ(H|o_%=>#J$;;19KAlPUZ0fB4G8XBN}TT3o$S@#yd1fBXrG-C&#!3~ z51qrS@~d~8x9-^P-S_nNP$97>W}!xVTiP2UP~d`sgMg1UI8Z(?U@3Q$2*jXo*f6_N z-Z4<-tt!RRfFsT{N0{D(v~92=*=`ReRiP%8*oZPa*^}cfcAa#nrDc6`qS|gfU7LGy z{bVqmius*W{atUoa`kX)cyY2R6*WUQl8Pyf&6RV@X{jhjTnvUCs2}dxzdEqLK52Vr zCHU!@Z5E2=lIXt5qG4O{Nt3bPEsV=cJME;A2yeVWi_ZsC3{aQhg#@IAk;Ul2=H~IP zb_^^>$RL{4LZh0dlwzD7VY0!&U?VZOY>R_C=ha_*RPp9bac>g=ihcxqxZB2u>_Jol z8eC+^_Dp7UW7_e~TX%Xp0!>No$dEP|f=B~2Hu1X$_^Eb8=Qy!-h*FbAHuu1{_XTgh z@IQR)-MMI=SX8WTDmr_R9eu^k?S*x9Ig7Kb*Pl4P`|Z}}k2{|1MJ|R5k1D`V@3^ka za!!}M7q@FO8Oh#-c&b{-A^}%`ZLK1kd?jsNn(G()E6wC;H$N6C6AM6fkO8r&R#{vh zH?t`PLIw(Ak=hW;CT-@HQ`XpjtH= zH+$tJ9u?fD20#Y607Sp{j2ez&DiQmW1>aW+Abd+a6n(7JMGu17*tZe zTvVc06BvxVygY!%0zNgTCB(JyL6xCQPC!CVz29!WGi08qEPVCIu{J?lny1gs(R&AQ z?On*79l_f#>(4INMrvr~Hds{%IX*0^OTlN?lozl14)@KEAJvVln5)`3Cy#rE)~vN% z_zSoEE4Mve`|753)##bCf48h<93QMJeEh8YZ+>_Er@uOT<2wt-k2==Q8deUY%NNR@ zzU+PW?#y5R^^O1f4_E*9pHBbqvi7G})Sn-gy|;@0@&y0qm$EOf6#e!Q{?AXz|M%Pc zUz`%|kL1s_!_&5tDf%VmE{@bU1MlpEv zqVCmwStgagx~#i=v+nuF^KX1IfA+9%^TyEFcDgg;V~_zn2Y78fG!hIAS05aAoZlW? zyHJ06C$n%IS~?0$EZV307@uC`|I=&Y-@nQD=a0mnpOyUR4D%n)xc~hL<6rO6|Lvjp zx99wSc}@SH-_kyu0Ka+N{l*2^P%8KF?VA2UnKwoemlt@GTz`~VR01kP?B-@;bF+D4 zBOQ-P+gc5MJr*b!g4TfeW^%JcfaNlAC2&X-0=0rs6VvOg*qUysB~{9+0@7GPEW`JO zDFSW|4h>K!fFc9D6yTJAWzF@s3i>uG2DhrJGy0~fKtuBZEEqq7H14WrgTlQ7yMSvzcb_;mHgt*Hlhmp12HJtjW3Fgx+ZX5pwc7^{;U zibw_qq7-r-q7W1pWyj|g=7V&zy*H!T**0u%D>^cSxiy0;RM0*knp{*jbl|;_Ja;wH zU@In20In3cT*kG{h1TY1)G6+%H5{+@mCFf7Q>nck$G9K;bWDCP0ehDiNUB$7)1fGPPEiE$H$>|=g(NY4fo`}mAaSKSO?Dk;5L|+&TVkD*Q=Xt&VxX0p z==rnF`t?5b^$q8>EywYS{&>T(I-@P)fuT-kCK0YFx16tyz58VAl?VN2*X!1gDh|(M z2ZzBsw-W1{>YhH-$QXKQRd93?NY?YA2->O_Kx}F^$?QfYbV0|XMQpM{NV4i_v1(CM zlcc9lGdyD+S+LFR1Y3F)R&Pl(&YzgD&(5o7CKW9S$#6$-Bpofz2ZIxp!wde!op4h| zlIZ00jazy;oVWr|8^CSOnMQk9&tGeM{&xS$o@4vWd-;{x%#?It+c&=)CQ-oXm~7{Q z;gu(qZ#-`t8@6<&tpOi*u&-inCNe!4X->JTgT_J_U~quC99fZ|#M88LFX+t(I(o#e z2Cl13aJ=WymY4q$81X!7vcya&X#>`y6Y)M#cPN68`aLRcc=ndA_ zIJ?r$L`=3in_iykj8|CGarfbBi;epp#M) z5u?Y!>uhtY6|kDB(vRL~f9;}ds}H5;0VW2NF>|39?zA8G)ji{HKdF23rgMC<%u_|W zdVT%LqvN_HJDp~i`;Zti7#x-lj|uE3j@M-6>)21LDT&x5?+7|rcsqgMZ?VGRl?(Ed=Txh6I z${`#a@AP%{Xkv}@;Yst}ZgOQRip>Y{fN*@sAFiU+#HdS)-jn0zawASHLqQa4F`-9B zR3oFj_BPDWAa#G&{ODHn%Bp^@g+3T()ANB4o9(_7)DutU8ymbbS^?l803WNQP$RX%0MF!;lNl&2nn?L=#aUW)g_sM9KGNL$6Jl zK3cPUwrYRYXFjZ=9@L_LeK+{e58Z#bt@uwj`9I%*|G!W9|LYO+Kb@lf>6YZ@*F7pk zHdSh<)zsbMQ>tn8o&NeRSE^Y*G#;8=uAg2`j?4s>H|nP*!l{I;EiGy9QY2e7@p`>Q zPk{&{=$D!eq=tmNt3fu@P}W{0@N4;`e84KqbFtAUowhM|&iz%+^Bd~Het1_OsXC4m zNP$ufx_X#5Z`OYF!SLpqzOS7&J*bQa5RJ)#(SH2Vs-(XIQ&opdPsr1w(v?%s&MoWZ z*B#HkU32unar=YbjaO2>9?amOK0PZoguuY8dTG~pbi3v4@9yth?n-p&A{lY_iU-o{ z`90LiXm22IwH5s4A^%r**{^S+<~o7T1sp+m z|EhW8Qe=C#a=f?faMAqWq_)zE@>xk@7Kr#6tCQ}QDEf_C;Rk2-<#Bp{FTSyZboQYB z;Ya<452Hs9!}eqmRtb!8S?^qUWw)WBNtUYPj8B*r)}4*roW5CI(|~+()4zTeUEL2R zqSU!g@w0W!)h_VaJ32y4H}-mn0x=DjdwoT>>Vqg-$-oi zcht9QTl&O3<9c66)Y$6ZJsh4`NVtMDm7^fo#<13v80(7br`0-taVZ^`9cXJg5dqJ( z7E#KHE$K?3iY&7->xVo`ms&d(?d@~+P^*HaMg&_;HQk;>SEw>(Ywf5?clfxHoZe~g z+Le|oZ;WqW>s&l+*g9*Q-%U-eCI=@%JsIcZXw^_pFyN7zb!?|YS`pCY!GO!npPGp~ zToeKx_&unmI?~$N)0P8OgR9`NTf4+eKhhycZlcFOJ_*Q@EFBfUR z7iLMc@G^OESy>50pQw^-l}m0uAXCvgy-2NP$P`2z5#m&=vr1RO$W0Da^>4Q&#{#kd z!`q?`cUS~kEEMI&66{2+0A2v_Xb`Vp%?`LjHUTCli!~J%Djo^gWTZ_eic}iPjht{m z-cYMD>rg%qL#xNA%^017MCX7aB&ezuG&U$qdbm+jq>-0!XgT9Ush!O+xrBy8f`$rL zTUA9FtrSy`MFAcSlM1+L2pF>niO!~vqWS1jLJm+ea9b12gH3L7K42Gt3MKB&cDm7n zw{gKzw|F7Lnd~MUu4|iXP&57Zv6lE)L+Zx%L}Qh7XRGeh?;bpPd*qd;Z95lqBg6QS zank4{er^%Ju*j&ZFDWPl3>uJ?!Sxy}pPgTB;uk?72&yPsLO=lx31w5g40;aH8eXV| zskP@hqv+OQ#mI`s9wIp^u`Ow4g%7R}=9CryG7>07rK98F-VrZH42GwI;tFJQEnS_*hUL4KOA~)JI3=r4`xPNnK+~ zULD|fb=c+?tA|G_3-Yp|85#vDs}*%UhME1y%29Z1*3jRNB7$ei%9SN-C09B}aq zIzo(kFFI9$O9UwCdbz_)A{PT3EPFNsk#}!PL$>H!$YT&NG%P$&uCyCi8Y#x8!qrsr z{pNy@K3k~s7Y}`#83n5lG`S1kJuUykbHktBDtq^^==}$(re;vnOiQFJ-}~Ov)#a+5 z9?|f)uBC(1+b7)Kbu6r^(;0kwKY3u1AB+|lOrR8tL2et~3?NFL3t!YI&t3l^5 zFEb=woYU_w+ID-?D?#MnpWA-2$NSzZuKQO#iwoB7j3HFTEjJTIM9`hoT{>;p+Ns!B z^!K(X>Z77qfS+l#Z>=^~_%V}{=KJ@%j`ovF)1lfb5j+p{_nP)kf}4lsHy_ru_c5dO zMa^mK>=5(jzW3&SG98ofiGYa)YIZRZmYwZIN9A}l;;3CT)FhqD2z!(Gp$3+M1l)3z z&%iS&_z(rcv&Hgw4L_SV=eJq(DoOUFEf1gy@*uC#^Qk<12?JBaA!6v*5)u+gDM52c zlbemrJPs?*Z+<-NgU#1oig^$Uj{o{PCvzv=!M`iQE`=uS_QTI-&|W#T8Uq zecW(d5)85A%0iU}Y4=dm?d8=0m5`HH6Qc#HF|9rNXhL2ew^lk7kbjwU6sLt)UoDE- zsXAtU&?Mtwv&T#(7OmSM?z9vh4`QEPk#BEFgSAMNIbR?L@dk0YhJ>SlmR9UL&)c3p zs8e#F5FgC-m4X*QU@({2vXXNLGs_EkT8;5z9o<^u4o zThX^Kx}b~R2)Mn9d3U?${XO)feeQ#s-hpYoJx(>&;vEV6z>wUo2QAfw4^QG}8#Njs z5HNGtG(g7zs~oIkSU0vbXUFCjANIZXgY`GRw|e1~T5lTBwZvVz;vYMz8{Kd1U#sd{ zazP$_d@a7bWf+*`Cb|i>SnlMWd-ZZ`;i$T~TaJQ(l#}wxW=(fFc(F?T3)?xfUZsPk_lF4 zyC{{UB;v&R1?RoTeb3%oeC5Sl@1!R^V4qkD%0fWKDfN|{D*sO*S z69^7TSewLPXeJQrmf|+Qn?|esRDQ?=5GfH2Ss)A4?N|oSt$y*&p843$10+OPaXXxixM(I-}{rHT=+{Xxx z=tm~Kp&_@^)1_8ZMPg(!YpNV&AHUdq^0HjMJa}fyzk3*8y)Y1rE9Fqr%#t+}q6_)J z2sQS(g+8BJrD3)5Q346fbnBN&o$DLgs|Vf1S;Bk)v$E8%zt7ySNQcKr^>}dQdi3he z7^wxg{SBdbeR7TwjNyEP)E*0sKn50<7&a|9N_HtaSOC1-i~Z?l?_Vy7e!GnL-D<;k z7kF=1MU!pdT0iFRH&lNpA%M-&B9_!DRX7$4u-SEN7KThg;fY8*5g`<}h~*T12OiXc z@sknvka=Qpcz!1%Fw-1k8gI(r&vaRaB@8LX7ghII$VFzn66?bP9*ZEkF6+~A=V+JC_re*7U_?T_$Dd)H0Bu z>bZj6*7TIJ%RolfR<{liNI=%vI2RA)6nmTt4TqK({KpS9Z^sG(w}WjgP}Y60xJJv%b0o zkD!&4RZ_eZcH_rIWc_BJX`?&13H=I3fTAov1DBH#pY`-5nk>n<(TB9SZpr39ufHSA~CsGaGAaD<&^)3*I z6T)K+nK{nhjlR_j#w!P&yA|)#J=bBP>BomXpDxxP7P({nc)1Yx+|RX+$LSnGK+zZp7>2*bE z-?FmXvwh^d_8@ZoPVmwt=jDA*Wq)vMV{q87QgExayQt#~ty`~FaWLr!t2`?_J;BIF zxVYoAEv7n+gx*+-fUmVR*Xcx5I6z^MDMB`<35~{KF!(y8oWs<&xALjgrAF{mO$a2H z(!!xM!|-{Gqc!lsqEHRUDyANv8ge~a!6M0MO>Knwc2WZo02L1GgarjTxSgVWdj$RP zMETDLod4Ww`QH)Yzt{2KZz3KBz#q3cUllRW=TP4qsvoS&mNU|9${6x04Qh^7)#h?4 zXXe}s%g$uhVDDr4!X0)SU#nm`Ok$6n3uznqtw1TkRjY-MOFogu&1taAWCVg;-!S5I z+2k@YnQRk_vLRpGBHEZ@-T#T+<^Y~Z3*@MG2I=D&o!W>ILp26F(ryLsRV;6wPgK^q zceZF>Kb8OWwc>|gcfWmQeD-C_mf5-CASqZFa8i1%?()x@eo+haXOHPAzM~PK?(8$_t9!4ZBWT zXSZ{W28NUgR)(2(H?-?T_WGvk;BMlRUu-{oeI{N~>VqIU%T1R|iFs)4uzzGxO4Wdg z73s}~p@|$t>jXW6n28m6BrkA88AfNbfQxJ=BjA{f2EcoH+ApuF$MxXxhT%p*oX`Wm z7WvF*9>*umfFFuOBdAm5fD7yC~wOxthnt@1!5Vcpw`o~APOADsu zqP?@b(b>m1Icu8ALN+_2*F|4ghCGA#;AHz$$sQ?~k}JK}-k*N)b-6#s4yLJ|5j<>J zv#Wyjef`$0zKz3yfrQ@JBT$H&JG=OuUbUuQ1C1HVk4{ydo(iP|lk=k336V-kB4A+^ z0H79@p(Z;G#gp1v6cCGv9*@v!1;wJoX+|Dgnt1%-#;c!Sd--}}@6fw;RyV&W&d=g>aU?05%n6gz%&{c;%7J2ghjV%rdwLDOu+o?*fOG+wo^8(LxRVKyt-DFB1d$Q; z@Hojm2qF`NaEu%tVmmzCHa;GKs4Ag|j0s9=zua+tyCeDXBzRZGe1AjnbU_ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/item_social_capsule_open.xml b/frontend/ARchive/app/src/main/res/layout/item_social_capsule_open.xml new file mode 100644 index 000000000..e875ae9bc --- /dev/null +++ b/frontend/ARchive/app/src/main/res/layout/item_social_capsule_open.xml @@ -0,0 +1,203 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/values/colors.xml b/frontend/ARchive/app/src/main/res/values/colors.xml index 851fd6ebc..9cd32d4b7 100644 --- a/frontend/ARchive/app/src/main/res/values/colors.xml +++ b/frontend/ARchive/app/src/main/res/values/colors.xml @@ -24,8 +24,14 @@ #324263F7 #801B3AC4 + #E6FFFFFF + #CCFFFFFF #B3FFFFFF #99FFFFFF + #80FFFFFF + #66FFFFFF + #4DFFFFFF + #33FFFFFF #D94263F7 #B3ECEFFE #B31B3AC4 From 9d0646c860f70063c7913209c4d26fe70058476f Mon Sep 17 00:00:00 2001 From: comst19 Date: Tue, 2 Apr 2024 20:32:53 +0900 Subject: [PATCH 125/301] =?UTF-8?q?chore:=20=ED=8C=A8=ED=82=A4=EC=A7=80=20?= =?UTF-8?q?=EA=B5=AC=EC=A1=B0=20=EB=B3=80=EA=B2=BD=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B8=ED=95=9C=20import=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ui/mypage/friend/adapter/FriendVPA.kt | 3 --- .../archive/presentation/ui/social/adapter/SocialVPA.kt | 4 ++-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/adapter/FriendVPA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/adapter/FriendVPA.kt index bdf1691c5..1483f4595 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/adapter/FriendVPA.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/adapter/FriendVPA.kt @@ -1,13 +1,10 @@ package com.droidblossom.archive.presentation.ui.mypage.friend.adapter -import android.app.Activity import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity import androidx.viewpager2.adapter.FragmentStateAdapter import com.droidblossom.archive.presentation.ui.mypage.friend.page.FriendListFragment import com.droidblossom.archive.presentation.ui.mypage.friend.page.GroupListFragment -import com.droidblossom.archive.presentation.ui.social.page.SocialFriendFragment -import com.droidblossom.archive.presentation.ui.social.page.SocialGroupFragment class FriendVPA(activity: FragmentActivity) : FragmentStateAdapter(activity){ diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/adapter/SocialVPA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/adapter/SocialVPA.kt index 0f4248a2d..e495277e8 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/adapter/SocialVPA.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/adapter/SocialVPA.kt @@ -2,8 +2,8 @@ package com.droidblossom.archive.presentation.ui.social.adapter import androidx.fragment.app.Fragment import androidx.viewpager2.adapter.FragmentStateAdapter -import com.droidblossom.archive.presentation.ui.social.page.SocialFriendFragment -import com.droidblossom.archive.presentation.ui.social.page.SocialGroupFragment +import com.droidblossom.archive.presentation.ui.social.page.friend.SocialFriendFragment +import com.droidblossom.archive.presentation.ui.social.page.group.SocialGroupFragment class SocialVPA(fragment: Fragment) : FragmentStateAdapter(fragment){ From 028bc9304eefe84986e2e77f1531dc28c7659905 Mon Sep 17 00:00:00 2001 From: comst19 Date: Tue, 2 Apr 2024 20:35:59 +0900 Subject: [PATCH 126/301] =?UTF-8?q?feat:=20=EB=A6=AC=EC=82=AC=EC=9D=B4?= =?UTF-8?q?=ED=81=B4=EB=9F=AC=EB=B7=B0=20=EA=B0=81=20=EC=95=84=EC=9D=B4?= =?UTF-8?q?=ED=85=9C=20=EA=B0=84=EA=B2=A9=20=EB=84=A3=EB=8A=94=20util?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/util/SpaceItemDecoration.kt | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/SpaceItemDecoration.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/SpaceItemDecoration.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/SpaceItemDecoration.kt new file mode 100644 index 000000000..57b31361c --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/SpaceItemDecoration.kt @@ -0,0 +1,19 @@ +package com.droidblossom.archive.util + +import android.graphics.Rect +import android.view.View +import androidx.recyclerview.widget.RecyclerView + +class SpaceItemDecoration( + private val spaceLeft: Int = 0, + private val spaceRight: Int = 0, + private val spaceTop: Int = 0, + private val spaceBottom: Int = 0 +) : RecyclerView.ItemDecoration() { + override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) { + outRect.left = spaceLeft + outRect.right = spaceRight + outRect.top = spaceTop + outRect.bottom = spaceBottom + } +} \ No newline at end of file From 28aa0bd2c11a1141a6f0d0027ca1d2093b666957 Mon Sep 17 00:00:00 2001 From: comst19 Date: Tue, 2 Apr 2024 20:36:53 +0900 Subject: [PATCH 127/301] =?UTF-8?q?feat:=20=EA=B7=B8=EB=A3=B9,=20=EC=B9=9C?= =?UTF-8?q?=EA=B5=AC=EB=93=A4=EC=9D=98=20=EC=BA=A1=EC=8A=90=20=EC=95=84?= =?UTF-8?q?=EC=9D=B4=ED=85=9C=EB=93=A4=EC=9D=98=20=EB=A6=AC=EC=82=AC?= =?UTF-8?q?=EC=9D=B4=ED=81=B4=EB=9F=AC=EB=B7=B0=20=EC=96=B4=EB=8C=91?= =?UTF-8?q?=ED=84=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../social/adapter/SocialFriendCapsuleRVA.kt | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/adapter/SocialFriendCapsuleRVA.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/adapter/SocialFriendCapsuleRVA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/adapter/SocialFriendCapsuleRVA.kt new file mode 100644 index 000000000..472e2a39e --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/adapter/SocialFriendCapsuleRVA.kt @@ -0,0 +1,74 @@ +package com.droidblossom.archive.presentation.ui.social.adapter + +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.droidblossom.archive.databinding.ItemSocialCapsuleCloseBinding +import com.droidblossom.archive.databinding.ItemSocialCapsuleOpenBinding + +class SocialFriendCapsuleRVA() : ListAdapter(differ) { + + inner class OpenedCapsuleViewHolder( + private val binding: ItemSocialCapsuleOpenBinding + ) : RecyclerView.ViewHolder(binding.root){ + + fun bindOpenedCapsule(data : TestSocialFriendModel){ + binding.item = data + } + + } + + inner class ClosedCapsuleViewHolder( + private val binding: ItemSocialCapsuleCloseBinding + ) : RecyclerView.ViewHolder(binding.root){ + + fun bindClosedCapsule(data: TestSocialFriendModel){ + binding.item = data + } + + } + + override fun getItemViewType(position: Int): Int { + return if (getItem(position).isOpened) { + TYPE_OPENED_CAPSULE + } else { + TYPE_CLOSED_CAPSULE + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + val inflater = LayoutInflater.from(parent.context) + return when (viewType) { + TYPE_OPENED_CAPSULE -> OpenedCapsuleViewHolder(ItemSocialCapsuleOpenBinding.inflate(inflater, parent, false)) + TYPE_CLOSED_CAPSULE -> ClosedCapsuleViewHolder(ItemSocialCapsuleCloseBinding.inflate(inflater, parent, false)) + else -> throw IllegalArgumentException("Invalid view type") + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + val item = getItem(position) + when (holder) { + is OpenedCapsuleViewHolder -> holder.bindOpenedCapsule(item) + is ClosedCapsuleViewHolder -> holder.bindClosedCapsule(item) + } + } + + + companion object { + + private const val TYPE_OPENED_CAPSULE = 0 + private const val TYPE_CLOSED_CAPSULE = 1 + + val differ = object : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: TestSocialFriendModel, newItem: TestSocialFriendModel): Boolean { + return oldItem.id == newItem.id + } + + override fun areContentsTheSame(oldItem: TestSocialFriendModel, newItem: TestSocialFriendModel): Boolean { + return oldItem == newItem + } + } + } +} \ No newline at end of file From cf8cb2ad44017904ebe174c51eedc0608f6cfd73 Mon Sep 17 00:00:00 2001 From: comst19 Date: Tue, 2 Apr 2024 20:37:30 +0900 Subject: [PATCH 128/301] =?UTF-8?q?feat:=20SocialFriendFragment=20?= =?UTF-8?q?=EB=A6=AC=EC=82=AC=EC=9D=B4=ED=81=B4=EB=9F=AC=EB=B7=B0=20?= =?UTF-8?q?=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../page/friend/SocialFriendFragment.kt | 122 ++++++++++++++++++ .../page/friend/SocialFriendViewModel.kt | 9 ++ .../page/friend/SocialFriendViewModelImpl.kt | 25 ++++ .../res/layout/fragment_social_friend.xml | 104 ++++++++++++++- 4 files changed, 255 insertions(+), 5 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendFragment.kt index 7c8435fc6..f32c1a969 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendFragment.kt @@ -1,28 +1,150 @@ package com.droidblossom.archive.presentation.ui.social.page.friend +import android.annotation.SuppressLint +import android.graphics.Rect import android.os.Bundle +import android.util.TypedValue import androidx.fragment.app.Fragment import android.view.LayoutInflater +import android.view.MotionEvent import android.view.View import android.view.ViewGroup +import android.view.inputmethod.EditorInfo +import android.view.inputmethod.InputMethodManager +import androidx.constraintlayout.widget.ConstraintLayout import androidx.fragment.app.viewModels +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView import com.droidblossom.archive.R import com.droidblossom.archive.databinding.FragmentSocialFriendBinding import com.droidblossom.archive.databinding.FragmentSocialGroupBinding import com.droidblossom.archive.presentation.base.BaseFragment +import com.droidblossom.archive.presentation.ui.social.adapter.SocialFriendCapsuleRVA +import com.droidblossom.archive.presentation.ui.social.adapter.TestSocialFriendModel import com.droidblossom.archive.presentation.ui.social.page.group.SocialGroupViewModelImpl +import com.droidblossom.archive.util.SpaceItemDecoration +import com.droidblossom.archive.util.updateTopConstraintsForSearch +import kotlinx.coroutines.launch class SocialFriendFragment : BaseFragment(R.layout.fragment_social_friend) { override val viewModel: SocialFriendViewModelImpl by viewModels() + private val socialFriendCapsuleRVA by lazy { + SocialFriendCapsuleRVA() + } + override fun observeData() { + viewLifecycleOwner.lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.isSearchOpen.collect { + val layoutParams = binding.socialFriendRV.layoutParams as ConstraintLayout.LayoutParams + layoutParams.updateTopConstraintsForSearch( + isSearchOpen = it, + searchOpenView = binding.searchOpenBtn, + searchView = binding.searchBtn, + additionalMarginDp = 16f, + resources = resources + ) + if (it){ + binding.searchOpenEditT.requestFocus() + val imm = requireActivity().getSystemService(InputMethodManager::class.java) + imm.showSoftInput(binding.searchOpenEditT, InputMethodManager.SHOW_IMPLICIT); + } + } + } + } } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding.vm = viewModel + initRVA() + initSearchEdit() + } + + private fun initRVA() { + binding.socialFriendRV.adapter = socialFriendCapsuleRVA + + val spaceInPixels = resources.getDimensionPixelSize(R.dimen.margin) + binding.socialFriendRV.addItemDecoration(SpaceItemDecoration(spaceBottom = spaceInPixels)) + socialFriendCapsuleRVA.submitList(dummyFriendCapsules()) + } + + private fun dummyFriendCapsules(): MutableList{ + + val dummyList = mutableListOf() + + for(i in 1..10){ + val capsule = TestSocialFriendModel( + id = i.toLong(), + capsuleTitle = "comprehensam", + capsuleWriter = "deseruisse", + capsuleContent = "eirmod", + capsuleContentImg = "enim", + capsuleLocation = "discere", + capsuleCreateTime = "amet", + isOpened = i % 2 == 0 + ) + + dummyList.add(capsule) + } + + return dummyList + } + + @SuppressLint("ClickableViewAccessibility") + fun initSearchEdit(){ + binding.searchOpenEditT.setOnEditorActionListener { _, i, _ -> + if (i == EditorInfo.IME_ACTION_DONE) { + if (!binding.searchOpenEditT.text.isNullOrEmpty()) { + viewModel.searchFriendCapsule() + } + true + } + false + } + binding.searchOpenEditT.setOnFocusChangeListener { _, hasFocus -> + if (!hasFocus) { + viewModel.closeSearchFriendCapsule() + } + } + binding.root.setOnTouchListener { _, event -> + if (event.action == MotionEvent.ACTION_DOWN) { + val focusedView = binding.searchOpenBtn + val outRect = Rect() + focusedView.getGlobalVisibleRect(outRect) + if (!outRect.contains(event.rawX.toInt(), event.rawY.toInt())) { + focusedView.clearFocus() + val imm = requireActivity().getSystemService(InputMethodManager::class.java) + imm.hideSoftInputFromWindow(focusedView.windowToken, 0) + } + } + false + } + + binding.socialFriendRV.setOnTouchListener { _, event -> + if (event.action == MotionEvent.ACTION_DOWN) { + val focusedView = binding.searchOpenBtn + val outRect = Rect() + focusedView.getGlobalVisibleRect(outRect) + if (!outRect.contains(event.rawX.toInt(), event.rawY.toInt())) { + focusedView.clearFocus() + val imm = requireActivity().getSystemService(InputMethodManager::class.java) + imm.hideSoftInputFromWindow(focusedView.windowToken, 0) + } + } + false + } + + binding.searchOpenBtnT.setOnClickListener { + val imm = requireActivity().getSystemService(InputMethodManager::class.java) + imm.hideSoftInputFromWindow(requireActivity().currentFocus?.windowToken, 0) + } } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModel.kt index 05c20cdf0..75329ffb2 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModel.kt @@ -1,4 +1,13 @@ package com.droidblossom.archive.presentation.ui.social.page.friend +import kotlinx.coroutines.flow.StateFlow + interface SocialFriendViewModel { + + val isSearchOpen : StateFlow + + fun openSearchFriendCapsule() + fun closeSearchFriendCapsule() + + fun searchFriendCapsule() } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModelImpl.kt index 91e436eac..d3437aacf 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModelImpl.kt @@ -1,7 +1,11 @@ package com.droidblossom.archive.presentation.ui.social.page.friend +import androidx.lifecycle.viewModelScope import com.droidblossom.archive.presentation.base.BaseViewModel import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel @@ -9,4 +13,25 @@ class SocialFriendViewModelImpl @Inject constructor( ): BaseViewModel(), SocialFriendViewModel { + + private val _isSearchOpen = MutableStateFlow(false) + override val isSearchOpen: StateFlow + get() = _isSearchOpen + + override fun openSearchFriendCapsule() { + viewModelScope.launch { + _isSearchOpen.emit(true) + } + } + + override fun closeSearchFriendCapsule() { + viewModelScope.launch { + _isSearchOpen.emit(false) + } + } + + override fun searchFriendCapsule(){ + + } + } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_social_friend.xml b/frontend/ARchive/app/src/main/res/layout/fragment_social_friend.xml index f5e341eb6..f7c68f36b 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_social_friend.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_social_friend.xml @@ -1,9 +1,13 @@ + xmlns:bind="http://schemas.android.com/tools"> + + + @@ -12,21 +16,111 @@ + android:background="@color/main_bg_1" + tools:context=".presentation.ui.social.page.friend.SocialFriendFragment"> + + + + + + + + + + + + + + + + + + + + + app:layout_constraintStart_toStartOf="parent"/> From 525dde42795725a61aeb8aee731eacbbed1385da Mon Sep 17 00:00:00 2001 From: comst19 Date: Tue, 2 Apr 2024 20:37:54 +0900 Subject: [PATCH 129/301] =?UTF-8?q?feat:=20SocialGroupFragment=20=EB=A6=AC?= =?UTF-8?q?=EC=82=AC=EC=9D=B4=ED=81=B4=EB=9F=AC=EB=B7=B0=20=EC=97=B0?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../social/page/group/SocialGroupFragment.kt | 135 +++++++++++++++++- .../social/page/group/SocialGroupViewModel.kt | 9 ++ .../page/group/SocialGroupViewModelImpl.kt | 25 ++++ .../main/res/layout/fragment_social_group.xml | 113 ++++++++++++++- 4 files changed, 273 insertions(+), 9 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupFragment.kt index 5b41c110b..086f2ddce 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupFragment.kt @@ -1,29 +1,154 @@ package com.droidblossom.archive.presentation.ui.social.page.group +import android.annotation.SuppressLint +import android.graphics.Rect import android.os.Bundle -import androidx.fragment.app.Fragment -import android.view.LayoutInflater +import android.util.TypedValue +import android.view.MotionEvent import android.view.View -import android.view.ViewGroup +import android.view.inputmethod.EditorInfo +import android.view.inputmethod.InputMethodManager +import androidx.constraintlayout.widget.ConstraintLayout import androidx.fragment.app.viewModels +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import com.droidblossom.archive.R -import com.droidblossom.archive.databinding.FragmentSocialFriendBinding import com.droidblossom.archive.databinding.FragmentSocialGroupBinding import com.droidblossom.archive.presentation.base.BaseFragment -import com.droidblossom.archive.presentation.ui.social.SocialViewModelImpl +import com.droidblossom.archive.presentation.ui.social.adapter.SocialFriendCapsuleRVA +import com.droidblossom.archive.presentation.ui.social.adapter.TestSocialFriendModel +import com.droidblossom.archive.util.SpaceItemDecoration +import com.droidblossom.archive.util.updateTopConstraintsForSearch import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.launch @AndroidEntryPoint class SocialGroupFragment : BaseFragment(R.layout.fragment_social_group) { override val viewModel: SocialGroupViewModelImpl by viewModels() + private val socialFriendCapsuleRVA by lazy { + SocialFriendCapsuleRVA() + } override fun observeData() { + viewLifecycleOwner.lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.isSearchOpen.collect { + val layoutParams = binding.socialGroupRV.layoutParams as ConstraintLayout.LayoutParams + layoutParams.updateTopConstraintsForSearch( + isSearchOpen = it, + searchOpenView = binding.searchOpenBtn, + searchView = binding.searchBtn, + additionalMarginDp = 16f, + resources = resources + ) + if (it){ + binding.searchOpenEditT.requestFocus() + val imm = requireActivity().getSystemService(InputMethodManager::class.java) + imm.showSoftInput(binding.searchOpenEditT, InputMethodManager.SHOW_IMPLICIT); + + } + } + } + } } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding.vm = viewModel + initSearchEdit() + initRVA() + } + + private fun initRVA() { + binding.socialGroupRV.adapter = socialFriendCapsuleRVA + + val spaceInPixels = resources.getDimensionPixelSize(R.dimen.margin) + binding.socialGroupRV.addItemDecoration(SpaceItemDecoration(spaceBottom = spaceInPixels)) + socialFriendCapsuleRVA.submitList(dummyGroupCapsules()) } + + private fun dummyGroupCapsules(): MutableList{ + + val dummyList = mutableListOf() + + for(i in 1..10){ + val capsule = TestSocialFriendModel( + id = i.toLong(), + capsuleTitle = "comprehensam", + capsuleWriter = "deseruisse", + capsuleContent = "eirmod", + capsuleContentImg = "enim", + capsuleLocation = "discere", + capsuleCreateTime = "amet", + isOpened = i % 2 == 0 + ) + + dummyList.add(capsule) + } + + return dummyList + } + + @SuppressLint("ClickableViewAccessibility") + fun initSearchEdit(){ + binding.searchOpenEditT.setOnEditorActionListener { _, i, _ -> + if (i == EditorInfo.IME_ACTION_DONE) { + if (!binding.searchOpenEditT.text.isNullOrEmpty()) { + viewModel.searchGroupCapsule() + } + true + } + false + } + binding.searchOpenEditT.setOnFocusChangeListener { _, hasFocus -> + if (!hasFocus) { + viewModel.closeSearchGroupCapsule() + } + } + binding.root.setOnTouchListener { _, event -> + if (event.action == MotionEvent.ACTION_DOWN) { + val focusedView = binding.searchOpenBtn + val outRect = Rect() + focusedView.getGlobalVisibleRect(outRect) + if (!outRect.contains(event.rawX.toInt(), event.rawY.toInt())) { + focusedView.clearFocus() + val imm = requireActivity().getSystemService(InputMethodManager::class.java) + imm.hideSoftInputFromWindow(focusedView.windowToken, 0) + } + } + false + } + + binding.socialGroupRV.setOnTouchListener { _, event -> + if (event.action == MotionEvent.ACTION_DOWN) { + val focusedView = binding.searchOpenBtn + val outRect = Rect() + focusedView.getGlobalVisibleRect(outRect) + if (!outRect.contains(event.rawX.toInt(), event.rawY.toInt())) { + focusedView.clearFocus() + val imm = requireActivity().getSystemService(InputMethodManager::class.java) + imm.hideSoftInputFromWindow(focusedView.windowToken, 0) + } + } + false + } + + binding.searchOpenBtnT.setOnClickListener { + val imm = requireActivity().getSystemService(InputMethodManager::class.java) + imm.hideSoftInputFromWindow(requireActivity().currentFocus?.windowToken, 0) + } + } + + private fun updateRecyclerViewConstraints(isSearchOpen: Boolean) { + val layoutParams = binding.socialGroupRV.layoutParams as ConstraintLayout.LayoutParams + layoutParams.topToBottom = if (isSearchOpen) binding.searchOpenBtn.id else binding.searchBtn.id + val additionalMargin = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 15f, resources.displayMetrics).toInt() + layoutParams.topMargin = additionalMargin + binding.socialGroupRV.layoutParams = layoutParams + } + + } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupViewModel.kt index 2939d5dd8..42f090dc2 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupViewModel.kt @@ -1,4 +1,13 @@ package com.droidblossom.archive.presentation.ui.social.page.group +import kotlinx.coroutines.flow.StateFlow + interface SocialGroupViewModel { + + val isSearchOpen : StateFlow + + fun openSearchGroupCapsule() + fun closeSearchGroupCapsule() + + fun searchGroupCapsule() } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupViewModelImpl.kt index 6febe1b76..7a5395976 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupViewModelImpl.kt @@ -1,7 +1,11 @@ package com.droidblossom.archive.presentation.ui.social.page.group +import androidx.lifecycle.viewModelScope import com.droidblossom.archive.presentation.base.BaseViewModel import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel @@ -9,4 +13,25 @@ class SocialGroupViewModelImpl @Inject constructor( ): BaseViewModel(), SocialGroupViewModel { + + private val _isSearchOpen = MutableStateFlow(false) + override val isSearchOpen: StateFlow + get() = _isSearchOpen + + override fun openSearchGroupCapsule() { + viewModelScope.launch { + _isSearchOpen.emit(true) + } + } + + override fun closeSearchGroupCapsule() { + viewModelScope.launch { + _isSearchOpen.emit(false) + } + } + + override fun searchGroupCapsule(){ + + } + } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_social_group.xml b/frontend/ARchive/app/src/main/res/layout/fragment_social_group.xml index 6aec867ad..01fe941a1 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_social_group.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_social_group.xml @@ -1,8 +1,12 @@ + + + @@ -11,13 +15,114 @@ - - + android:layout_height="match_parent"> + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From b4118695480672bc7a9760b58f87240ef044fb34 Mon Sep 17 00:00:00 2001 From: comst19 Date: Tue, 2 Apr 2024 20:43:49 +0900 Subject: [PATCH 130/301] =?UTF-8?q?design:=20=EA=B2=80=EC=83=89=EC=B0=BD?= =?UTF-8?q?=20=EC=95=84=EB=9E=98=EC=9D=98=20=EB=A6=AC=EC=82=AC=EC=9D=B4?= =?UTF-8?q?=ED=81=B4=EB=9F=AC=EB=B7=B0=20=EB=94=94=EC=9E=90=EC=9D=B8=20?= =?UTF-8?q?=ED=86=B5=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/home/createcapsule/CreateCapsule2Fragment.kt | 10 ++++++++++ .../archive/presentation/ui/skin/SkinFragment.kt | 10 ++++++++++ .../src/main/res/layout/fragment_create_capsule2.xml | 4 ++-- .../ARchive/app/src/main/res/layout/fragment_skin.xml | 4 ++-- 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsule2Fragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsule2Fragment.kt index 449b9af2b..35c142fd5 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsule2Fragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsule2Fragment.kt @@ -10,6 +10,7 @@ import android.view.inputmethod.EditorInfo import android.view.inputmethod.InputMethodManager import android.widget.Toast import androidx.activity.OnBackPressedCallback +import androidx.constraintlayout.widget.ConstraintLayout import androidx.fragment.app.activityViewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope @@ -24,6 +25,7 @@ import com.droidblossom.archive.databinding.FragmentCreateCapsule2Binding import com.droidblossom.archive.presentation.base.BaseFragment import com.droidblossom.archive.presentation.ui.home.createcapsule.adapter.SkinRVA import com.droidblossom.archive.presentation.ui.skin.adapter.SkinMotionRVA +import com.droidblossom.archive.util.updateTopConstraintsForSearch import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch @@ -99,6 +101,14 @@ class CreateCapsule2Fragment : viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.isSearchOpen.collect { + val layoutParams = binding.recycleView.layoutParams as ConstraintLayout.LayoutParams + layoutParams.updateTopConstraintsForSearch( + isSearchOpen = it, + searchOpenView = binding.searchOpenBtn, + searchView = binding.searchBtn, + additionalMarginDp = 16f, + resources = resources + ) if (it){ binding.searchOpenEditT.requestFocus() val imm = requireActivity().getSystemService(InputMethodManager::class.java) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinFragment.kt index ac38ac5f3..19ec026e8 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinFragment.kt @@ -11,6 +11,7 @@ import android.view.View import android.view.ViewGroup import android.view.inputmethod.EditorInfo import android.view.inputmethod.InputMethodManager +import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.content.ContentProviderCompat.requireContext import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle @@ -25,6 +26,7 @@ import com.droidblossom.archive.presentation.ui.home.HomeFragment import com.droidblossom.archive.presentation.ui.skin.adapter.MySkinRVA import com.droidblossom.archive.presentation.ui.skin.skinmake.SkinMakeActivity import com.droidblossom.archive.util.LocationUtil +import com.droidblossom.archive.util.updateTopConstraintsForSearch import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch @@ -107,6 +109,14 @@ class SkinFragment : BaseFragment(R.layo viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.isSearchOpen.collect { + val layoutParams = binding.skinRV.layoutParams as ConstraintLayout.LayoutParams + layoutParams.updateTopConstraintsForSearch( + isSearchOpen = it, + searchOpenView = binding.searchOpenBtn, + searchView = binding.searchBtn, + additionalMarginDp = 16f, + resources = resources + ) if (it){ binding.searchOpenEditT.requestFocus() val imm = requireActivity().getSystemService(InputMethodManager::class.java) diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_create_capsule2.xml b/frontend/ARchive/app/src/main/res/layout/fragment_create_capsule2.xml index 1d41721fc..2f685bb8f 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_create_capsule2.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_create_capsule2.xml @@ -32,7 +32,7 @@ Date: Tue, 2 Apr 2024 22:37:01 +0900 Subject: [PATCH 131/301] =?UTF-8?q?feat:=20vm=20=EC=9E=91=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/mypage/friend/FriendActivity.kt | 7 +++ .../friend/addfriend/AddFriendViewModel.kt | 9 +++ .../addfriend/AddFriendViewModelImpl.kt | 20 ++++++- .../addfriend/SearchFriendNicknameFragment.kt | 56 +++++++++++++++++-- .../fragment_friend_search_nickname.xml | 2 +- .../src/main/res/layout/item_add_friend.xml | 2 +- 6 files changed, 87 insertions(+), 9 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendActivity.kt index 64925e2d8..54843fd19 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendActivity.kt @@ -9,6 +9,7 @@ import com.droidblossom.archive.R import com.droidblossom.archive.databinding.ActivityFriendBinding import com.droidblossom.archive.presentation.base.BaseActivity import com.droidblossom.archive.presentation.ui.mypage.friend.adapter.FriendVPA +import com.droidblossom.archive.presentation.ui.mypage.friend.addfriend.AddFriendActivity import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayout.OnTabSelectedListener import com.google.android.material.tabs.TabLayoutMediator @@ -48,8 +49,14 @@ class FriendActivity : override fun onTabSelected(tab: TabLayout.Tab) { if (tab.position == 0){ binding.addT.text = "그룹 추가" + binding.addCV.setOnClickListener { + + } } else { binding.addT.text = "친구 추가" + binding.addCV.setOnClickListener{ + startActivity(AddFriendActivity.newIntent(this@FriendActivity)) + } } } override fun onTabUnselected(tab: TabLayout.Tab) {} diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModel.kt index 5e81218f6..b7e808c8a 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModel.kt @@ -1,8 +1,17 @@ package com.droidblossom.archive.presentation.ui.mypage.friend.addfriend +import com.droidblossom.archive.presentation.ui.mypage.MyPageViewModel +import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow interface AddFriendViewModel { + val addEvent : SharedFlow + val isSearchNumOpen : StateFlow fun searchName() + fun openSearchName() + + sealed class AddEvent { + data class ShowToastMessage(val message : String) : AddEvent() + } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt index 1d9f0bdbc..9bfeb5935 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt @@ -3,8 +3,11 @@ package com.droidblossom.archive.presentation.ui.mypage.friend.addfriend import androidx.lifecycle.viewModelScope import com.droidblossom.archive.presentation.base.BaseViewModel import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.launch import javax.inject.Inject @@ -12,9 +15,24 @@ import javax.inject.Inject class AddFriendViewModelImpl @Inject constructor( ) : BaseViewModel(), AddFriendViewModel { - + + private val _addEvent = MutableSharedFlow() + + override val addEvent: SharedFlow + get() = _addEvent.asSharedFlow() + + private val _isSearchNumOpen = MutableStateFlow(false) + override val isSearchNumOpen: StateFlow + get() = _isSearchNumOpen + override fun searchName() { } + override fun openSearchName() { + viewModelScope.launch { + _isSearchNumOpen.emit(true) + } + } + } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNicknameFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNicknameFragment.kt index d5fd937c2..8707b201e 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNicknameFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNicknameFragment.kt @@ -5,16 +5,60 @@ 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.navigation.NavController +import androidx.navigation.Navigation import com.droidblossom.archive.R +import com.droidblossom.archive.databinding.FragmentFriendSearchNicknameBinding +import com.droidblossom.archive.presentation.base.BaseFragment import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.launch @AndroidEntryPoint -class SearchFriendNicknameFragment : Fragment() { +class SearchFriendNicknameFragment : + BaseFragment(R.layout.fragment_friend_search_nickname) { - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - return inflater.inflate(R.layout.fragment_friend_search_nickname, container, false) + override val viewModel: AddFriendViewModelImpl by viewModels() + + lateinit var navController: NavController + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + navController = Navigation.findNavController(view) + initView() + } + + private fun initView() { + + val layoutParams = binding.closeBtn.layoutParams as ViewGroup.MarginLayoutParams + layoutParams.topMargin += getStatusBarHeight() + binding.closeBtn.layoutParams = layoutParams + + binding.closeBtn.setOnClickListener { + (activity as AddFriendActivity).finish() + } + + binding.addCV.setOnClickListener { + navController.navigate(R.id.action_searchFriendNicknameFragment_to_searchFriendNumberFragment) + } + } + + override fun observeData() { + viewLifecycleOwner.lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.addEvent.collect { event -> + when (event) { + is AddFriendViewModel.AddEvent.ShowToastMessage -> { + showToastMessage(event.message) + } + + else -> {} + } + } + } + } } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_friend_search_nickname.xml b/frontend/ARchive/app/src/main/res/layout/fragment_friend_search_nickname.xml index 351464e5c..98c680adf 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_friend_search_nickname.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_friend_search_nickname.xml @@ -106,7 +106,7 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/titleT" app:spanCount="3" - tools:itemCount="30" + tools:itemCount="1" tools:listitem="@layout/item_add_friend" /> Date: Wed, 3 Apr 2024 11:54:40 +0900 Subject: [PATCH 132/301] build: Update Retrofit base URL for API endpoint changes --- .../archive/data/source/remote/api/SecretService.kt | 2 +- .../src/main/java/com/droidblossom/archive/di/RetrofitModule.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/SecretService.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/SecretService.kt index 155ffe15c..19b3b115d 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/SecretService.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/SecretService.kt @@ -25,7 +25,7 @@ interface SecretService { @Query("createdAt") createdAt: String ) : Response> - @POST("secret/capsules") + @POST("capsules/secret") suspend fun postSecretCapsuleApi( @Body request : SecretCapsuleCreateRequestDto ) : Response> diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/di/RetrofitModule.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/di/RetrofitModule.kt index f1a595c3d..1308a9931 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/di/RetrofitModule.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/di/RetrofitModule.kt @@ -80,7 +80,7 @@ object RetrofitModule { gsonConverterFactory: GsonConverterFactory ): Retrofit { return Retrofit.Builder() - .baseUrl(BuildConfig.BASE_URL) + .baseUrl("${BuildConfig.BASE_URL}api/") .addConverterFactory(gsonConverterFactory) .client(client) .build() From 02dc5dfeee9b0b9783454944d603a9d6aca79880 Mon Sep 17 00:00:00 2001 From: comst19 Date: Wed, 3 Apr 2024 16:13:24 +0900 Subject: [PATCH 133/301] rename: SecretCapsuleCreateRequest(model, dto) -> CapsuleCreateRequest(model, dto) --- .../CapsuleCreateRequestDto.kt} | 5 ++--- .../archive/data/repository/SecretRepositoryImpl.kt | 5 ++--- .../archive/data/source/remote/api/SecretService.kt | 6 ++---- .../CapsuleCreateRequest.kt} | 10 ++++------ .../archive/domain/repository/SecretRepository.kt | 4 ++-- .../usecase/secret/SecretCapsuleCreateUseCase.kt | 6 ++---- .../home/createcapsule/CreateCapsuleViewModelImpl.kt | 12 ++---------- 7 files changed, 16 insertions(+), 32 deletions(-) rename frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/{secret/request/SecretCapsuleCreateRequestDto.kt => common/CapsuleCreateRequestDto.kt} (69%) rename frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/{secret/SecretCapsuleCreateRequest.kt => common/CapsuleCreateRequest.kt} (66%) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/secret/request/SecretCapsuleCreateRequestDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/common/CapsuleCreateRequestDto.kt similarity index 69% rename from frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/secret/request/SecretCapsuleCreateRequestDto.kt rename to frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/common/CapsuleCreateRequestDto.kt index 7edd1bc6e..d19dd010a 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/secret/request/SecretCapsuleCreateRequestDto.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/common/CapsuleCreateRequestDto.kt @@ -1,9 +1,8 @@ -package com.droidblossom.archive.data.dto.secret.request +package com.droidblossom.archive.data.dto.common import com.droidblossom.archive.data.dto.capsule.response.AddressDataDto -import com.droidblossom.archive.data.dto.common.FileNameDto -data class SecretCapsuleCreateRequestDto( +data class CapsuleCreateRequestDto( val capsuleSkinId: Long, val content: String, val directory: String, diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/SecretRepositoryImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/SecretRepositoryImpl.kt index 36873b06b..fef2933c4 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/SecretRepositoryImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/SecretRepositoryImpl.kt @@ -2,10 +2,9 @@ package com.droidblossom.archive.data.repository import com.droidblossom.archive.data.dto.ResponseBody import com.droidblossom.archive.data.dto.common.toModel -import com.droidblossom.archive.data.dto.secret.request.SecretCapsuleCreateRequestDto +import com.droidblossom.archive.data.dto.common.CapsuleCreateRequestDto import com.droidblossom.archive.data.dto.secret.request.SecretCapsuleModifyRequestDto import com.droidblossom.archive.data.dto.secret.request.SecretCapsulePageRequestDto -import com.droidblossom.archive.data.dto.secret.response.SecretCapsuleCreateResponseDto import com.droidblossom.archive.data.dto.secret.response.SecretCapsuleDetailResponseDto import com.droidblossom.archive.data.dto.secret.response.SecretCapsuleModifyResponseDto import com.droidblossom.archive.data.dto.secret.response.SecretCapsulePageResponseDto @@ -28,7 +27,7 @@ class SecretRepositoryImpl @Inject constructor( return apiHandler({ api.getSecretCapsulePageApi(request.size, request.createdAt) }) { response: ResponseBody -> response.result.toModel() } } - override suspend fun createSecretCapsule(request: SecretCapsuleCreateRequestDto): RetrofitResult { + override suspend fun createSecretCapsule(request: CapsuleCreateRequestDto): RetrofitResult { return apiHandler({ api.postSecretCapsuleApi(request) }) { response: ResponseBody -> response.result.toModel() } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/SecretService.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/SecretService.kt index 19b3b115d..9184d94dc 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/SecretService.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/SecretService.kt @@ -1,10 +1,8 @@ package com.droidblossom.archive.data.source.remote.api import com.droidblossom.archive.data.dto.ResponseBody -import com.droidblossom.archive.data.dto.secret.request.SecretCapsuleCreateRequestDto +import com.droidblossom.archive.data.dto.common.CapsuleCreateRequestDto import com.droidblossom.archive.data.dto.secret.request.SecretCapsuleModifyRequestDto -import com.droidblossom.archive.data.dto.secret.request.SecretCapsulePageRequestDto -import com.droidblossom.archive.data.dto.secret.response.SecretCapsuleCreateResponseDto import com.droidblossom.archive.data.dto.secret.response.SecretCapsuleDetailResponseDto import com.droidblossom.archive.data.dto.secret.response.SecretCapsuleModifyResponseDto import com.droidblossom.archive.data.dto.secret.response.SecretCapsulePageResponseDto @@ -27,7 +25,7 @@ interface SecretService { @POST("capsules/secret") suspend fun postSecretCapsuleApi( - @Body request : SecretCapsuleCreateRequestDto + @Body request : CapsuleCreateRequestDto ) : Response> @GET("secret/capsules/{capsule_id}/detail") diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/secret/SecretCapsuleCreateRequest.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/common/CapsuleCreateRequest.kt similarity index 66% rename from frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/secret/SecretCapsuleCreateRequest.kt rename to frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/common/CapsuleCreateRequest.kt index 3274632f4..81fa6f1e7 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/secret/SecretCapsuleCreateRequest.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/common/CapsuleCreateRequest.kt @@ -1,10 +1,8 @@ -package com.droidblossom.archive.domain.model.secret +package com.droidblossom.archive.domain.model.common -import com.droidblossom.archive.data.dto.secret.request.SecretCapsuleCreateRequestDto -import com.droidblossom.archive.domain.model.common.AddressData -import com.droidblossom.archive.domain.model.common.FileName +import com.droidblossom.archive.data.dto.common.CapsuleCreateRequestDto -data class SecretCapsuleCreateRequest( +data class CapsuleCreateRequest( val capsuleSkinId: Long, val content: String, val directory: String, @@ -16,7 +14,7 @@ data class SecretCapsuleCreateRequest( val longitude: Double, val title: String ) { - fun toDto() = SecretCapsuleCreateRequestDto( + fun toDto() = CapsuleCreateRequestDto( capsuleSkinId = this.capsuleSkinId, content = this.content, directory = this.directory, diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/SecretRepository.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/SecretRepository.kt index 8fcb0c6d0..6c6b1bbac 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/SecretRepository.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/SecretRepository.kt @@ -1,6 +1,6 @@ package com.droidblossom.archive.domain.repository -import com.droidblossom.archive.data.dto.secret.request.SecretCapsuleCreateRequestDto +import com.droidblossom.archive.data.dto.common.CapsuleCreateRequestDto import com.droidblossom.archive.data.dto.secret.request.SecretCapsuleModifyRequestDto import com.droidblossom.archive.data.dto.secret.request.SecretCapsulePageRequestDto import com.droidblossom.archive.domain.model.secret.SecretCapsuleDetail @@ -13,7 +13,7 @@ interface SecretRepository { suspend fun getSecretCapsulePage (request: SecretCapsulePageRequestDto) : RetrofitResult - suspend fun createSecretCapsule (request: SecretCapsuleCreateRequestDto) : RetrofitResult + suspend fun createSecretCapsule (request: CapsuleCreateRequestDto) : RetrofitResult suspend fun getSecretCapsuleDetail (capsuleId: Long) : RetrofitResult diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/secret/SecretCapsuleCreateUseCase.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/secret/SecretCapsuleCreateUseCase.kt index 0e9ff9f6e..a60c6ff35 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/secret/SecretCapsuleCreateUseCase.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/secret/SecretCapsuleCreateUseCase.kt @@ -1,9 +1,7 @@ package com.droidblossom.archive.domain.usecase.secret import android.util.Log -import com.droidblossom.archive.data.dto.secret.request.SecretCapsuleCreateRequestDto -import com.droidblossom.archive.domain.model.secret.SecretCapsuleCreateRequest -import com.droidblossom.archive.domain.model.secret.SecretCapsulePage +import com.droidblossom.archive.domain.model.common.CapsuleCreateRequest import com.droidblossom.archive.domain.repository.SecretRepository import com.droidblossom.archive.util.RetrofitResult import com.droidblossom.archive.util.onException @@ -15,7 +13,7 @@ import javax.inject.Inject class SecretCapsuleCreateUseCase @Inject constructor( private val repository: SecretRepository ) { - suspend operator fun invoke(request: SecretCapsuleCreateRequest) = + suspend operator fun invoke(request: CapsuleCreateRequest) = flow> { try { emit(repository.createSecretCapsule(request.toDto()).onSuccess { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt index 649a972fc..e708efa3a 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt @@ -9,27 +9,20 @@ import com.droidblossom.archive.domain.model.common.CapsuleSkinSummary import com.droidblossom.archive.domain.model.common.Dummy import com.droidblossom.archive.domain.model.common.FileName import com.droidblossom.archive.domain.model.common.Location -import com.droidblossom.archive.domain.model.common.Skin import com.droidblossom.archive.domain.model.s3.S3UrlRequest -import com.droidblossom.archive.domain.model.secret.SecretCapsuleCreateRequest +import com.droidblossom.archive.domain.model.common.CapsuleCreateRequest import com.droidblossom.archive.domain.usecase.capsule.GetAddressUseCase import com.droidblossom.archive.domain.usecase.capsule_skin.CapsuleSkinsPageUseCase -import com.droidblossom.archive.domain.usecase.kakao.ToAddressUseCase import com.droidblossom.archive.domain.usecase.s3.S3UrlsGetUseCase import com.droidblossom.archive.domain.usecase.secret.SecretCapsuleCreateUseCase import com.droidblossom.archive.presentation.base.BaseViewModel -import com.droidblossom.archive.presentation.ui.home.createcapsule.CreateCapsuleViewModelImpl.Companion.S3DIRECTORY import com.droidblossom.archive.util.DateUtils -import com.droidblossom.archive.util.FileUtils import com.droidblossom.archive.util.S3Util -import com.droidblossom.archive.util.onError -import com.droidblossom.archive.util.onException import com.droidblossom.archive.util.onFail import com.droidblossom.archive.util.onSuccess import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.flow.MutableSharedFlow @@ -37,7 +30,6 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.joinAll import kotlinx.coroutines.launch import java.io.File import javax.inject.Inject @@ -536,7 +528,7 @@ class CreateCapsuleViewModelImpl @Inject constructor( CreateCapsuleViewModel.CapsuleTypeCreate.SECRET -> { secretCapsuleCreateUseCase( - SecretCapsuleCreateRequest( + CapsuleCreateRequest( capsuleSkinId = skinId.value, content = capsuleContent.value, directory = S3DIRECTORY, From 59b52d256509156fcd6c30d481b5fc425709890c Mon Sep 17 00:00:00 2001 From: comst19 Date: Wed, 3 Apr 2024 16:29:46 +0900 Subject: [PATCH 134/301] rename: SecretCapsuleSummary(model, dto) -> CapsuleSummary(model, dto) --- .../CapsuleSummaryResponseDto.kt} | 8 ++++---- .../archive/data/repository/SecretRepositoryImpl.kt | 8 ++++---- .../archive/data/source/remote/api/SecretService.kt | 4 ++-- .../CapsuleSummaryResponse.kt} | 4 ++-- .../archive/domain/repository/SecretRepository.kt | 4 ++-- .../ui/home/dialog/CapsulePreviewDialogViewModel.kt | 4 ++-- .../home/dialog/CapsulePreviewDialogViewModelImpl.kt | 12 ++++++------ .../res/layout/fragment_capsule_preview_dialog.xml | 10 +++++----- 8 files changed, 27 insertions(+), 27 deletions(-) rename frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/{secret/response/SecretCapsuleSummaryResponseDto.kt => common/CapsuleSummaryResponseDto.kt} (71%) rename frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/{secret/SecretCapsuleSummary.kt => common/CapsuleSummaryResponse.kt} (72%) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/secret/response/SecretCapsuleSummaryResponseDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/common/CapsuleSummaryResponseDto.kt similarity index 71% rename from frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/secret/response/SecretCapsuleSummaryResponseDto.kt rename to frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/common/CapsuleSummaryResponseDto.kt index 49347289e..bc8ec65c4 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/secret/response/SecretCapsuleSummaryResponseDto.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/common/CapsuleSummaryResponseDto.kt @@ -1,8 +1,8 @@ -package com.droidblossom.archive.data.dto.secret.response +package com.droidblossom.archive.data.dto.common -import com.droidblossom.archive.domain.model.secret.SecretCapsuleSummary +import com.droidblossom.archive.domain.model.common.CapsuleSummaryResponse -data class SecretCapsuleSummaryResponseDto( +data class CapsuleSummaryResponseDto( val nickname: String, val profileUrl: String, val skinUrl: String, @@ -13,7 +13,7 @@ data class SecretCapsuleSummaryResponseDto( val isOpened: Boolean, val createdAt: String ){ - fun toModel() = SecretCapsuleSummary( + fun toModel() = CapsuleSummaryResponse( nickname = this.nickname, profileUrl = this.profileUrl, skinUrl = this.skinUrl, diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/SecretRepositoryImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/SecretRepositoryImpl.kt index fef2933c4..19d6107bd 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/SecretRepositoryImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/SecretRepositoryImpl.kt @@ -8,12 +8,12 @@ import com.droidblossom.archive.data.dto.secret.request.SecretCapsulePageRequest import com.droidblossom.archive.data.dto.secret.response.SecretCapsuleDetailResponseDto import com.droidblossom.archive.data.dto.secret.response.SecretCapsuleModifyResponseDto import com.droidblossom.archive.data.dto.secret.response.SecretCapsulePageResponseDto -import com.droidblossom.archive.data.dto.secret.response.SecretCapsuleSummaryResponseDto +import com.droidblossom.archive.data.dto.common.CapsuleSummaryResponseDto import com.droidblossom.archive.data.source.remote.api.SecretService import com.droidblossom.archive.domain.model.secret.SecretCapsuleDetail import com.droidblossom.archive.domain.model.secret.SecretCapsuleModify import com.droidblossom.archive.domain.model.secret.SecretCapsulePage -import com.droidblossom.archive.domain.model.secret.SecretCapsuleSummary +import com.droidblossom.archive.domain.model.common.CapsuleSummaryResponse import com.droidblossom.archive.domain.repository.SecretRepository import com.droidblossom.archive.util.RetrofitResult import com.droidblossom.archive.util.apiHandler @@ -35,8 +35,8 @@ class SecretRepositoryImpl @Inject constructor( return apiHandler({ api.getSecretCapsuleDetailApi(capsuleId) }) { response: ResponseBody -> response.result.toModel() } } - override suspend fun getSecretCapsuleSummary (capsuleId: Int) : RetrofitResult { - return apiHandler({ api.getSecretCapsuleSummaryApi(capsuleId) }) { response: ResponseBody -> response.result.toModel()} + override suspend fun getSecretCapsuleSummary (capsuleId: Int) : RetrofitResult { + return apiHandler({ api.getSecretCapsuleSummaryApi(capsuleId) }) { response: ResponseBody -> response.result.toModel()} } override suspend fun modifySecretCapsule( diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/SecretService.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/SecretService.kt index 9184d94dc..e24fdbfd3 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/SecretService.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/SecretService.kt @@ -6,7 +6,7 @@ import com.droidblossom.archive.data.dto.secret.request.SecretCapsuleModifyReque import com.droidblossom.archive.data.dto.secret.response.SecretCapsuleDetailResponseDto import com.droidblossom.archive.data.dto.secret.response.SecretCapsuleModifyResponseDto import com.droidblossom.archive.data.dto.secret.response.SecretCapsulePageResponseDto -import com.droidblossom.archive.data.dto.secret.response.SecretCapsuleSummaryResponseDto +import com.droidblossom.archive.data.dto.common.CapsuleSummaryResponseDto import retrofit2.Response import retrofit2.http.Body import retrofit2.http.GET @@ -36,7 +36,7 @@ interface SecretService { @GET("secret/capsules/{capsule_id}/summary") suspend fun getSecretCapsuleSummaryApi( @Path("capsule_id") capsuleId : Int, - ) : Response> + ) : Response> //미정 (1/27 기준) @PATCH("secret/capsules/{capsule_id}") diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/secret/SecretCapsuleSummary.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/common/CapsuleSummaryResponse.kt similarity index 72% rename from frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/secret/SecretCapsuleSummary.kt rename to frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/common/CapsuleSummaryResponse.kt index a2183c694..eec832e69 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/secret/SecretCapsuleSummary.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/common/CapsuleSummaryResponse.kt @@ -1,6 +1,6 @@ -package com.droidblossom.archive.domain.model.secret +package com.droidblossom.archive.domain.model.common -data class SecretCapsuleSummary( +data class CapsuleSummaryResponse( val nickname: String, val profileUrl: String, val skinUrl: String, diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/SecretRepository.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/SecretRepository.kt index 6c6b1bbac..2f075c783 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/SecretRepository.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/SecretRepository.kt @@ -6,7 +6,7 @@ import com.droidblossom.archive.data.dto.secret.request.SecretCapsulePageRequest import com.droidblossom.archive.domain.model.secret.SecretCapsuleDetail import com.droidblossom.archive.domain.model.secret.SecretCapsuleModify import com.droidblossom.archive.domain.model.secret.SecretCapsulePage -import com.droidblossom.archive.domain.model.secret.SecretCapsuleSummary +import com.droidblossom.archive.domain.model.common.CapsuleSummaryResponse import com.droidblossom.archive.util.RetrofitResult interface SecretRepository { @@ -17,6 +17,6 @@ interface SecretRepository { suspend fun getSecretCapsuleDetail (capsuleId: Long) : RetrofitResult - suspend fun getSecretCapsuleSummary (capsuleId: Int) : RetrofitResult + suspend fun getSecretCapsuleSummary (capsuleId: Int) : RetrofitResult suspend fun modifySecretCapsule (capsuleId: Int, request: SecretCapsuleModifyRequestDto) : RetrofitResult } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogViewModel.kt index e4de8b46d..439eed4e9 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogViewModel.kt @@ -1,6 +1,6 @@ package com.droidblossom.archive.presentation.ui.home.dialog -import com.droidblossom.archive.domain.model.secret.SecretCapsuleSummary +import com.droidblossom.archive.domain.model.common.CapsuleSummaryResponse import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow import java.util.Calendar @@ -8,7 +8,7 @@ import java.util.Calendar interface CapsulePreviewDialogViewModel { val capsulePreviewDialogEvents: SharedFlow - val secretCapsuleSummary : StateFlow + val capsuleSummaryResponse : StateFlow val startTime: StateFlow val endTime: StateFlow val totalTime: StateFlow diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogViewModelImpl.kt index f1658b5c9..23ab5fd28 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogViewModelImpl.kt @@ -2,7 +2,7 @@ package com.droidblossom.archive.presentation.ui.home.dialog import android.util.Log import androidx.lifecycle.viewModelScope -import com.droidblossom.archive.domain.model.secret.SecretCapsuleSummary +import com.droidblossom.archive.domain.model.common.CapsuleSummaryResponse import com.droidblossom.archive.domain.usecase.capsule.PatchCapsuleOpenedUseCase import com.droidblossom.archive.domain.usecase.secret.SecretCapsuleSummaryUseCase import com.droidblossom.archive.presentation.base.BaseViewModel @@ -34,9 +34,9 @@ class CapsulePreviewDialogViewModelImpl @Inject constructor( private val _capsulePreviewDialogEvents = MutableSharedFlow() override val capsulePreviewDialogEvents: SharedFlow = _capsulePreviewDialogEvents.asSharedFlow() - private val _secretCapsuleSummary = MutableStateFlow(SecretCapsuleSummary("","","","","","","", false, "")) - override val secretCapsuleSummary: StateFlow - get() = _secretCapsuleSummary + private val _CapsuleSummaryResponse = MutableStateFlow(CapsuleSummaryResponse("","","","","","","", false, "")) + override val capsuleSummaryResponse: StateFlow + get() = _CapsuleSummaryResponse private val _startTime = MutableStateFlow(null) private val _endTime = MutableStateFlow(null) @@ -88,8 +88,8 @@ class CapsulePreviewDialogViewModelImpl @Inject constructor( viewModelScope.launch { secretCapsuleSummaryUseCase(capsuleId).collect { result -> result.onSuccess { - _secretCapsuleSummary.emit(it) - _capsuleOpenState.emit(secretCapsuleSummary.value.isOpened) + _CapsuleSummaryResponse.emit(it) + _capsuleOpenState.emit(capsuleSummaryResponse.value.isOpened) if (!capsuleOpenState.value){ calculateCapsuleOpenTime(it.createdAt, it.dueDate) } diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_capsule_preview_dialog.xml b/frontend/ARchive/app/src/main/res/layout/fragment_capsule_preview_dialog.xml index cfc55228c..e7d134b92 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_capsule_preview_dialog.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_capsule_preview_dialog.xml @@ -53,7 +53,7 @@ android:id="@+id/capsuleTitleTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="@{vm.secretCapsuleSummary.title}" + android:text="@{vm.capsuleSummaryResponse.title}" android:textAppearance="@style/TextAppearance.App.h2" android:textColor="@color/white" app:layout_constraintEnd_toEndOf="parent" @@ -66,7 +66,7 @@ android:layout_height="wrap_content" android:drawableStart="@drawable/ic_group_chip" android:drawablePadding="@dimen/padding_small" - bind:displayCreationDateFormatted="@{vm.secretCapsuleSummary.createdAt}" + bind:displayCreationDateFormatted="@{vm.capsuleSummaryResponse.createdAt}" android:textAppearance="@style/TextAppearance.App.caption4" android:textColor="@color/white" app:layout_constraintEnd_toStartOf="@id/capsuleLocationTextView" @@ -79,7 +79,7 @@ android:layout_height="wrap_content" android:drawableStart="@drawable/ic_group_chip" android:drawablePadding="@dimen/padding_small" - android:text="@{vm.secretCapsuleSummary.roadName.empty ? vm.secretCapsuleSummary.address : vm.secretCapsuleSummary.roadName}" + android:text="@{vm.capsuleSummaryResponse.roadName.empty ? vm.capsuleSummaryResponse.address : vm.capsuleSummaryResponse.roadName}" android:textAppearance="@style/TextAppearance.App.caption4" android:textColor="@color/white" app:layout_constraintEnd_toEndOf="parent" @@ -113,7 +113,7 @@ app:civ_circle_background_color="@color/white" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" - bind:imageUrl="@{vm.secretCapsuleSummary.profileUrl}" + bind:imageUrl="@{vm.capsuleSummaryResponse.profileUrl}" bind:placeholder="@{@drawable/app_symbol}"/> + bind:url="@{vm.capsuleSummaryResponse.skinUrl}" /> From e8ea522167c839315f8cc71e242b8acbbcbbd2a1 Mon Sep 17 00:00:00 2001 From: comst19 Date: Wed, 3 Apr 2024 16:44:33 +0900 Subject: [PATCH 135/301] =?UTF-8?q?feat:=20PublicService=20=EC=84=A0?= =?UTF-8?q?=EC=96=B8=20-=20=EA=B3=B5=EA=B0=9C=20=EC=BA=A1=EC=8A=90=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1,=20=EC=9A=94=EC=95=BD=20=EC=A1=B0=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/source/remote/api/PublicService.kt | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/PublicService.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/PublicService.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/PublicService.kt new file mode 100644 index 000000000..a807647bd --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/PublicService.kt @@ -0,0 +1,25 @@ +package com.droidblossom.archive.data.source.remote.api + +import com.droidblossom.archive.data.dto.ResponseBody +import com.droidblossom.archive.data.dto.common.CapsuleCreateRequestDto +import com.droidblossom.archive.data.dto.common.CapsuleSummaryResponseDto +import retrofit2.Response +import retrofit2.http.Body +import retrofit2.http.GET +import retrofit2.http.POST +import retrofit2.http.Path + +interface PublicService { + + @POST("capsules/public") + suspend fun postPublicCapsuleApi( + @Body request : CapsuleCreateRequestDto + ) : Response> + + @GET("public/capsules/{capsule_id}/summary") + suspend fun getPublicCapsuleSummaryApi( + @Path("capsule_id") capsuleId : Int, + ) : Response> + + +} \ No newline at end of file From 6e0d788c301b32ac73ea4ce56f53769763914113 Mon Sep 17 00:00:00 2001 From: comst19 Date: Wed, 3 Apr 2024 16:45:42 +0900 Subject: [PATCH 136/301] =?UTF-8?q?feat:=20PublicRepository=20=EC=84=A0?= =?UTF-8?q?=EC=96=B8=20-=20=EA=B3=B5=EA=B0=9C=20=EC=BA=A1=EC=8A=90=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1,=20=EC=9A=94=EC=95=BD=20=EC=A1=B0=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/repository/PublicRepositoryImpl.kt | 24 +++++++++++++++++++ .../domain/repository/PublicRepository.kt | 13 ++++++++++ 2 files changed, 37 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/PublicRepositoryImpl.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/PublicRepository.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/PublicRepositoryImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/PublicRepositoryImpl.kt new file mode 100644 index 000000000..1d597b6e3 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/PublicRepositoryImpl.kt @@ -0,0 +1,24 @@ +package com.droidblossom.archive.data.repository + +import com.droidblossom.archive.data.dto.ResponseBody +import com.droidblossom.archive.data.dto.common.CapsuleCreateRequestDto +import com.droidblossom.archive.data.dto.common.CapsuleSummaryResponseDto +import com.droidblossom.archive.data.dto.common.toModel +import com.droidblossom.archive.data.source.remote.api.PublicService +import com.droidblossom.archive.domain.model.common.CapsuleSummaryResponse +import com.droidblossom.archive.domain.repository.PublicRepository +import com.droidblossom.archive.util.RetrofitResult +import com.droidblossom.archive.util.apiHandler +import javax.inject.Inject + +class PublicRepositoryImpl @Inject constructor( + private val api: PublicService +): PublicRepository { + override suspend fun createPublicCapsule(request: CapsuleCreateRequestDto): RetrofitResult { + return apiHandler({ api.postPublicCapsuleApi(request) }) { response: ResponseBody -> response.result.toModel() } + } + + override suspend fun getPublicCapsuleSummary(capsuleId: Int): RetrofitResult { + return apiHandler({ api.getPublicCapsuleSummaryApi(capsuleId) }) { response: ResponseBody -> response.result.toModel()} + } +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/PublicRepository.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/PublicRepository.kt new file mode 100644 index 000000000..a86e5ba41 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/PublicRepository.kt @@ -0,0 +1,13 @@ +package com.droidblossom.archive.domain.repository + +import com.droidblossom.archive.data.dto.common.CapsuleCreateRequestDto +import com.droidblossom.archive.domain.model.common.CapsuleSummaryResponse +import com.droidblossom.archive.util.RetrofitResult + +interface PublicRepository { + + suspend fun createPublicCapsule (request: CapsuleCreateRequestDto) : RetrofitResult + + suspend fun getPublicCapsuleSummary (capsuleId: Int) : RetrofitResult + +} \ No newline at end of file From fbf602b124a4c4347f3c7b48c53659db9542dd5f Mon Sep 17 00:00:00 2001 From: comst19 Date: Wed, 3 Apr 2024 16:46:18 +0900 Subject: [PATCH 137/301] feat: PublicCapsuleCreateUseCase --- .../open/PublicCapsuleCreateUseCase.kt | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/open/PublicCapsuleCreateUseCase.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/open/PublicCapsuleCreateUseCase.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/open/PublicCapsuleCreateUseCase.kt new file mode 100644 index 000000000..02e2df640 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/open/PublicCapsuleCreateUseCase.kt @@ -0,0 +1,31 @@ +package com.droidblossom.archive.domain.usecase.open + +import android.util.Log +import com.droidblossom.archive.domain.model.common.CapsuleCreateRequest +import com.droidblossom.archive.domain.repository.PublicRepository +import com.droidblossom.archive.util.RetrofitResult +import com.droidblossom.archive.util.onException +import com.droidblossom.archive.util.onFail +import com.droidblossom.archive.util.onSuccess +import kotlinx.coroutines.flow.flow +import javax.inject.Inject + +class PublicCapsuleCreateUseCase @Inject constructor( + private val repository: PublicRepository +) { + suspend operator fun invoke(request: CapsuleCreateRequest) = + flow> { + try { + emit(repository.createPublicCapsule(request.toDto()).onSuccess { + + }.onFail { + + }.onException { + throw Exception(it) + }) + } catch (e: Exception) { + Log.d("예외확인", "$e") + e.printStackTrace() + } + } +} \ No newline at end of file From e83ddcea13b71c2e54e8892812ba141bd01932d0 Mon Sep 17 00:00:00 2001 From: comst19 Date: Wed, 3 Apr 2024 16:46:48 +0900 Subject: [PATCH 138/301] feat: PublicSummaryUseCase --- .../open/PublicCapsuleSummaryUseCase.kt | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/open/PublicCapsuleSummaryUseCase.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/open/PublicCapsuleSummaryUseCase.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/open/PublicCapsuleSummaryUseCase.kt new file mode 100644 index 000000000..40a0034fc --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/open/PublicCapsuleSummaryUseCase.kt @@ -0,0 +1,29 @@ +package com.droidblossom.archive.domain.usecase.open + +import android.util.Log +import com.droidblossom.archive.domain.repository.PublicRepository +import com.droidblossom.archive.util.onException +import com.droidblossom.archive.util.onFail +import com.droidblossom.archive.util.onSuccess +import kotlinx.coroutines.flow.flow +import javax.inject.Inject + +class PublicCapsuleSummaryUseCase @Inject constructor( + private val repository: PublicRepository +) { + suspend operator fun invoke(capsuleId :Int) = + flow { + try { + emit(repository.getPublicCapsuleSummary(capsuleId).onSuccess { + + }.onFail { + + }.onException { + throw Exception(it) + }) + } catch (e: Exception) { + Log.d("예외확인", "$e") + e.printStackTrace() + } + } +} \ No newline at end of file From 5f2791c078a3d4d7342aaa60d7e6990b524f888d Mon Sep 17 00:00:00 2001 From: comst19 Date: Wed, 3 Apr 2024 16:47:30 +0900 Subject: [PATCH 139/301] =?UTF-8?q?feat:=20PublicService,=20PublicReposito?= =?UTF-8?q?ry=EC=97=90=20=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=A3=BC=EC=9E=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/droidblossom/archive/di/RepositoryModule.kt | 7 +++++++ .../main/java/com/droidblossom/archive/di/ServiceModule.kt | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/di/RepositoryModule.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/di/RepositoryModule.kt index b650a97ea..f84ad067b 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/di/RepositoryModule.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/di/RepositoryModule.kt @@ -6,6 +6,7 @@ import com.droidblossom.archive.data.repository.CapsuleSkinRepositoryImpl import com.droidblossom.archive.data.repository.FriendRepositoryImpl import com.droidblossom.archive.data.repository.MemberRepositoryImpl import com.droidblossom.archive.data.repository.KakaoRepositoryImpl +import com.droidblossom.archive.data.repository.PublicRepositoryImpl import com.droidblossom.archive.data.repository.S3RepositoryImpl import com.droidblossom.archive.data.repository.SecretRepositoryImpl import com.droidblossom.archive.data.source.remote.api.AuthService @@ -14,6 +15,7 @@ import com.droidblossom.archive.data.source.remote.api.CapsuleSkinService import com.droidblossom.archive.data.source.remote.api.FriendService import com.droidblossom.archive.data.source.remote.api.MemberService import com.droidblossom.archive.data.source.remote.api.KakaoService +import com.droidblossom.archive.data.source.remote.api.PublicService import com.droidblossom.archive.data.source.remote.api.S3Service import com.droidblossom.archive.data.source.remote.api.SecretService import com.droidblossom.archive.domain.repository.AuthRepository @@ -22,6 +24,7 @@ import com.droidblossom.archive.domain.repository.CapsuleSkinRepository import com.droidblossom.archive.domain.repository.FriendRepository import com.droidblossom.archive.domain.repository.MemberRepository import com.droidblossom.archive.domain.repository.KakaoRepository +import com.droidblossom.archive.domain.repository.PublicRepository import com.droidblossom.archive.domain.repository.S3Repository import com.droidblossom.archive.domain.repository.SecretRepository import dagger.Module @@ -71,4 +74,8 @@ object RepositoryModule { @ViewModelScoped fun providesFriendRepository(api : FriendService) : FriendRepository = FriendRepositoryImpl(api) + @Provides + @ViewModelScoped + fun providesPublicRepository(api : PublicService) : PublicRepository = PublicRepositoryImpl(api) + } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/di/ServiceModule.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/di/ServiceModule.kt index 8ca410362..7f0cbc53a 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/di/ServiceModule.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/di/ServiceModule.kt @@ -5,6 +5,7 @@ import com.droidblossom.archive.data.source.remote.api.CapsuleService import com.droidblossom.archive.data.source.remote.api.CapsuleSkinService import com.droidblossom.archive.data.source.remote.api.FriendService import com.droidblossom.archive.data.source.remote.api.MemberService +import com.droidblossom.archive.data.source.remote.api.PublicService import com.droidblossom.archive.data.source.remote.api.S3Service import com.droidblossom.archive.data.source.remote.api.SecretService import dagger.Module @@ -46,4 +47,8 @@ object ServiceModule { @Singleton @Provides fun providesFriendService(retrofit : Retrofit) : FriendService = retrofit.create(FriendService::class.java) + + @Singleton + @Provides + fun providesPublicService(retrofit: Retrofit) : PublicService = retrofit.create(PublicService::class.java) } \ No newline at end of file From ccd2b6e55ee18d5175fe4858d4a73b633d719158 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Wed, 3 Apr 2024 19:04:11 +0900 Subject: [PATCH 140/301] =?UTF-8?q?fix=20:=20=EC=B9=9C=EA=B5=AC=EC=9A=94?= =?UTF-8?q?=EC=B2=AD=20api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/dto/friend/request/FriendAcceptRequestDto.kt | 2 +- .../data/dto/friend/request/FriendReqRequestDto.kt | 2 +- .../data/dto/friend/response/FriendsSearchResponseDto.kt | 1 + .../archive/data/source/remote/api/FriendService.kt | 8 ++++---- .../archive/domain/model/friend/FriendAcceptRequest.kt | 2 +- .../archive/domain/model/friend/FriendReqRequest.kt | 2 +- 6 files changed, 9 insertions(+), 8 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/request/FriendAcceptRequestDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/request/FriendAcceptRequestDto.kt index dd874ff77..a3de310a6 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/request/FriendAcceptRequestDto.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/request/FriendAcceptRequestDto.kt @@ -1,5 +1,5 @@ package com.droidblossom.archive.data.dto.friend.request data class FriendAcceptRequestDto ( - val friendId : Double + val friendId : Long ) \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/request/FriendReqRequestDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/request/FriendReqRequestDto.kt index 07e5bf9a1..6b8fc0669 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/request/FriendReqRequestDto.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/request/FriendReqRequestDto.kt @@ -1,5 +1,5 @@ package com.droidblossom.archive.data.dto.friend.request data class FriendReqRequestDto( - val friendId : Double + val friendId : Long ) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendsSearchResponseDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendsSearchResponseDto.kt index e6124d942..cff2e9914 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendsSearchResponseDto.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendsSearchResponseDto.kt @@ -15,5 +15,6 @@ data class FriendsSearchResponseDto( profileUrl = this.profileUrl, nickname = this.nickname, isFriend = this.isFriend, + isChecked = false ) } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt index 9a957bcb5..06c7e3a7f 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt @@ -17,14 +17,14 @@ import retrofit2.http.Query interface FriendService { - @POST("friends/{friend_id/request}") + @POST("friends/{friend_id}/request") suspend fun postFriendsRequestApi( - @Path("friend_id") friendId : Double, + @Path("friend_id") friendId : Long, ) : Response> - @POST("friends/{friend_id/accept-request}") + @POST("friends/{friend_id}/accept-request") suspend fun postFriendsAcceptRequestApi( - @Path("friend_id") friendId : Double, + @Path("friend_id") friendId : Long, ) : Response> @GET("friends/search") diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendAcceptRequest.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendAcceptRequest.kt index 7d39dec71..e04c42203 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendAcceptRequest.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendAcceptRequest.kt @@ -4,7 +4,7 @@ import com.droidblossom.archive.data.dto.friend.request.FriendAcceptRequestDto data class FriendAcceptRequest ( - val friendId : Double + val friendId : Long ){ fun toDto() = FriendAcceptRequestDto( friendId = this.friendId, diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendReqRequest.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendReqRequest.kt index 9c5b535f0..f98361009 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendReqRequest.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendReqRequest.kt @@ -3,7 +3,7 @@ package com.droidblossom.archive.domain.model.friend import com.droidblossom.archive.data.dto.friend.request.FriendReqRequestDto data class FriendReqRequest( - val friendId : Double + val friendId : Long ){ fun toDto() = FriendReqRequestDto( friendId = this.friendId, From 7765dac826dbe9402be220110a4cb42c610c03ba Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Wed, 3 Apr 2024 19:04:38 +0900 Subject: [PATCH 141/301] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20RVA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../friend/addfriend/adapter/AddFriendRVA.kt | 59 +++++++++++++++++++ .../src/main/res/layout/item_add_friend.xml | 16 ++--- 2 files changed, 68 insertions(+), 7 deletions(-) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/adapter/AddFriendRVA.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/adapter/AddFriendRVA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/adapter/AddFriendRVA.kt new file mode 100644 index 000000000..c27b0226c --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/adapter/AddFriendRVA.kt @@ -0,0 +1,59 @@ +package com.droidblossom.archive.presentation.ui.mypage.friend.addfriend.adapter + +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.droidblossom.archive.databinding.ItemAddFriendBinding +import com.droidblossom.archive.domain.model.friend.FriendsSearchResponse + +class AddFriendRVA(private val check: (Int) -> Unit) : + ListAdapter(differ) { + + inner class ItemViewHolder( + private val binding: ItemAddFriendBinding + ) : RecyclerView.ViewHolder(binding.root) { + + fun bind(data: FriendsSearchResponse) { + binding.item = data + binding.root.setOnClickListener { + check(bindingAdapterPosition) + notifyItemChanged(bindingAdapterPosition) + } + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder { + return ItemViewHolder( + ItemAddFriendBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) + } + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + holder.bind(getItem(position)) + } + + companion object { + val differ = object : DiffUtil.ItemCallback() { + override fun areItemsTheSame( + oldItem: FriendsSearchResponse, + newItem: FriendsSearchResponse + ): Boolean { + return oldItem.id == newItem.id + } + + override fun areContentsTheSame( + oldItem: FriendsSearchResponse, + newItem: FriendsSearchResponse + ): Boolean { + return oldItem == newItem + } + + } + } +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/item_add_friend.xml b/frontend/ARchive/app/src/main/res/layout/item_add_friend.xml index d884c482d..4213d679b 100644 --- a/frontend/ARchive/app/src/main/res/layout/item_add_friend.xml +++ b/frontend/ARchive/app/src/main/res/layout/item_add_friend.xml @@ -10,7 +10,7 @@ + type="com.droidblossom.archive.domain.model.friend.FriendsSearchResponse" /> @@ -31,7 +31,7 @@ app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" - app:strokeColor="@{item.isClicked ? @color/main_2 : @color/white}" + app:strokeColor="@{item.checked ? @color/main_2 : @color/white}" app:strokeWidth="3dp"> + app:layout_constraintTop_toTopOf="parent" + bind:baseImg="@{@drawable/base_use_img}" + bind:url="@{item.profileUrl}" /> + android:src="@{item.checked ? @drawable/ic_check_24 : @drawable/ic_plus_small_24}" /> From 693f864ccf84eae151a304142661521cb6bc1249 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Wed, 3 Apr 2024 19:06:10 +0900 Subject: [PATCH 142/301] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC=20name=20?= =?UTF-8?q?=EA=B2=80=EC=83=89=20&=20=EC=B9=9C=EA=B5=AC=20=EC=9A=94?= =?UTF-8?q?=EC=B2=AD=20usecase=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/home/createcapsule/adapter/SkinRVA.kt | 3 - .../friend/addfriend/AddFriendViewModel.kt | 20 ++++- .../addfriend/AddFriendViewModelImpl.kt | 87 ++++++++++++++++++- .../fragment_friend_search_nickname.xml | 4 + 4 files changed, 106 insertions(+), 8 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/adapter/SkinRVA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/adapter/SkinRVA.kt index eb1f978b5..ca0cdf6e4 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/adapter/SkinRVA.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/adapter/SkinRVA.kt @@ -1,15 +1,12 @@ package com.droidblossom.archive.presentation.ui.home.createcapsule.adapter -import android.annotation.SuppressLint import android.view.LayoutInflater import android.view.ViewGroup -import androidx.datastore.preferences.core.preferencesOf import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView import com.droidblossom.archive.databinding.ItemSkinBinding import com.droidblossom.archive.domain.model.common.CapsuleSkinSummary -import com.droidblossom.archive.domain.model.common.Skin class SkinRVA(val SkinFlow: (previousPosition: Int?, currentPosition: Int) -> Unit) : ListAdapter(differ) { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModel.kt index b7e808c8a..ccd552d34 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModel.kt @@ -1,15 +1,31 @@ package com.droidblossom.archive.presentation.ui.mypage.friend.addfriend +import com.droidblossom.archive.domain.model.friend.FriendsSearchResponse import com.droidblossom.archive.presentation.ui.mypage.MyPageViewModel +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow interface AddFriendViewModel { val addEvent : SharedFlow + + //searchName + val addFriendList : StateFlow> + val checkedList : StateFlow> + val tagT : MutableStateFlow + + //searchNum val isSearchNumOpen : StateFlow - fun searchName() - fun openSearchName() + + + fun requestFriends() + //searchName + fun searchTag() + + //searchNum + fun searchNum() + fun openSearchNum() sealed class AddEvent { data class ShowToastMessage(val message : String) : AddEvent() diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt index 9bfeb5935..e85624cb6 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt @@ -1,19 +1,29 @@ package com.droidblossom.archive.presentation.ui.mypage.friend.addfriend import androidx.lifecycle.viewModelScope +import com.droidblossom.archive.R +import com.droidblossom.archive.domain.model.friend.FriendReqRequest +import com.droidblossom.archive.domain.model.friend.FriendsSearchRequest +import com.droidblossom.archive.domain.model.friend.FriendsSearchResponse +import com.droidblossom.archive.domain.usecase.friend.FriendsRequestUseCase +import com.droidblossom.archive.domain.usecase.friend.FriendsSearchUseCase import com.droidblossom.archive.presentation.base.BaseViewModel +import com.droidblossom.archive.util.onFail +import com.droidblossom.archive.util.onSuccess import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel class AddFriendViewModelImpl @Inject constructor( - + private val friendsSearchUseCase : FriendsSearchUseCase, + private val friendsRequestUseCase: FriendsRequestUseCase ) : BaseViewModel(), AddFriendViewModel { private val _addEvent = MutableSharedFlow() @@ -21,15 +31,86 @@ class AddFriendViewModelImpl @Inject constructor( override val addEvent: SharedFlow get() = _addEvent.asSharedFlow() + //name + + private val _addFriendList = MutableStateFlow>(listOf()) + override val addFriendList: StateFlow> + get() = _addFriendList + + private val _checkedList = MutableStateFlow>(listOf()) + override val checkedList: StateFlow> + get() = _checkedList + + override val tagT: MutableStateFlow = MutableStateFlow("") + + + //Num + private val _isSearchNumOpen = MutableStateFlow(false) override val isSearchNumOpen: StateFlow get() = _isSearchNumOpen - override fun searchName() { + override fun requestFriends() { + viewModelScope.launch { + checkedList.value.forEach { friend -> + friendsRequestUseCase(FriendReqRequest(friendId = friend.id)).collect{ result -> + result.onSuccess { + _addEvent.emit(AddFriendViewModel.AddEvent.ShowToastMessage("친구 요청을 보냈습니다")) + }.onFail { + _addEvent.emit(AddFriendViewModel.AddEvent.ShowToastMessage("친구 요청 오류 발생")) + } + } + } + } + } + + fun checkAddFriendList(position : Int) { + viewModelScope.launch { + val newAddList = addFriendList.value + if (newAddList[position].isChecked){ + newAddList[position].isChecked = false + _checkedList.emit(newAddList.filter { it.isChecked }) + } else { + newAddList[position].isChecked = true + _checkedList.emit(newAddList.filter { it.isChecked }) + } + _addFriendList.emit(newAddList) + } + } + + override fun searchTag() { + viewModelScope.launch { + friendsSearchUseCase(FriendsSearchRequest(tagT.value)).collect{ result -> + result.onSuccess { response -> + _addFriendList.emit(listOf(response)) + }.onFail { + _addEvent.emit(AddFriendViewModel.AddEvent.ShowToastMessage("검색이 불가능 합니다.")) + } + } + } + } + + fun resetList() { + viewModelScope.launch { + if (checkedList.value.isNotEmpty()) { + _checkedList.emit(listOf()) + } + if (addFriendList.value.isNotEmpty()){ + _addFriendList.emit(listOf()) + } + } + } + + //Name + + + + //Num + override fun searchNum() { } - override fun openSearchName() { + override fun openSearchNum() { viewModelScope.launch { _isSearchNumOpen.emit(true) } diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_friend_search_nickname.xml b/frontend/ARchive/app/src/main/res/layout/fragment_friend_search_nickname.xml index 98c680adf..e3f2c364a 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_friend_search_nickname.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_friend_search_nickname.xml @@ -68,6 +68,7 @@ android:ellipsize="start" android:imeOptions="actionDone" android:lines="1" + android:text="@={vm.tagT}" android:maxLines="1" android:singleLine="true" android:textAppearance="@style/TextAppearance.App.body2" @@ -177,9 +178,12 @@ android:layout_height="56dp" android:layout_marginHorizontal="@dimen/margin" android:layout_marginBottom="@dimen/margin" + tools:alpha="0.5" + android:alpha="@{vm.checkedList.size() == 0 ? 0.5f : 1f}" android:background="@drawable/corner_radius_8" android:backgroundTint="@color/main_1" android:text="추가하기" + android:onClick="@{()->vm.requestFriends()}" android:textAppearance="@style/TextAppearance.App.subtitle1" android:textColor="@color/white" app:circularflow_radiusInDP="20dp" From 68df529e20c8c497539ff5583f7e21eba1937193 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Wed, 3 Apr 2024 19:06:25 +0900 Subject: [PATCH 143/301] =?UTF-8?q?feat:=20RVA=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../model/friend/FriendsSearchResponse.kt | 3 +- .../addfriend/SearchFriendNicknameFragment.kt | 41 +++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendsSearchResponse.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendsSearchResponse.kt index 40f5b66bf..f3f8e8302 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendsSearchResponse.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendsSearchResponse.kt @@ -4,5 +4,6 @@ data class FriendsSearchResponse ( val id : Long, val profileUrl : String, val nickname : String, - val isFriend : Boolean + val isFriend : Boolean, + var isChecked : Boolean ) \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNicknameFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNicknameFragment.kt index 8707b201e..255dc41cf 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNicknameFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNicknameFragment.kt @@ -4,6 +4,9 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.view.inputmethod.EditorInfo +import android.view.inputmethod.InputMethodManager +import androidx.core.widget.addTextChangedListener import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle @@ -14,6 +17,7 @@ import androidx.navigation.Navigation import com.droidblossom.archive.R import com.droidblossom.archive.databinding.FragmentFriendSearchNicknameBinding import com.droidblossom.archive.presentation.base.BaseFragment +import com.droidblossom.archive.presentation.ui.mypage.friend.addfriend.adapter.AddFriendRVA import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch @@ -24,8 +28,15 @@ class SearchFriendNicknameFragment : override val viewModel: AddFriendViewModelImpl by viewModels() lateinit var navController: NavController + + private val addFriendRVA by lazy { + AddFriendRVA{ position -> + viewModel.checkAddFriendList(position) + } + } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + binding.vm = viewModel navController = Navigation.findNavController(view) initView() @@ -37,13 +48,35 @@ class SearchFriendNicknameFragment : layoutParams.topMargin += getStatusBarHeight() binding.closeBtn.layoutParams = layoutParams + binding.recycleView.adapter = addFriendRVA + binding.closeBtn.setOnClickListener { (activity as AddFriendActivity).finish() } + binding.searchOpenEditT.setOnEditorActionListener { _, i, _ -> + if (i == EditorInfo.IME_ACTION_DONE) { + if (!binding.searchOpenEditT.text.isNullOrEmpty()) { + viewModel.searchTag() + } + true + } + false + } + binding.searchOpenEditT.addTextChangedListener { + viewModel.resetList() + } + binding.addCV.setOnClickListener { + //viewModel.resetList() navController.navigate(R.id.action_searchFriendNicknameFragment_to_searchFriendNumberFragment) } + + binding.searchOpenBtnT.setOnClickListener { + val imm = requireActivity().getSystemService(InputMethodManager::class.java) + imm.hideSoftInputFromWindow(requireActivity().currentFocus?.windowToken, 0) + viewModel.searchTag() + } } override fun observeData() { @@ -60,5 +93,13 @@ class SearchFriendNicknameFragment : } } } + + viewLifecycleOwner.lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.addFriendList.collect{ friends -> + addFriendRVA.submitList(friends) + } + } + } } } \ No newline at end of file From d6f892210f3416b90b75f1f6a311498c4270a598 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Wed, 3 Apr 2024 22:18:17 +0900 Subject: [PATCH 144/301] =?UTF-8?q?feat=20:=20=EC=A3=BC=EC=86=8C=EB=A1=9D?= =?UTF-8?q?=20=EC=B9=9C=EA=B5=AC=20=EC=B6=94=EA=B0=80=20UI=20&=20=EC=9E=90?= =?UTF-8?q?=EC=9E=98=ED=95=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../friend/addfriend/AddFriendViewModel.kt | 2 +- .../addfriend/AddFriendViewModelImpl.kt | 21 +- .../addfriend/SearchFriendNicknameFragment.kt | 2 +- .../fragment_friend_search_nickname.xml | 8 +- .../layout/fragment_friend_search_number.xml | 214 +++++++++++++++++- 5 files changed, 229 insertions(+), 18 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModel.kt index ccd552d34..2158031c0 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModel.kt @@ -11,7 +11,7 @@ interface AddFriendViewModel { val addEvent : SharedFlow //searchName - val addFriendList : StateFlow> + val addFriendListUI : StateFlow> val checkedList : StateFlow> val tagT : MutableStateFlow diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt index e85624cb6..c29ceb2ff 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt @@ -33,9 +33,9 @@ class AddFriendViewModelImpl @Inject constructor( //name - private val _addFriendList = MutableStateFlow>(listOf()) - override val addFriendList: StateFlow> - get() = _addFriendList + private val _addFriendListUI = MutableStateFlow>(listOf()) + override val addFriendListUI: StateFlow> + get() = _addFriendListUI private val _checkedList = MutableStateFlow>(listOf()) override val checkedList: StateFlow> @@ -66,7 +66,7 @@ class AddFriendViewModelImpl @Inject constructor( fun checkAddFriendList(position : Int) { viewModelScope.launch { - val newAddList = addFriendList.value + val newAddList = addFriendListUI.value if (newAddList[position].isChecked){ newAddList[position].isChecked = false _checkedList.emit(newAddList.filter { it.isChecked }) @@ -74,7 +74,7 @@ class AddFriendViewModelImpl @Inject constructor( newAddList[position].isChecked = true _checkedList.emit(newAddList.filter { it.isChecked }) } - _addFriendList.emit(newAddList) + _addFriendListUI.emit(newAddList) } } @@ -82,7 +82,7 @@ class AddFriendViewModelImpl @Inject constructor( viewModelScope.launch { friendsSearchUseCase(FriendsSearchRequest(tagT.value)).collect{ result -> result.onSuccess { response -> - _addFriendList.emit(listOf(response)) + _addFriendListUI.emit(listOf(response)) }.onFail { _addEvent.emit(AddFriendViewModel.AddEvent.ShowToastMessage("검색이 불가능 합니다.")) } @@ -95,8 +95,8 @@ class AddFriendViewModelImpl @Inject constructor( if (checkedList.value.isNotEmpty()) { _checkedList.emit(listOf()) } - if (addFriendList.value.isNotEmpty()){ - _addFriendList.emit(listOf()) + if (addFriendListUI.value.isNotEmpty()){ + _addFriendListUI.emit(listOf()) } } } @@ -116,4 +116,9 @@ class AddFriendViewModelImpl @Inject constructor( } } + fun closeSearchNum(){ + viewModelScope.launch { + _isSearchNumOpen.emit(false) + } + } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNicknameFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNicknameFragment.kt index 255dc41cf..0d18e4afd 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNicknameFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNicknameFragment.kt @@ -96,7 +96,7 @@ class SearchFriendNicknameFragment : viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { - viewModel.addFriendList.collect{ friends -> + viewModel.addFriendListUI.collect{ friends -> addFriendRVA.submitList(friends) } } diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_friend_search_nickname.xml b/frontend/ARchive/app/src/main/res/layout/fragment_friend_search_nickname.xml index e3f2c364a..9626848ce 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_friend_search_nickname.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_friend_search_nickname.xml @@ -68,9 +68,9 @@ android:ellipsize="start" android:imeOptions="actionDone" android:lines="1" - android:text="@={vm.tagT}" android:maxLines="1" android:singleLine="true" + android:text="@={vm.tagT}" android:textAppearance="@style/TextAppearance.App.body2" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@id/searchOpenBtnT" @@ -178,18 +178,18 @@ android:layout_height="56dp" android:layout_marginHorizontal="@dimen/margin" android:layout_marginBottom="@dimen/margin" - tools:alpha="0.5" android:alpha="@{vm.checkedList.size() == 0 ? 0.5f : 1f}" android:background="@drawable/corner_radius_8" android:backgroundTint="@color/main_1" - android:text="추가하기" android:onClick="@{()->vm.requestFriends()}" + android:text="추가하기" android:textAppearance="@style/TextAppearance.App.subtitle1" android:textColor="@color/white" app:circularflow_radiusInDP="20dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" /> + app:layout_constraintStart_toStartOf="parent" + tools:alpha="0.5" /> - + - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 7c6bb58f0929aa38d6f90dc0fe58db51537af6ab Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Wed, 3 Apr 2024 22:40:45 +0900 Subject: [PATCH 145/301] =?UTF-8?q?feat:=20=EC=A3=BC=EC=86=8C=EB=A1=9D=20f?= =?UTF-8?q?ragment?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../addfriend/SearchFriendNumberFragment.kt | 131 +++++++++++++++++- .../fragment_friend_search_nickname.xml | 19 +-- .../layout/fragment_friend_search_number.xml | 20 ++- 3 files changed, 144 insertions(+), 26 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNumberFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNumberFragment.kt index 598ace6d9..bc68c11dc 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNumberFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNumberFragment.kt @@ -1,18 +1,137 @@ package com.droidblossom.archive.presentation.ui.mypage.friend.addfriend +import android.annotation.SuppressLint +import android.graphics.Rect import android.os.Bundle import android.view.LayoutInflater +import android.view.MotionEvent import android.view.View import android.view.ViewGroup +import android.view.inputmethod.EditorInfo +import android.view.inputmethod.InputMethodManager +import androidx.core.widget.addTextChangedListener import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import androidx.navigation.NavController +import androidx.navigation.Navigation import com.droidblossom.archive.R +import com.droidblossom.archive.databinding.FragmentFriendSearchNicknameBinding +import com.droidblossom.archive.databinding.FragmentFriendSearchNumberBinding +import com.droidblossom.archive.presentation.base.BaseFragment +import com.droidblossom.archive.presentation.ui.mypage.friend.addfriend.adapter.AddFriendRVA +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.launch -class SearchFriendNumberFragment : Fragment() { +@AndroidEntryPoint +class SearchFriendNumberFragment : + BaseFragment(R.layout.fragment_friend_search_number) { - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - return inflater.inflate(R.layout.fragment_friend_search_number, container, false) + override val viewModel: AddFriendViewModelImpl by viewModels() + + lateinit var navController: NavController + + private val addFriendRVA by lazy { + AddFriendRVA{ position -> + viewModel.checkAddFriendList(position) + } + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.vm = viewModel + + navController = Navigation.findNavController(view) + initView() + } + + @SuppressLint("ClickableViewAccessibility") + private fun initView() { + + val layoutParams = binding.closeBtn.layoutParams as ViewGroup.MarginLayoutParams + layoutParams.topMargin += getStatusBarHeight() + binding.closeBtn.layoutParams = layoutParams + + binding.recycleView.adapter = addFriendRVA + + binding.closeBtn.setOnClickListener { + navController.popBackStack() + } + + binding.searchOpenEditT.setOnEditorActionListener { _, i, _ -> + if (i == EditorInfo.IME_ACTION_DONE) { + if (!binding.searchOpenEditT.text.isNullOrEmpty()) { + viewModel.searchTag() + } + true + } + false + } + binding.searchOpenEditT.addTextChangedListener { + viewModel.resetList() + } + + binding.searchOpenBtnT.setOnClickListener { + val imm = requireActivity().getSystemService(InputMethodManager::class.java) + imm.hideSoftInputFromWindow(requireActivity().currentFocus?.windowToken, 0) + viewModel.searchTag() + } + + binding.searchOpenEditT.setOnFocusChangeListener { _, hasFocus -> + if (!hasFocus) { + viewModel.closeSearchNum() + } + } + binding.root.setOnTouchListener { _, event -> + if (event.action == MotionEvent.ACTION_DOWN) { + val focusedView = binding.searchOpenBtn + val outRect = Rect() + focusedView.getGlobalVisibleRect(outRect) + if (!outRect.contains(event.rawX.toInt(), event.rawY.toInt())) { + focusedView.clearFocus() + val imm = requireActivity().getSystemService(InputMethodManager::class.java) + imm.hideSoftInputFromWindow(focusedView.windowToken, 0) + } + } + false + } + + binding.recycleView.setOnTouchListener { _, event -> + if (event.action == MotionEvent.ACTION_DOWN) { + val focusedView = binding.searchOpenBtn + val outRect = Rect() + focusedView.getGlobalVisibleRect(outRect) + if (!outRect.contains(event.rawX.toInt(), event.rawY.toInt())) { + val imm = requireActivity().getSystemService(InputMethodManager::class.java) + imm.hideSoftInputFromWindow(focusedView.windowToken, 0) + } + } + false + } + } + + override fun observeData() { + viewLifecycleOwner.lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.addEvent.collect { event -> + when (event) { + is AddFriendViewModel.AddEvent.ShowToastMessage -> { + showToastMessage(event.message) + } + + else -> {} + } + } + } + } + + viewLifecycleOwner.lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.addFriendListUI.collect{ friends -> + addFriendRVA.submitList(friends) + } + } + } } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_friend_search_nickname.xml b/frontend/ARchive/app/src/main/res/layout/fragment_friend_search_nickname.xml index 9626848ce..4fcd64662 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_friend_search_nickname.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_friend_search_nickname.xml @@ -171,8 +171,16 @@ + - - \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_friend_search_number.xml b/frontend/ARchive/app/src/main/res/layout/fragment_friend_search_number.xml index 2761aca36..6e21aff84 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_friend_search_number.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_friend_search_number.xml @@ -148,6 +148,15 @@ tools:itemCount="20" tools:listitem="@layout/item_add_friend" /> + + - - - - \ No newline at end of file From 16f9ec651c9e6dfaee3eadc10d788f6b8e4f3f28 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Thu, 4 Apr 2024 01:39:45 +0900 Subject: [PATCH 146/301] =?UTF-8?q?faet:=20=EC=A3=BC=EC=86=8C=EB=A1=9D?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EB=B2=88=ED=98=B8=20=EB=B6=88=EB=9F=AC?= =?UTF-8?q?=EC=98=A4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/util/ContactsUtils.kt | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/ContactsUtils.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/ContactsUtils.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/ContactsUtils.kt new file mode 100644 index 000000000..15edfd4a9 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/ContactsUtils.kt @@ -0,0 +1,35 @@ +package com.droidblossom.archive.util + +import android.content.ContentResolver +import android.content.Context +import android.provider.ContactsContract +import android.util.Log + + +object ContactsUtils { + + fun getContacts(context : Context) : List { + val resolver: ContentResolver = context.contentResolver + val phoneUri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI + val projection = arrayOf( + ContactsContract.CommonDataKinds.Phone.CONTACT_ID, + ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, + ContactsContract.CommonDataKinds.Phone.NUMBER + ) + val numberList = mutableListOf() + val cursor = resolver.query(phoneUri, projection, null, null, null) + if (cursor != null) { + while (cursor.moveToNext()) { + val nameIndex = cursor.getColumnIndex(projection[1]) + val numberIndex = cursor.getColumnIndex(projection[2]) + val name = cursor.getString(nameIndex) + var number = cursor.getString(numberIndex) + number = number.replace("-", "") + numberList.add(number) + Log.d("GetContact", "이름 : $name 번호 : $number") + } + } + cursor!!.close() + return numberList.toList() + } +} \ No newline at end of file From 335f006549f815552e6d0bdb9a0ccd3f588b7175 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Thu, 4 Apr 2024 01:40:11 +0900 Subject: [PATCH 147/301] =?UTF-8?q?feat:=20=EC=A3=BC=EC=86=8C=EB=A1=9D=20?= =?UTF-8?q?=EB=B6=88=EB=9F=AC=EC=98=A4=EA=B8=B0=20vm?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ARchive/app/src/main/AndroidManifest.xml | 1 + .../friend/addfriend/AddFriendViewModel.kt | 1 + .../addfriend/AddFriendViewModelImpl.kt | 40 ++++++++++++++----- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/frontend/ARchive/app/src/main/AndroidManifest.xml b/frontend/ARchive/app/src/main/AndroidManifest.xml index 402738e11..d8b51f5b4 100644 --- a/frontend/ARchive/app/src/main/AndroidManifest.xml +++ b/frontend/ARchive/app/src/main/AndroidManifest.xml @@ -7,6 +7,7 @@ + diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModel.kt index 2158031c0..55cbd7a4a 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModel.kt @@ -17,6 +17,7 @@ interface AddFriendViewModel { //searchNum val isSearchNumOpen : StateFlow + val addFriendList: StateFlow> fun requestFriends() diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt index c29ceb2ff..2f7740e1b 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt @@ -3,9 +3,11 @@ package com.droidblossom.archive.presentation.ui.mypage.friend.addfriend import androidx.lifecycle.viewModelScope import com.droidblossom.archive.R import com.droidblossom.archive.domain.model.friend.FriendReqRequest +import com.droidblossom.archive.domain.model.friend.FriendsSearchPhoneRequest import com.droidblossom.archive.domain.model.friend.FriendsSearchRequest import com.droidblossom.archive.domain.model.friend.FriendsSearchResponse import com.droidblossom.archive.domain.usecase.friend.FriendsRequestUseCase +import com.droidblossom.archive.domain.usecase.friend.FriendsSearchPhoneUseCase import com.droidblossom.archive.domain.usecase.friend.FriendsSearchUseCase import com.droidblossom.archive.presentation.base.BaseViewModel import com.droidblossom.archive.util.onFail @@ -22,8 +24,9 @@ import javax.inject.Inject @HiltViewModel class AddFriendViewModelImpl @Inject constructor( - private val friendsSearchUseCase : FriendsSearchUseCase, - private val friendsRequestUseCase: FriendsRequestUseCase + private val friendsSearchUseCase: FriendsSearchUseCase, + private val friendsRequestUseCase: FriendsRequestUseCase, + private val friendsSearchPhoneUseCase: FriendsSearchPhoneUseCase ) : BaseViewModel(), AddFriendViewModel { private val _addEvent = MutableSharedFlow() @@ -37,6 +40,11 @@ class AddFriendViewModelImpl @Inject constructor( override val addFriendListUI: StateFlow> get() = _addFriendListUI + private val _addFriendList = MutableStateFlow>(listOf()) + override val addFriendList: StateFlow> + get() = _addFriendList + + private val _checkedList = MutableStateFlow>(listOf()) override val checkedList: StateFlow> get() = _checkedList @@ -53,7 +61,7 @@ class AddFriendViewModelImpl @Inject constructor( override fun requestFriends() { viewModelScope.launch { checkedList.value.forEach { friend -> - friendsRequestUseCase(FriendReqRequest(friendId = friend.id)).collect{ result -> + friendsRequestUseCase(FriendReqRequest(friendId = friend.id)).collect { result -> result.onSuccess { _addEvent.emit(AddFriendViewModel.AddEvent.ShowToastMessage("친구 요청을 보냈습니다")) }.onFail { @@ -64,10 +72,10 @@ class AddFriendViewModelImpl @Inject constructor( } } - fun checkAddFriendList(position : Int) { + fun checkAddFriendList(position: Int) { viewModelScope.launch { val newAddList = addFriendListUI.value - if (newAddList[position].isChecked){ + if (newAddList[position].isChecked) { newAddList[position].isChecked = false _checkedList.emit(newAddList.filter { it.isChecked }) } else { @@ -80,7 +88,7 @@ class AddFriendViewModelImpl @Inject constructor( override fun searchTag() { viewModelScope.launch { - friendsSearchUseCase(FriendsSearchRequest(tagT.value)).collect{ result -> + friendsSearchUseCase(FriendsSearchRequest(tagT.value)).collect { result -> result.onSuccess { response -> _addFriendListUI.emit(listOf(response)) }.onFail { @@ -95,7 +103,7 @@ class AddFriendViewModelImpl @Inject constructor( if (checkedList.value.isNotEmpty()) { _checkedList.emit(listOf()) } - if (addFriendListUI.value.isNotEmpty()){ + if (addFriendListUI.value.isNotEmpty()) { _addFriendListUI.emit(listOf()) } } @@ -104,7 +112,6 @@ class AddFriendViewModelImpl @Inject constructor( //Name - //Num override fun searchNum() { @@ -116,9 +123,24 @@ class AddFriendViewModelImpl @Inject constructor( } } - fun closeSearchNum(){ + fun closeSearchNum() { viewModelScope.launch { _isSearchNumOpen.emit(false) } } + + fun contactsSearch(phones: List) { + viewModelScope.launch { + friendsSearchPhoneUseCase(FriendsSearchPhoneRequest(phones.filter { + it.length == 11 && it.substring(0,3) == "010" + })).collect { result -> + result.onSuccess { response -> + _addFriendList.emit(response.friends) + _addFriendListUI.emit(response.friends) + }.onFail { + _addEvent.emit(AddFriendViewModel.AddEvent.ShowToastMessage("주소록 불러오기 실패.")) + } + } + } + } } \ No newline at end of file From 7d16968d41839af3cc9cbd62e3229a23892b8070 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Thu, 4 Apr 2024 13:43:45 +0900 Subject: [PATCH 148/301] chore --- .../ui/mypage/friend/addfriend/AddFriendViewModel.kt | 1 + .../friend/addfriend/AddFriendViewModelImpl.kt | 4 +++- .../friend/addfriend/SearchFriendNumberFragment.kt | 12 ++++++++---- .../res/layout/fragment_friend_search_number.xml | 6 +++--- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModel.kt index 55cbd7a4a..dc6e7282b 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModel.kt @@ -30,5 +30,6 @@ interface AddFriendViewModel { sealed class AddEvent { data class ShowToastMessage(val message : String) : AddEvent() + object CloseLoading : AddEvent() } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt index 2f7740e1b..819f6dffc 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt @@ -114,7 +114,7 @@ class AddFriendViewModelImpl @Inject constructor( //Num override fun searchNum() { - + //respone에 이름 추가시 구현 } override fun openSearchNum() { @@ -137,8 +137,10 @@ class AddFriendViewModelImpl @Inject constructor( result.onSuccess { response -> _addFriendList.emit(response.friends) _addFriendListUI.emit(response.friends) + _addEvent.emit(AddFriendViewModel.AddEvent.CloseLoading) }.onFail { _addEvent.emit(AddFriendViewModel.AddEvent.ShowToastMessage("주소록 불러오기 실패.")) + _addEvent.emit(AddFriendViewModel.AddEvent.CloseLoading) } } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNumberFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNumberFragment.kt index bc68c11dc..9c0c7ea49 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNumberFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNumberFragment.kt @@ -3,14 +3,12 @@ package com.droidblossom.archive.presentation.ui.mypage.friend.addfriend import android.annotation.SuppressLint import android.graphics.Rect import android.os.Bundle -import android.view.LayoutInflater import android.view.MotionEvent import android.view.View import android.view.ViewGroup import android.view.inputmethod.EditorInfo import android.view.inputmethod.InputMethodManager import androidx.core.widget.addTextChangedListener -import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope @@ -18,13 +16,14 @@ import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.NavController import androidx.navigation.Navigation import com.droidblossom.archive.R -import com.droidblossom.archive.databinding.FragmentFriendSearchNicknameBinding import com.droidblossom.archive.databinding.FragmentFriendSearchNumberBinding import com.droidblossom.archive.presentation.base.BaseFragment import com.droidblossom.archive.presentation.ui.mypage.friend.addfriend.adapter.AddFriendRVA +import com.droidblossom.archive.util.ContactsUtils import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch + @AndroidEntryPoint class SearchFriendNumberFragment : BaseFragment(R.layout.fragment_friend_search_number) { @@ -44,6 +43,9 @@ class SearchFriendNumberFragment : navController = Navigation.findNavController(view) initView() + + showLoading(requireContext()) + viewModel.contactsSearch(ContactsUtils.getContacts(requireContext())) } @SuppressLint("ClickableViewAccessibility") @@ -120,7 +122,9 @@ class SearchFriendNumberFragment : showToastMessage(event.message) } - else -> {} + is AddFriendViewModel.AddEvent.CloseLoading -> { + dismissLoading() + } } } } diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_friend_search_number.xml b/frontend/ARchive/app/src/main/res/layout/fragment_friend_search_number.xml index 6e21aff84..cb15be200 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_friend_search_number.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_friend_search_number.xml @@ -34,8 +34,8 @@ android:layout_height="wrap_content" android:layout_marginStart="@dimen/margin" android:layout_marginTop="12dp" - android:lines="1" android:ellipsize="end" + android:lines="1" android:text="주소록에서 친구를 추가하세요" android:textAppearance="@style/TextAppearance.App.h2" app:layout_constraintStart_toStartOf="parent" @@ -181,11 +181,11 @@ android:layout_marginEnd="10dp" android:background="@drawable/corner_radius_16" android:backgroundTint="@color/white" - android:text="2" - android:visibility="gone" android:gravity="center" + android:text="@{vm.checkedList.size()}" android:textAppearance="@style/TextAppearance.App.subtitle1" android:textColor="@color/main_1" + android:visibility="@{vm.checkedList.size() == 0 ?View.GONE : View.VISIBLE}" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@id/addT" app:layout_constraintHorizontal_chainStyle="packed" From c15268212c7abb0d30266eebb12304b95ea8d2a6 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Thu, 4 Apr 2024 13:43:55 +0900 Subject: [PATCH 149/301] =?UTF-8?q?feat:=20=EC=BA=A1=EC=8A=90=20=EB=94=94?= =?UTF-8?q?=ED=85=8C=EC=9D=BC=20->=20common?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CapsuleDetailResponseDto.kt} | 8 ++++---- .../archive/data/repository/SecretRepositoryImpl.kt | 8 ++++---- .../archive/data/source/remote/api/PublicService.kt | 5 +++++ .../archive/data/source/remote/api/SecretService.kt | 4 ++-- .../SecretCapsuleDetail.kt => common/CapsuleDetail.kt} | 4 ++-- .../archive/domain/repository/SecretRepository.kt | 4 ++-- .../domain/usecase/secret/SecretCapsuleDetailUseCase.kt | 4 ++-- .../presentation/ui/capsule/CapsuleDetailViewModel.kt | 6 ++---- .../ui/capsule/CapsuleDetailViewModelImpl.kt | 9 +++------ 9 files changed, 26 insertions(+), 26 deletions(-) rename frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/{secret/response/SecretCapsuleDetailResponseDto.kt => common/CapsuleDetailResponseDto.kt} (78%) rename frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/{secret/SecretCapsuleDetail.kt => common/CapsuleDetail.kt} (84%) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/secret/response/SecretCapsuleDetailResponseDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/common/CapsuleDetailResponseDto.kt similarity index 78% rename from frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/secret/response/SecretCapsuleDetailResponseDto.kt rename to frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/common/CapsuleDetailResponseDto.kt index 579ee9263..4d6a6a53a 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/secret/response/SecretCapsuleDetailResponseDto.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/common/CapsuleDetailResponseDto.kt @@ -1,8 +1,8 @@ -package com.droidblossom.archive.data.dto.secret.response +package com.droidblossom.archive.data.dto.common -import com.droidblossom.archive.domain.model.secret.SecretCapsuleDetail +import com.droidblossom.archive.domain.model.common.CapsuleDetail -data class SecretCapsuleDetailResponseDto( +data class CapsuleDetailResponseDto( val address: String, val capsuleSkinUrl: String, val content: String?, @@ -16,7 +16,7 @@ data class SecretCapsuleDetailResponseDto( val title: String, val capsuleType: String, ){ - fun toModel() = SecretCapsuleDetail( + fun toModel() = CapsuleDetail( address = this.address, capsuleSkinUrl = this.capsuleSkinUrl, content = this.content ?: "", diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/SecretRepositoryImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/SecretRepositoryImpl.kt index 19d6107bd..a9fddeacf 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/SecretRepositoryImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/SecretRepositoryImpl.kt @@ -5,12 +5,12 @@ import com.droidblossom.archive.data.dto.common.toModel import com.droidblossom.archive.data.dto.common.CapsuleCreateRequestDto import com.droidblossom.archive.data.dto.secret.request.SecretCapsuleModifyRequestDto import com.droidblossom.archive.data.dto.secret.request.SecretCapsulePageRequestDto -import com.droidblossom.archive.data.dto.secret.response.SecretCapsuleDetailResponseDto +import com.droidblossom.archive.data.dto.common.CapsuleDetailResponseDto import com.droidblossom.archive.data.dto.secret.response.SecretCapsuleModifyResponseDto import com.droidblossom.archive.data.dto.secret.response.SecretCapsulePageResponseDto import com.droidblossom.archive.data.dto.common.CapsuleSummaryResponseDto import com.droidblossom.archive.data.source.remote.api.SecretService -import com.droidblossom.archive.domain.model.secret.SecretCapsuleDetail +import com.droidblossom.archive.domain.model.common.CapsuleDetail import com.droidblossom.archive.domain.model.secret.SecretCapsuleModify import com.droidblossom.archive.domain.model.secret.SecretCapsulePage import com.droidblossom.archive.domain.model.common.CapsuleSummaryResponse @@ -31,8 +31,8 @@ class SecretRepositoryImpl @Inject constructor( return apiHandler({ api.postSecretCapsuleApi(request) }) { response: ResponseBody -> response.result.toModel() } } - override suspend fun getSecretCapsuleDetail(capsuleId: Long): RetrofitResult { - return apiHandler({ api.getSecretCapsuleDetailApi(capsuleId) }) { response: ResponseBody -> response.result.toModel() } + override suspend fun getSecretCapsuleDetail(capsuleId: Long): RetrofitResult { + return apiHandler({ api.getSecretCapsuleDetailApi(capsuleId) }) { response: ResponseBody -> response.result.toModel() } } override suspend fun getSecretCapsuleSummary (capsuleId: Int) : RetrofitResult { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/PublicService.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/PublicService.kt index a807647bd..0f0263892 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/PublicService.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/PublicService.kt @@ -2,6 +2,7 @@ package com.droidblossom.archive.data.source.remote.api import com.droidblossom.archive.data.dto.ResponseBody import com.droidblossom.archive.data.dto.common.CapsuleCreateRequestDto +import com.droidblossom.archive.data.dto.common.CapsuleDetailResponseDto import com.droidblossom.archive.data.dto.common.CapsuleSummaryResponseDto import retrofit2.Response import retrofit2.http.Body @@ -21,5 +22,9 @@ interface PublicService { @Path("capsule_id") capsuleId : Int, ) : Response> + @GET("public/capsules/{capsule_id}/detail") + suspend fun getPublicCapsuleDetailApi( + @Path("capsule_id") capsuleId : Int, + ) : Response> } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/SecretService.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/SecretService.kt index e24fdbfd3..6945cd642 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/SecretService.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/SecretService.kt @@ -3,7 +3,7 @@ package com.droidblossom.archive.data.source.remote.api import com.droidblossom.archive.data.dto.ResponseBody import com.droidblossom.archive.data.dto.common.CapsuleCreateRequestDto import com.droidblossom.archive.data.dto.secret.request.SecretCapsuleModifyRequestDto -import com.droidblossom.archive.data.dto.secret.response.SecretCapsuleDetailResponseDto +import com.droidblossom.archive.data.dto.common.CapsuleDetailResponseDto import com.droidblossom.archive.data.dto.secret.response.SecretCapsuleModifyResponseDto import com.droidblossom.archive.data.dto.secret.response.SecretCapsulePageResponseDto import com.droidblossom.archive.data.dto.common.CapsuleSummaryResponseDto @@ -31,7 +31,7 @@ interface SecretService { @GET("secret/capsules/{capsule_id}/detail") suspend fun getSecretCapsuleDetailApi( @Path("capsule_id") capsuleId : Long, - ) : Response> + ) : Response> @GET("secret/capsules/{capsule_id}/summary") suspend fun getSecretCapsuleSummaryApi( diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/secret/SecretCapsuleDetail.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/common/CapsuleDetail.kt similarity index 84% rename from frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/secret/SecretCapsuleDetail.kt rename to frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/common/CapsuleDetail.kt index 79955443d..affa279d4 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/secret/SecretCapsuleDetail.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/common/CapsuleDetail.kt @@ -1,7 +1,7 @@ -package com.droidblossom.archive.domain.model.secret +package com.droidblossom.archive.domain.model.common // 모든 캡슐 디테일로 통합될 예정 maybe -data class SecretCapsuleDetail( +data class CapsuleDetail( val address: String, val capsuleSkinUrl: String, val content: String, diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/SecretRepository.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/SecretRepository.kt index 2f075c783..ec5c8410c 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/SecretRepository.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/SecretRepository.kt @@ -3,7 +3,7 @@ package com.droidblossom.archive.domain.repository import com.droidblossom.archive.data.dto.common.CapsuleCreateRequestDto import com.droidblossom.archive.data.dto.secret.request.SecretCapsuleModifyRequestDto import com.droidblossom.archive.data.dto.secret.request.SecretCapsulePageRequestDto -import com.droidblossom.archive.domain.model.secret.SecretCapsuleDetail +import com.droidblossom.archive.domain.model.common.CapsuleDetail import com.droidblossom.archive.domain.model.secret.SecretCapsuleModify import com.droidblossom.archive.domain.model.secret.SecretCapsulePage import com.droidblossom.archive.domain.model.common.CapsuleSummaryResponse @@ -15,7 +15,7 @@ interface SecretRepository { suspend fun createSecretCapsule (request: CapsuleCreateRequestDto) : RetrofitResult - suspend fun getSecretCapsuleDetail (capsuleId: Long) : RetrofitResult + suspend fun getSecretCapsuleDetail (capsuleId: Long) : RetrofitResult suspend fun getSecretCapsuleSummary (capsuleId: Int) : RetrofitResult suspend fun modifySecretCapsule (capsuleId: Int, request: SecretCapsuleModifyRequestDto) : RetrofitResult diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/secret/SecretCapsuleDetailUseCase.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/secret/SecretCapsuleDetailUseCase.kt index 4f08516aa..aaf6c9f38 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/secret/SecretCapsuleDetailUseCase.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/secret/SecretCapsuleDetailUseCase.kt @@ -1,7 +1,7 @@ package com.droidblossom.archive.domain.usecase.secret import android.util.Log -import com.droidblossom.archive.domain.model.secret.SecretCapsuleDetail +import com.droidblossom.archive.domain.model.common.CapsuleDetail import com.droidblossom.archive.domain.repository.SecretRepository import com.droidblossom.archive.util.RetrofitResult import com.droidblossom.archive.util.onException @@ -14,7 +14,7 @@ class SecretCapsuleDetailUseCase @Inject constructor( private val repository: SecretRepository ) { suspend operator fun invoke(capsuleId: Long) = - flow> { + flow> { try { emit(repository.getSecretCapsuleDetail(capsuleId).onSuccess { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/capsule/CapsuleDetailViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/capsule/CapsuleDetailViewModel.kt index 6c479ec7b..db0241327 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/capsule/CapsuleDetailViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/capsule/CapsuleDetailViewModel.kt @@ -1,8 +1,6 @@ package com.droidblossom.archive.presentation.ui.capsule -import com.droidblossom.archive.domain.model.common.ContentUrl -import com.droidblossom.archive.domain.model.secret.SecretCapsuleDetail -import com.droidblossom.archive.presentation.ui.mypage.MyPageViewModel +import com.droidblossom.archive.domain.model.common.CapsuleDetail import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow @@ -10,7 +8,7 @@ interface CapsuleDetailViewModel { val detailEvents : SharedFlow - val capsuleDetail :StateFlow + val capsuleDetail :StateFlow fun getSecretCapsuleDetail(id:Long) sealed class DetailEvent { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/capsule/CapsuleDetailViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/capsule/CapsuleDetailViewModelImpl.kt index b10613f59..0fdbd993e 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/capsule/CapsuleDetailViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/capsule/CapsuleDetailViewModelImpl.kt @@ -2,15 +2,12 @@ package com.droidblossom.archive.presentation.ui.capsule import android.util.Log import androidx.lifecycle.viewModelScope -import com.droidblossom.archive.domain.model.common.ContentType -import com.droidblossom.archive.domain.model.common.ContentUrl -import com.droidblossom.archive.domain.model.secret.SecretCapsuleDetail +import com.droidblossom.archive.domain.model.common.CapsuleDetail import com.droidblossom.archive.domain.usecase.secret.SecretCapsuleDetailUseCase import com.droidblossom.archive.presentation.base.BaseViewModel import com.droidblossom.archive.util.onFail import com.droidblossom.archive.util.onSuccess import dagger.hilt.android.lifecycle.HiltViewModel -import dagger.hilt.android.scopes.ViewModelScoped import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow @@ -28,8 +25,8 @@ class CapsuleDetailViewModelImpl @Inject constructor( override val detailEvents: SharedFlow get() = _detailEvent.asSharedFlow() - private val _capsuleDetail = MutableStateFlow(SecretCapsuleDetail()) - override val capsuleDetail: StateFlow + private val _capsuleDetail = MutableStateFlow(CapsuleDetail()) + override val capsuleDetail: StateFlow get() = _capsuleDetail override fun getSecretCapsuleDetail(id: Long) { From 108b78b083fffba09b810307c33f2a298c9e9b04 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Thu, 4 Apr 2024 13:47:30 +0900 Subject: [PATCH 150/301] =?UTF-8?q?feat:=20=EA=B3=B5=EA=B0=9C=EC=BA=A1?= =?UTF-8?q?=EC=8A=90=20=EB=94=94=ED=85=8C=EC=9D=BC=20repo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/data/repository/PublicRepositoryImpl.kt | 6 ++++++ .../archive/domain/repository/PublicRepository.kt | 3 +++ 2 files changed, 9 insertions(+) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/PublicRepositoryImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/PublicRepositoryImpl.kt index 1d597b6e3..ce2ca8933 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/PublicRepositoryImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/PublicRepositoryImpl.kt @@ -2,9 +2,11 @@ package com.droidblossom.archive.data.repository import com.droidblossom.archive.data.dto.ResponseBody import com.droidblossom.archive.data.dto.common.CapsuleCreateRequestDto +import com.droidblossom.archive.data.dto.common.CapsuleDetailResponseDto import com.droidblossom.archive.data.dto.common.CapsuleSummaryResponseDto import com.droidblossom.archive.data.dto.common.toModel import com.droidblossom.archive.data.source.remote.api.PublicService +import com.droidblossom.archive.domain.model.common.CapsuleDetail import com.droidblossom.archive.domain.model.common.CapsuleSummaryResponse import com.droidblossom.archive.domain.repository.PublicRepository import com.droidblossom.archive.util.RetrofitResult @@ -21,4 +23,8 @@ class PublicRepositoryImpl @Inject constructor( override suspend fun getPublicCapsuleSummary(capsuleId: Int): RetrofitResult { return apiHandler({ api.getPublicCapsuleSummaryApi(capsuleId) }) { response: ResponseBody -> response.result.toModel()} } + + override suspend fun getPublicCapsuleDetail(capsuleId: Long): RetrofitResult { + return apiHandler({ api.getPublicCapsuleDetailApi(capsuleId) }) { response: ResponseBody -> response.result.toModel()} + } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/PublicRepository.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/PublicRepository.kt index a86e5ba41..2582b7712 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/PublicRepository.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/PublicRepository.kt @@ -1,6 +1,7 @@ package com.droidblossom.archive.domain.repository import com.droidblossom.archive.data.dto.common.CapsuleCreateRequestDto +import com.droidblossom.archive.domain.model.common.CapsuleDetail import com.droidblossom.archive.domain.model.common.CapsuleSummaryResponse import com.droidblossom.archive.util.RetrofitResult @@ -10,4 +11,6 @@ interface PublicRepository { suspend fun getPublicCapsuleSummary (capsuleId: Int) : RetrofitResult + suspend fun getPublicCapsuleDetail (capsuleId: Long) : RetrofitResult + } \ No newline at end of file From 7a66bf3b87bcde7cf7d88328bd241a60ea085453 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Thu, 4 Apr 2024 13:48:47 +0900 Subject: [PATCH 151/301] =?UTF-8?q?feat:=20=EA=B3=B5=EA=B0=9C=EC=BA=A1?= =?UTF-8?q?=EC=8A=90=20=EB=94=94=ED=85=8C=EC=9D=BC=20usecase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/source/remote/api/PublicService.kt | 2 +- .../open/PublicCapsuleDetailUseCase.kt | 29 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/open/PublicCapsuleDetailUseCase.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/PublicService.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/PublicService.kt index 0f0263892..974656806 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/PublicService.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/PublicService.kt @@ -24,7 +24,7 @@ interface PublicService { @GET("public/capsules/{capsule_id}/detail") suspend fun getPublicCapsuleDetailApi( - @Path("capsule_id") capsuleId : Int, + @Path("capsule_id") capsuleId : Long, ) : Response> } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/open/PublicCapsuleDetailUseCase.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/open/PublicCapsuleDetailUseCase.kt new file mode 100644 index 000000000..16a25f340 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/open/PublicCapsuleDetailUseCase.kt @@ -0,0 +1,29 @@ +package com.droidblossom.archive.domain.usecase.open + +import android.util.Log +import com.droidblossom.archive.domain.repository.PublicRepository +import com.droidblossom.archive.util.onException +import com.droidblossom.archive.util.onFail +import com.droidblossom.archive.util.onSuccess +import kotlinx.coroutines.flow.flow +import javax.inject.Inject + +class PublicCapsuleDetailUseCase @Inject constructor( + private val repository: PublicRepository +) { + suspend operator fun invoke(capsuleId :Long) = + flow { + try { + emit(repository.getPublicCapsuleDetail(capsuleId).onSuccess { + + }.onFail { + + }.onException { + throw Exception(it) + }) + } catch (e: Exception) { + Log.d("예외확인", "$e") + e.printStackTrace() + } + } +} \ No newline at end of file From c4b6999e5fcd62b526b5129cfe00acf3c0cfb2b9 Mon Sep 17 00:00:00 2001 From: comst19 Date: Thu, 4 Apr 2024 14:42:44 +0900 Subject: [PATCH 152/301] =?UTF-8?q?feat:=20=EA=B3=B5=EA=B0=9C=20=EC=BA=A1?= =?UTF-8?q?=EC=8A=90=20=EC=9A=94=EC=95=BD=20=EC=A1=B0=ED=9A=8C=20api=20?= =?UTF-8?q?=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CreateCapsuleViewModelImpl.kt | 24 ++++++++++++++++- .../dialog/CapsulePreviewDialogFragment.kt | 3 ++- .../dialog/CapsulePreviewDialogViewModel.kt | 2 ++ .../CapsulePreviewDialogViewModelImpl.kt | 26 ++++++++++++++----- 4 files changed, 47 insertions(+), 8 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt index e708efa3a..4c1d1d158 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt @@ -13,6 +13,7 @@ import com.droidblossom.archive.domain.model.s3.S3UrlRequest import com.droidblossom.archive.domain.model.common.CapsuleCreateRequest import com.droidblossom.archive.domain.usecase.capsule.GetAddressUseCase import com.droidblossom.archive.domain.usecase.capsule_skin.CapsuleSkinsPageUseCase +import com.droidblossom.archive.domain.usecase.open.PublicCapsuleCreateUseCase import com.droidblossom.archive.domain.usecase.s3.S3UrlsGetUseCase import com.droidblossom.archive.domain.usecase.secret.SecretCapsuleCreateUseCase import com.droidblossom.archive.presentation.base.BaseViewModel @@ -38,6 +39,7 @@ import javax.inject.Inject class CreateCapsuleViewModelImpl @Inject constructor( private val getAddressUseCase: GetAddressUseCase, private val secretCapsuleCreateUseCase: SecretCapsuleCreateUseCase, + private val publicCapsuleCreateUseCase: PublicCapsuleCreateUseCase, private val s3UrlsGetUseCase: S3UrlsGetUseCase, private val capsuleSkinsPageUseCase: CapsuleSkinsPageUseCase, private val s3Util: S3Util, @@ -523,7 +525,27 @@ class CreateCapsuleViewModelImpl @Inject constructor( viewModelScope.launch { when (capsuleTypeCreateIs.value) { CreateCapsuleViewModel.CapsuleTypeCreate.PUBLIC -> { - + publicCapsuleCreateUseCase( + CapsuleCreateRequest( + capsuleSkinId = skinId.value, + content = capsuleContent.value, + directory = S3DIRECTORY, + dueDate = dueTime.value, + imageNames = imageNames, + videoNames = videoNames, + addressData = address.value, + latitude = capsuleLatitude.value, + longitude = capsuleLongitude.value, + title = capsuleTitle.value, + ) + ).collect{ result -> + result.onSuccess { + _create3Events.emit(CreateCapsuleViewModel.Create3Event.ShowToastMessage("캡슐이 생성되었습니다.")) + Log.d("캡슐생성", "$it") + }.onFail { + _create3Events.emit(CreateCapsuleViewModel.Create3Event.ShowToastMessage("캡슐이 생성 실패했습니다..")) + } + } } CreateCapsuleViewModel.CapsuleTypeCreate.SECRET -> { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogFragment.kt index cb8d6bc85..485a1e498 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogFragment.kt @@ -77,7 +77,8 @@ class CapsulePreviewDialogFragment : } HomeFragment.CapsuleType.PUBLIC -> { - + viewModel.getPublicCapsuleSummary(capsuleId) + viewModel.setCapsuleTypeImage(R.drawable.ic_public_marker_24) } HomeFragment.CapsuleType.GROUP -> { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogViewModel.kt index 439eed4e9..48bb135dd 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogViewModel.kt @@ -33,6 +33,8 @@ interface CapsulePreviewDialogViewModel { fun setCalledFromCamera(calledFromCamera : Boolean) + fun getSecretCapsuleSummary(capsuleId: Int) + fun getPublicCapsuleSummary(capsuleId: Int) sealed class CapsulePreviewDialogEvent{ data class ShowToastMessage(val message : String) : CapsulePreviewDialogEvent() diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogViewModelImpl.kt index 23ab5fd28..0e9d4f793 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogViewModelImpl.kt @@ -4,6 +4,7 @@ import android.util.Log import androidx.lifecycle.viewModelScope import com.droidblossom.archive.domain.model.common.CapsuleSummaryResponse import com.droidblossom.archive.domain.usecase.capsule.PatchCapsuleOpenedUseCase +import com.droidblossom.archive.domain.usecase.open.PublicCapsuleSummaryUseCase import com.droidblossom.archive.domain.usecase.secret.SecretCapsuleSummaryUseCase import com.droidblossom.archive.presentation.base.BaseViewModel import com.droidblossom.archive.util.onError @@ -28,15 +29,16 @@ import javax.inject.Inject @HiltViewModel class CapsulePreviewDialogViewModelImpl @Inject constructor( private val secretCapsuleSummaryUseCase: SecretCapsuleSummaryUseCase, + private val publicCapsuleSummaryUseCase: PublicCapsuleSummaryUseCase, private val patchCapsuleOpenedUseCase: PatchCapsuleOpenedUseCase ) : BaseViewModel(), CapsulePreviewDialogViewModel { private val _capsulePreviewDialogEvents = MutableSharedFlow() override val capsulePreviewDialogEvents: SharedFlow = _capsulePreviewDialogEvents.asSharedFlow() - private val _CapsuleSummaryResponse = MutableStateFlow(CapsuleSummaryResponse("","","","","","","", false, "")) + private val _capsuleSummaryResponse = MutableStateFlow(CapsuleSummaryResponse("","","","","","","", false, "")) override val capsuleSummaryResponse: StateFlow - get() = _CapsuleSummaryResponse + get() = _capsuleSummaryResponse private val _startTime = MutableStateFlow(null) private val _endTime = MutableStateFlow(null) @@ -84,20 +86,32 @@ class CapsulePreviewDialogViewModelImpl @Inject constructor( override fun setCalledFromCamera(calledFromCamera : Boolean){ _calledFromCamera.value = calledFromCamera } - fun getSecretCapsuleSummary(capsuleId: Int) { + override fun getSecretCapsuleSummary(capsuleId: Int) { viewModelScope.launch { secretCapsuleSummaryUseCase(capsuleId).collect { result -> result.onSuccess { - _CapsuleSummaryResponse.emit(it) + _capsuleSummaryResponse.emit(it) _capsuleOpenState.emit(capsuleSummaryResponse.value.isOpened) if (!capsuleOpenState.value){ calculateCapsuleOpenTime(it.createdAt, it.dueDate) } }.onFail { - }.onException { + } + } + } + } - }.onError { + override fun getPublicCapsuleSummary(capsuleId: Int) { + viewModelScope.launch { + publicCapsuleSummaryUseCase(capsuleId).collect { result -> + result.onSuccess { + _capsuleSummaryResponse.emit(it) + _capsuleOpenState.emit(capsuleSummaryResponse.value.isOpened) + if (!capsuleOpenState.value){ + calculateCapsuleOpenTime(it.createdAt, it.dueDate) + } + }.onFail { } } From 27d3199f20255b1ead8fafaedb15443564cb5e54 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Fri, 5 Apr 2024 22:21:59 +0900 Subject: [PATCH 153/301] =?UTF-8?q?fix=20:=20=EC=A3=BC=EC=86=8C=EB=A1=9D?= =?UTF-8?q?=20=EB=8D=B0=EC=9D=B4=ED=84=B0=EB=B0=94=EC=9D=B8=EB=94=A9?= =?UTF-8?q?=EC=97=90=20int=20=EB=AC=B8=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/droidblossom/archive/util/BindingAdapter.kt | 6 ++++++ .../src/main/res/layout/fragment_friend_search_number.xml | 5 +++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/BindingAdapter.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/BindingAdapter.kt index da626ce61..42dd79bec 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/BindingAdapter.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/BindingAdapter.kt @@ -11,6 +11,7 @@ import android.view.ViewGroup import android.widget.EditText import android.widget.ImageView import android.widget.TextView +import androidx.appcompat.widget.AppCompatTextView import androidx.cardview.widget.CardView import androidx.core.content.ContextCompat import androidx.databinding.BindingAdapter @@ -23,6 +24,11 @@ import de.hdodenhof.circleimageview.CircleImageView import java.text.SimpleDateFormat import java.util.Locale +@BindingAdapter("textInt") +fun AppCompatTextView.textInt(int: Int) { + this.text = int.toString() +} + @BindingAdapter(value = ["bind:imageUrl", "bind:placeholder"], requireAll = false) fun ImageView.setImage(imageUrl: Uri?, placeholder: Drawable?) { Glide.with(this.context) diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_friend_search_number.xml b/frontend/ARchive/app/src/main/res/layout/fragment_friend_search_number.xml index cb15be200..c00c1fc5b 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_friend_search_number.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_friend_search_number.xml @@ -1,7 +1,8 @@ + xmlns:tools="http://schemas.android.com/tools" + xmlns:bind="http://schemas.android.com/apk/res-auto"> @@ -182,7 +183,7 @@ android:background="@drawable/corner_radius_16" android:backgroundTint="@color/white" android:gravity="center" - android:text="@{vm.checkedList.size()}" + bind:textInt="@{vm.checkedList.size()}" android:textAppearance="@style/TextAppearance.App.subtitle1" android:textColor="@color/main_1" android:visibility="@{vm.checkedList.size() == 0 ?View.GONE : View.VISIBLE}" From 09be7538e8b08d98fb3373fc0b3eeff62e632cdb Mon Sep 17 00:00:00 2001 From: comst19 Date: Sat, 6 Apr 2024 12:01:50 +0900 Subject: [PATCH 154/301] =?UTF-8?q?design:=20CapsuleThumbnail=20=EB=B9=84?= =?UTF-8?q?=EC=9C=A8=20=EC=A1=B0=EC=A0=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/src/main/res/layout/item_social_capsule_open.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/ARchive/app/src/main/res/layout/item_social_capsule_open.xml b/frontend/ARchive/app/src/main/res/layout/item_social_capsule_open.xml index e875ae9bc..8cc05199b 100644 --- a/frontend/ARchive/app/src/main/res/layout/item_social_capsule_open.xml +++ b/frontend/ARchive/app/src/main/res/layout/item_social_capsule_open.xml @@ -128,7 +128,7 @@ app:cardCornerRadius="19.5dp" android:layout_marginTop="@dimen/margin" app:layout_constraintTop_toBottomOf="@id/capsuleContentTextView" - app:layout_constraintDimensionRatio="w,1:2"> + app:layout_constraintDimensionRatio="w,151:264"> Date: Sat, 6 Apr 2024 23:18:31 +0900 Subject: [PATCH 155/301] =?UTF-8?q?feat:=20=EC=A7=80=EB=8F=84=EC=97=90=20?= =?UTF-8?q?=EB=B0=94=EC=9A=B4=EB=8D=94=EB=A6=AC=20=EC=A7=80=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/presentation/ui/home/HomeFragment.kt | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt index 12e992b35..3697b4002 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt @@ -21,6 +21,8 @@ import com.droidblossom.archive.presentation.ui.home.notification.NotificationAc import com.droidblossom.archive.util.CapsuleTypeUtils import com.droidblossom.archive.util.LocationUtil import com.naver.maps.geometry.LatLng +import com.naver.maps.geometry.LatLngBounds +import com.naver.maps.map.CameraAnimation import com.naver.maps.map.CameraUpdate import com.naver.maps.map.LocationTrackingMode import com.naver.maps.map.MapFragment @@ -176,6 +178,12 @@ class HomeFragment : BaseFragment(R.layo naverMap.locationTrackingMode = LocationTrackingMode.Follow naverMap.minZoom = MINZOOM naverMap.maxZoom = MAXZOOM + + val southWest = LatLng(MINLAT, MINLNG) + val northEast = LatLng(MAXLAT, MAXLNG) + val koreaBounds = LatLngBounds(southWest, northEast) + naverMap.extent = koreaBounds + LocationUtil(requireContext()).getCurrentLocation { latitude, longitude -> val cameraUpdate = CameraUpdate.scrollTo(LatLng(latitude, longitude)) naverMap.moveCamera(cameraUpdate) @@ -280,9 +288,13 @@ class HomeFragment : BaseFragment(R.layo fun newIntent() = HomeFragment() private const val LOCATION_PERMISSION_REQUEST_CODE = 1000 - const val MAXZOOM = 18.0 - const val MINZOOM = 6.0 + const val MAXZOOM = 20.0 + const val MINZOOM = 5.5 const val FIXZOOM = 14.0 + const val MINLAT = 32.0 + const val MAXLAT = 43.0 + const val MINLNG = 124.0 + const val MAXLNG = 132.0 } } \ No newline at end of file From fc25617bcffdb962a48a7eb103bfe56c2dca4912 Mon Sep 17 00:00:00 2001 From: comst19 Date: Sat, 6 Apr 2024 23:25:28 +0900 Subject: [PATCH 156/301] =?UTF-8?q?feat:=20=EC=B9=B4=EB=A9=94=EB=9D=BC=20?= =?UTF-8?q?=EC=B4=88=EC=A0=90=EC=9D=98=20=EC=9C=84=EA=B2=BD=EB=8F=84?= =?UTF-8?q?=EB=A5=BC=20=EB=B0=94=ED=83=95=EC=9C=BC=EB=A1=9C=20=EC=BA=A1?= =?UTF-8?q?=EC=8A=90=20=EC=A1=B0=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/presentation/ui/home/HomeFragment.kt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt index 3697b4002..77d7aec57 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt @@ -3,6 +3,7 @@ package com.droidblossom.archive.presentation.ui.home import android.graphics.Point import android.location.Location import android.os.Bundle +import android.util.Log import android.view.View import android.view.ViewGroup import androidx.core.content.ContextCompat @@ -72,14 +73,13 @@ class HomeFragment : BaseFragment(R.layo startActivity(CreateCapsuleActivity.newIntent(requireContext(), 3)) } refreshBtn.setOnClickListener { - locationUtil.getCurrentLocation { latitude, longitude -> - viewModel.getNearbyCapsules( - latitude, - longitude, - calculateRadiusForZoomLevel(), - viewModel.filterCapsuleSelect.value.toString() - ) - } + val cameraTarget = naverMap.cameraPosition.target + viewModel.getNearbyCapsules( + cameraTarget.latitude, + cameraTarget.longitude, + calculateRadiusForZoomLevel(), + viewModel.filterCapsuleSelect.value.toString() + ) } From 64307bd054bc2d46f37e6b4e8b4708258b330746 Mon Sep 17 00:00:00 2001 From: comst19 Date: Sun, 7 Apr 2024 17:47:23 +0900 Subject: [PATCH 157/301] =?UTF-8?q?feat:=20=EC=A7=80=EB=8F=84=EC=97=90=20?= =?UTF-8?q?=EB=A7=88=EC=BB=A4=EB=93=A4=20=ED=81=B4=EB=9F=AC=EC=8A=A4?= =?UTF-8?q?=ED=84=B0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/ARchive/app/build.gradle | 4 + .../presentation/ui/home/HomeFragment.kt | 190 +++++++++++------- .../presentation/ui/home/MapCapsuleMarker.kt | 15 ++ .../main/res/layout/item_marker_cluster.xml | 48 +++++ 4 files changed, 189 insertions(+), 68 deletions(-) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/MapCapsuleMarker.kt create mode 100644 frontend/ARchive/app/src/main/res/layout/item_marker_cluster.xml diff --git a/frontend/ARchive/app/build.gradle b/frontend/ARchive/app/build.gradle index 340099232..df3b7b11b 100644 --- a/frontend/ARchive/app/build.gradle +++ b/frontend/ARchive/app/build.gradle @@ -182,6 +182,10 @@ dependencies { // oss-licenses : https://github.com/google/play-services-plugins/tree/master/oss-licenses-plugin implementation 'com.google.android.gms:play-services-oss-licenses:17.0.1' + + // TedNaverMapClustering: https://github.com/ParkSangGwon/TedNaverMapClustering + //implementation 'io.github.ParkSangGwon:tedclustering-naver:x.y.z' + implementation 'io.github.ParkSangGwon:tedclustering-naver:1.0.2' } kapt { correctErrorTypes true diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt index 77d7aec57..620bc49e7 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt @@ -3,9 +3,10 @@ package com.droidblossom.archive.presentation.ui.home import android.graphics.Point import android.location.Location import android.os.Bundle -import android.util.Log +import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.TextView import androidx.core.content.ContextCompat import androidx.core.graphics.toPointF import androidx.fragment.app.viewModels @@ -23,7 +24,6 @@ import com.droidblossom.archive.util.CapsuleTypeUtils import com.droidblossom.archive.util.LocationUtil import com.naver.maps.geometry.LatLng import com.naver.maps.geometry.LatLngBounds -import com.naver.maps.map.CameraAnimation import com.naver.maps.map.CameraUpdate import com.naver.maps.map.LocationTrackingMode import com.naver.maps.map.MapFragment @@ -36,6 +36,7 @@ import com.naver.maps.map.overlay.OverlayImage import com.naver.maps.map.util.FusedLocationSource import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch +import ted.gun0912.clustering.naver.TedNaverClustering @AndroidEntryPoint class HomeFragment : BaseFragment(R.layout.fragment_home), @@ -46,8 +47,7 @@ class HomeFragment : BaseFragment(R.layo private lateinit var naverMap: NaverMap private lateinit var locationUtil: LocationUtil private lateinit var locationSource: FusedLocationSource - private val markers: MutableList = mutableListOf() - + private lateinit var naverCluster: TedNaverClustering override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) locationUtil = LocationUtil(requireContext()) @@ -73,6 +73,7 @@ class HomeFragment : BaseFragment(R.layo startActivity(CreateCapsuleActivity.newIntent(requireContext(), 3)) } refreshBtn.setOnClickListener { + naverCluster.clearItems() val cameraTarget = naverMap.cameraPosition.target viewModel.getNearbyCapsules( cameraTarget.latitude, @@ -92,15 +93,15 @@ class HomeFragment : BaseFragment(R.layo repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.filterCapsuleSelect.collect { viewModel.resetNearbyCapsules() - locationUtil.getCurrentLocation { latitude, longitude -> - if (::naverMap.isInitialized) { - viewModel.getNearbyCapsules( - latitude, - longitude, - calculateRadiusForZoomLevel(), - viewModel.filterCapsuleSelect.value.toString() - ) - } + if (::naverMap.isInitialized) { + naverCluster.clearItems() + val cameraTarget = naverMap.cameraPosition.target + viewModel.getNearbyCapsules( + cameraTarget.latitude, + cameraTarget.longitude, + calculateRadiusForZoomLevel(), + viewModel.filterCapsuleSelect.value.toString() + ) } } } @@ -115,7 +116,12 @@ class HomeFragment : BaseFragment(R.layo } is HomeViewModel.HomeEvent.ShowCapsulePreviewDialog -> { - val sheet = CapsulePreviewDialogFragment.newInstance("-1",event.capsuleId, event.capsuleType, false) + val sheet = CapsulePreviewDialogFragment.newInstance( + "-1", + event.capsuleId, + event.capsuleType, + false + ) sheet.show(parentFragmentManager, "CapsulePreviewDialog") } @@ -153,8 +159,10 @@ class HomeFragment : BaseFragment(R.layo viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.capsuleList.collect { - removeAllMarkers() - it.map { capsule -> addMarker(capsule) } + if (::naverMap.isInitialized) { + naverCluster.clearItems() + naverCluster.addItems(getItems(it)) + } } } } @@ -174,6 +182,7 @@ class HomeFragment : BaseFragment(R.layo override fun onMapReady(naverMap: NaverMap) { this.naverMap = naverMap + naverMap.uiSettings.isRotateGesturesEnabled = false naverMap.locationSource = locationSource naverMap.locationTrackingMode = LocationTrackingMode.Follow naverMap.minZoom = MINZOOM @@ -187,75 +196,89 @@ class HomeFragment : BaseFragment(R.layo LocationUtil(requireContext()).getCurrentLocation { latitude, longitude -> val cameraUpdate = CameraUpdate.scrollTo(LatLng(latitude, longitude)) naverMap.moveCamera(cameraUpdate) - } with(naverMap.locationOverlay) { isVisible = true icon = OverlayImage.fromResource(R.drawable.ic_my_location_24) } - } - - private fun addMarker(capsuleMarker: CapsuleMarker) { - val marker = Marker().apply { - position = LatLng(capsuleMarker.latitude, capsuleMarker.longitude) - icon = when (capsuleMarker.capsuleType) { - CapsuleType.SECRET -> OverlayImage.fromResource(R.drawable.ic_marker_pin_secret) - CapsuleType.GROUP -> OverlayImage.fromResource(R.drawable.ic_marker_pin_group) - CapsuleType.PUBLIC -> OverlayImage.fromResource(R.drawable.ic_marker_pin_public) - } - map = naverMap - - tag = hashMapOf( - "id" to capsuleMarker.id.toString(), - "type" to CapsuleTypeUtils.enumToString(capsuleMarker.capsuleType) - ) - onClickListener = Overlay.OnClickListener { overlay -> - val clickedMarker = overlay as Marker - val markerData = clickedMarker.tag as? HashMap<*, *> - val capsuleId = markerData?.get("id") as? String - val capsuleType = markerData?.get("type") as? String - if (capsuleId != null && capsuleType != null) { - viewModel.homeEvent( - HomeViewModel.HomeEvent.ShowCapsulePreviewDialog( - capsuleId, - capsuleType - ) + naverCluster = TedNaverClustering.with(requireContext(), naverMap) + .markerClickListener { capsuleMarker -> + viewModel.homeEvent( + HomeViewModel.HomeEvent.ShowCapsulePreviewDialog( + capsuleMarker.capsuleMarker.id.toString(), + capsuleMarker.capsuleMarker.capsuleType.toString() ) + ) + + }.customMarker { capsuleMarker -> + Marker().apply { + icon = when (capsuleMarker.capsuleMarker.capsuleType) { + CapsuleType.SECRET -> OverlayImage.fromResource(R.drawable.ic_marker_pin_secret) + CapsuleType.GROUP -> OverlayImage.fromResource(R.drawable.ic_marker_pin_group) + CapsuleType.PUBLIC -> OverlayImage.fromResource(R.drawable.ic_marker_pin_public) + } + } + }.customCluster { cluster -> + val clusterView = LayoutInflater.from(requireContext()).inflate(R.layout.item_marker_cluster, null) + val clusterSize = clusterView.findViewById(R.id.capsuleNumTextView) + + val displayText = when { + cluster.items.size < 10 -> cluster.items.size.toString() + cluster.items.size % 10 == 0 -> cluster.items.size.toString() + else -> "${(cluster.items.size / 10) * 10}+" } - true + + clusterSize.text = displayText + clusterView } - } - markers.add(marker) + .make() } - private fun removeAllMarkers() { - markers.forEach { it.map = null } - markers.clear() + + private fun getItems(capsuleList: List): List { + val markers = ArrayList() + capsuleList.map { capsuleMarker -> + val temp = MapCapsuleMarker(capsuleMarker) + markers.add(temp) + } +// val bounds = naverMap.contentBounds +// for (i in 0..49) { +// val temp = MapCapsuleMarker( +// CapsuleMarker( +// id = 0, +// longitude = (bounds.eastLongitude - bounds.westLongitude) * Math.random() + bounds.westLongitude, +// latitude = (bounds.northLatitude - bounds.southLatitude) * Math.random() + bounds.southLatitude, +// capsuleType = when (i % 3) { +// 0 -> CapsuleType.SECRET +// 1 -> CapsuleType.PUBLIC +// 2 -> CapsuleType.GROUP +// else -> CapsuleType.SECRET +// }, +// skinUrl = "" +// ) +// ) +// markers.add(temp) +// } + return markers } - // 구현은 했는데 이렇게하면 한국 전체에 생성된 캡슐을 찾기가 어려움 - private fun calculateDistanceInKilometers(): Double { - val projection: Projection = naverMap.projection - val latLng = projection.fromScreenLocation(Point(0, 0).toPointF()) + private fun calculateRadiusForZoomLevel(): Double { + val earthRadius = 6371.01 - val centerLatLng = naverMap.cameraPosition.target + val latDistance = Math.toRadians(MAXLAT - MINLAT) + val lngDistance = Math.toRadians(MAXLNG - MINLNG) - val distanceInMeters = FloatArray(1) - Location.distanceBetween( - centerLatLng.latitude, centerLatLng.longitude, - latLng.latitude, latLng.longitude, - distanceInMeters - ) + val a = Math.sin(latDistance / 2) * Math.sin(latDistance / 2) + + Math.cos(Math.toRadians(MINLAT)) * Math.cos(Math.toRadians(MAXLAT)) * + Math.sin(lngDistance / 2) * Math.sin(lngDistance / 2) - return distanceInMeters[0] / 1000.0 - } + val c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)) + + val maxRadius = earthRadius * c - fun calculateRadiusForZoomLevel(): Double { - val radiusAtMinZoom = 550.0 - val zoomDifference = naverMap.cameraPosition.zoom - MINZOOM - return radiusAtMinZoom * Math.pow(2.0, -zoomDifference) + return maxRadius } override fun onRequestPermissionsResult( @@ -283,12 +306,43 @@ class HomeFragment : BaseFragment(R.layo PUBLIC } + override fun onResume() { + super.onResume() + locationUtil.getCurrentLocation { latitude, longitude -> + if (::naverMap.isInitialized) { + naverCluster.clearItems() + viewModel.getNearbyCapsules( + latitude, + longitude, + calculateRadiusForZoomLevel(), + viewModel.filterCapsuleSelect.value.toString() + ) + } + } + } + + override fun onHiddenChanged(hidden: Boolean) { + super.onHiddenChanged(hidden) + if (!hidden){ + if (::naverMap.isInitialized) { + naverCluster.clearItems() + val cameraTarget = naverMap.cameraPosition.target + viewModel.getNearbyCapsules( + cameraTarget.latitude, + cameraTarget.longitude, + calculateRadiusForZoomLevel(), + viewModel.filterCapsuleSelect.value.toString() + ) + } + } + } + companion object { const val TAG = "homeFragment" fun newIntent() = HomeFragment() private const val LOCATION_PERMISSION_REQUEST_CODE = 1000 - const val MAXZOOM = 20.0 + const val MAXZOOM = 21.0 const val MINZOOM = 5.5 const val FIXZOOM = 14.0 const val MINLAT = 32.0 diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/MapCapsuleMarker.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/MapCapsuleMarker.kt new file mode 100644 index 000000000..9a88e7062 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/MapCapsuleMarker.kt @@ -0,0 +1,15 @@ +package com.droidblossom.archive.presentation.ui.home + +import com.droidblossom.archive.domain.model.common.CapsuleMarker +import com.naver.maps.geometry.LatLng +import ted.gun0912.clustering.clustering.TedClusterItem +import ted.gun0912.clustering.geometry.TedLatLng + +data class MapCapsuleMarker( + val capsuleMarker: CapsuleMarker +) : TedClusterItem { + + override fun getTedLatLng(): TedLatLng = TedLatLng(capsuleMarker.latitude,capsuleMarker.longitude) + + +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/item_marker_cluster.xml b/frontend/ARchive/app/src/main/res/layout/item_marker_cluster.xml new file mode 100644 index 000000000..8655abc19 --- /dev/null +++ b/frontend/ARchive/app/src/main/res/layout/item_marker_cluster.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + \ No newline at end of file From 53073ff98a753b2f8980acea5fbe30e67711d7e5 Mon Sep 17 00:00:00 2001 From: comst19 Date: Sun, 7 Apr 2024 18:00:08 +0900 Subject: [PATCH 158/301] =?UTF-8?q?refact:=20=EC=A4=91=EB=B3=B5=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=EB=A5=BC=20=ED=95=A8=EC=88=98=EB=A1=9C=20?= =?UTF-8?q?=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ui/home/HomeFragment.kt | 82 ++++++------------- 1 file changed, 26 insertions(+), 56 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt index 620bc49e7..8e83d1b2d 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt @@ -73,17 +73,9 @@ class HomeFragment : BaseFragment(R.layo startActivity(CreateCapsuleActivity.newIntent(requireContext(), 3)) } refreshBtn.setOnClickListener { - naverCluster.clearItems() - val cameraTarget = naverMap.cameraPosition.target - viewModel.getNearbyCapsules( - cameraTarget.latitude, - cameraTarget.longitude, - calculateRadiusForZoomLevel(), - viewModel.filterCapsuleSelect.value.toString() - ) + fetchCapsulesInCameraFocus() } - } } @@ -94,14 +86,7 @@ class HomeFragment : BaseFragment(R.layo viewModel.filterCapsuleSelect.collect { viewModel.resetNearbyCapsules() if (::naverMap.isInitialized) { - naverCluster.clearItems() - val cameraTarget = naverMap.cameraPosition.target - viewModel.getNearbyCapsules( - cameraTarget.latitude, - cameraTarget.longitude, - calculateRadiusForZoomLevel(), - viewModel.filterCapsuleSelect.value.toString() - ) + fetchCapsulesInCameraFocus() } } } @@ -242,27 +227,31 @@ class HomeFragment : BaseFragment(R.layo val temp = MapCapsuleMarker(capsuleMarker) markers.add(temp) } -// val bounds = naverMap.contentBounds -// for (i in 0..49) { -// val temp = MapCapsuleMarker( -// CapsuleMarker( -// id = 0, -// longitude = (bounds.eastLongitude - bounds.westLongitude) * Math.random() + bounds.westLongitude, -// latitude = (bounds.northLatitude - bounds.southLatitude) * Math.random() + bounds.southLatitude, -// capsuleType = when (i % 3) { -// 0 -> CapsuleType.SECRET -// 1 -> CapsuleType.PUBLIC -// 2 -> CapsuleType.GROUP -// else -> CapsuleType.SECRET -// }, -// skinUrl = "" -// ) -// ) -// markers.add(temp) -// } return markers } + private fun fetchCapsulesNearUser(){ + locationUtil.getCurrentLocation { latitude, longitude -> + viewModel.getNearbyCapsules( + latitude, + longitude, + calculateRadiusForZoomLevel(), + viewModel.filterCapsuleSelect.value.toString() + ) + } + } + + private fun fetchCapsulesInCameraFocus(){ + if (::naverMap.isInitialized){ + val cameraTarget = naverMap.cameraPosition.target + viewModel.getNearbyCapsules( + cameraTarget.latitude, + cameraTarget.longitude, + calculateRadiusForZoomLevel(), + viewModel.filterCapsuleSelect.value.toString() + ) + } + } private fun calculateRadiusForZoomLevel(): Double { val earthRadius = 6371.01 @@ -308,32 +297,13 @@ class HomeFragment : BaseFragment(R.layo override fun onResume() { super.onResume() - locationUtil.getCurrentLocation { latitude, longitude -> - if (::naverMap.isInitialized) { - naverCluster.clearItems() - viewModel.getNearbyCapsules( - latitude, - longitude, - calculateRadiusForZoomLevel(), - viewModel.filterCapsuleSelect.value.toString() - ) - } - } + fetchCapsulesNearUser() } override fun onHiddenChanged(hidden: Boolean) { super.onHiddenChanged(hidden) if (!hidden){ - if (::naverMap.isInitialized) { - naverCluster.clearItems() - val cameraTarget = naverMap.cameraPosition.target - viewModel.getNearbyCapsules( - cameraTarget.latitude, - cameraTarget.longitude, - calculateRadiusForZoomLevel(), - viewModel.filterCapsuleSelect.value.toString() - ) - } + fetchCapsulesInCameraFocus() } } From dfede271a229b125368423685ddd4f1f41130dc6 Mon Sep 17 00:00:00 2001 From: comst19 Date: Sun, 7 Apr 2024 23:06:34 +0900 Subject: [PATCH 159/301] =?UTF-8?q?perf:=20=EC=A4=8C=20=EB=A0=88=EB=B2=A8?= =?UTF-8?q?=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EB=B0=98=EA=B2=BD=20=EA=B0=92?= =?UTF-8?q?=EC=9D=84=20=EC=82=AC=EC=A0=84=20=EA=B3=84=EC=82=B0=ED=95=98?= =?UTF-8?q?=EC=97=AC=20=EB=A7=A4=ED=95=91=ED=95=98=EC=97=AC=20=EC=84=B1?= =?UTF-8?q?=EB=8A=A5=20=EC=B5=9C=EC=A0=81=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ui/home/HomeFragment.kt | 64 +++++++++++-------- 1 file changed, 39 insertions(+), 25 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt index 8e83d1b2d..d5806b279 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt @@ -3,6 +3,7 @@ package com.droidblossom.archive.presentation.ui.home import android.graphics.Point import android.location.Location import android.os.Bundle +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -37,6 +38,7 @@ import com.naver.maps.map.util.FusedLocationSource import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch import ted.gun0912.clustering.naver.TedNaverClustering +import kotlin.math.pow @AndroidEntryPoint class HomeFragment : BaseFragment(R.layout.fragment_home), @@ -48,6 +50,19 @@ class HomeFragment : BaseFragment(R.layo private lateinit var locationUtil: LocationUtil private lateinit var locationSource: FusedLocationSource private lateinit var naverCluster: TedNaverClustering + + private val zoomToRadiusMap: Map by lazy { + val map = mutableMapOf() + val maxRadius = 1100.0 + val minRadius = 2.0 + + for (zoomLevel in MINZOOM.toInt()..MAXZOOM.toInt()) { + val normalizedZoom = 1 - ((zoomLevel - MINZOOM) / (MAXZOOM - MINZOOM)) + val radius = minRadius + (maxRadius - minRadius) * normalizedZoom.pow(3) + map[zoomLevel.toDouble()] = radius + } + map.toMap() + } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) locationUtil = LocationUtil(requireContext()) @@ -85,9 +100,7 @@ class HomeFragment : BaseFragment(R.layo repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.filterCapsuleSelect.collect { viewModel.resetNearbyCapsules() - if (::naverMap.isInitialized) { - fetchCapsulesInCameraFocus() - } + fetchCapsulesInCameraFocus() } } } @@ -231,13 +244,24 @@ class HomeFragment : BaseFragment(R.layo } private fun fetchCapsulesNearUser(){ - locationUtil.getCurrentLocation { latitude, longitude -> - viewModel.getNearbyCapsules( - latitude, - longitude, - calculateRadiusForZoomLevel(), - viewModel.filterCapsuleSelect.value.toString() - ) + if (::naverMap.isInitialized){ + locationUtil.getCurrentLocation { latitude, longitude -> + viewModel.getNearbyCapsules( + latitude, + longitude, + getRadiusForCurrentZoom(), + viewModel.filterCapsuleSelect.value.toString() + ) + } + }else{ + locationUtil.getCurrentLocation { latitude, longitude -> + viewModel.getNearbyCapsules( + latitude, + longitude, + 4.0, + viewModel.filterCapsuleSelect.value.toString() + ) + } } } @@ -247,27 +271,17 @@ class HomeFragment : BaseFragment(R.layo viewModel.getNearbyCapsules( cameraTarget.latitude, cameraTarget.longitude, - calculateRadiusForZoomLevel(), + getRadiusForCurrentZoom(), viewModel.filterCapsuleSelect.value.toString() ) } } - private fun calculateRadiusForZoomLevel(): Double { - val earthRadius = 6371.01 - - val latDistance = Math.toRadians(MAXLAT - MINLAT) - val lngDistance = Math.toRadians(MAXLNG - MINLNG) - - val a = Math.sin(latDistance / 2) * Math.sin(latDistance / 2) + - Math.cos(Math.toRadians(MINLAT)) * Math.cos(Math.toRadians(MAXLAT)) * - Math.sin(lngDistance / 2) * Math.sin(lngDistance / 2) - - val c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)) - - val maxRadius = earthRadius * c - return maxRadius + private fun getRadiusForCurrentZoom(): Double { + val currentZoom = naverMap.cameraPosition.zoom + val closestZoomLevel = zoomToRadiusMap.keys.minByOrNull { Math.abs(it - currentZoom) } + return zoomToRadiusMap[closestZoomLevel] ?: throw IllegalArgumentException("Invalid zoom level: $currentZoom") } override fun onRequestPermissionsResult( From a1bf950402f8716b12329c0f82c62af4c4eccfbb Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Mon, 8 Apr 2024 10:55:11 +0900 Subject: [PATCH 160/301] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC=20item?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/mypage/friend/adapter/FriendRVA.kt | 104 ++++++++++++++ .../main/res/layout/fragment_list_friend.xml | 136 +++++++++++++++++- .../app/src/main/res/layout/item_friend.xml | 94 ++++++++++++ 3 files changed, 328 insertions(+), 6 deletions(-) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/adapter/FriendRVA.kt create mode 100644 frontend/ARchive/app/src/main/res/layout/item_friend.xml diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/adapter/FriendRVA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/adapter/FriendRVA.kt new file mode 100644 index 000000000..3134140dd --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/adapter/FriendRVA.kt @@ -0,0 +1,104 @@ +package com.droidblossom.archive.presentation.ui.mypage.friend.adapter + +import android.annotation.SuppressLint +import android.content.Context +import android.gesture.Gesture +import android.gesture.GestureOverlayView +import android.util.Log +import android.view.GestureDetector +import android.view.LayoutInflater +import android.view.MotionEvent +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import com.droidblossom.archive.databinding.ItemFriendBinding +import com.droidblossom.archive.domain.model.friend.Friend + + +class FriendRVA(private val context: Context, private val click: (Int?,Int) -> Unit) : + ListAdapter(differ) { + + private var previousClickedPosition: Int? = null + + inner class ItemViewHolder( + private val binding: ItemFriendBinding + ) : RecyclerView.ViewHolder(binding.root) { + + @SuppressLint("ClickableViewAccessibility") + fun bind(data: Friend) { + binding.item = data + + val detector = GestureDetector(context, object : GestureDetector.OnGestureListener { + override fun onDown(p0: MotionEvent) = true + override fun onShowPress(p0: MotionEvent) = Unit + override fun onSingleTapUp(p0: MotionEvent) = true + override fun onScroll( + p0: MotionEvent?, p1: MotionEvent, + p2: Float, p3: Float + ): Boolean { + val currentClickedPosition = bindingAdapterPosition + if (currentClickedPosition != RecyclerView.NO_POSITION) { + click(previousClickedPosition, currentClickedPosition) + previousClickedPosition?.let { previousClickedPosition -> + notifyItemChanged(previousClickedPosition) + } + notifyItemChanged(currentClickedPosition) + previousClickedPosition = currentClickedPosition + } + Log.d("아이템 ", "onScroll() called : $p0") + return true + } + override fun onLongPress(p0: MotionEvent) = Unit + override fun onFling(p0: MotionEvent?, p1: MotionEvent, p2: Float, p3: Float) = true + }) + binding.root.setOnTouchListener { _, event -> + detector.onTouchEvent(event) + return@setOnTouchListener false + } + + + binding.root.setOnGenericMotionListener { _, motionEvent -> + when (motionEvent.action) { + MotionEvent.ACTION_SCROLL -> { + Log.d("아이탬", "tqtqt") + } + } + + return@setOnGenericMotionListener false + } + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder { + return ItemViewHolder( + ItemFriendBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) + } + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + holder.bind(getItem(position)) + } + + companion object { + val differ = object : DiffUtil.ItemCallback() { + override fun areItemsTheSame( + oldItem: Friend, + newItem: Friend + ): Boolean { + return oldItem.id == newItem.id + } + + override fun areContentsTheSame( + oldItem: Friend, + newItem: Friend + ): Boolean { + return oldItem == newItem + } + } + } +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_list_friend.xml b/frontend/ARchive/app/src/main/res/layout/fragment_list_friend.xml index 462e558ad..6cf4c1133 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_list_friend.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_list_friend.xml @@ -1,10 +1,134 @@ - + - + + + + + + + - \ No newline at end of file + android:background="@color/main_bg_1" + tools:context=".presentation.ui.social.page.friend.SocialFriendFragment"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/item_friend.xml b/frontend/ARchive/app/src/main/res/layout/item_friend.xml new file mode 100644 index 000000000..0a75b60dd --- /dev/null +++ b/frontend/ARchive/app/src/main/res/layout/item_friend.xml @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From b56edc8cbf95fa89c5f485c866a56fdabc38d3d2 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Mon, 8 Apr 2024 10:55:55 +0900 Subject: [PATCH 161/301] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC=20=EB=AA=A8?= =?UTF-8?q?=EB=8D=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/data/dto/friend/response/FriendResponseDto.kt | 3 ++- .../com/droidblossom/archive/domain/model/friend/Friend.kt | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendResponseDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendResponseDto.kt index 9d02821b7..0b3d0c7cf 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendResponseDto.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendResponseDto.kt @@ -12,6 +12,7 @@ data class FriendResponseDto( createdAt = this.createdAt, id = this.id, nickname = this.nickname, - profileUrl = this.profileUrl + profileUrl = this.profileUrl, + isOpenDelete = false ) } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/Friend.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/Friend.kt index c3295b2be..9406c9b6c 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/Friend.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/Friend.kt @@ -4,5 +4,6 @@ data class Friend( val createdAt: String, val id: Int, val nickname: String, - val profileUrl: String + val profileUrl: String, + var isOpenDelete : Boolean, ) \ No newline at end of file From 748ab2c1ccccd984a7d23f4aab97841f03bb27b4 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Mon, 8 Apr 2024 10:56:33 +0900 Subject: [PATCH 162/301] =?UTF-8?q?faet=20:=20=EC=B9=9C=EA=B5=AC=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/mypage/friend/FriendActivity.kt | 10 ++- .../mypage/friend/page/FriendListFragment.kt | 71 +++++++++++++++++-- .../src/main/res/layout/activity_friend.xml | 18 ++++- 3 files changed, 88 insertions(+), 11 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendActivity.kt index 54843fd19..76b1b0323 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendActivity.kt @@ -28,15 +28,21 @@ class FriendActivity : override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) initView() + + viewModel.getFriendList() } private fun initView(){ - val layoutParams = binding.tab.layoutParams as ViewGroup.MarginLayoutParams + val layoutParams = binding.closeBtn.layoutParams as ViewGroup.MarginLayoutParams layoutParams.topMargin += getStatusBarHeight() - binding.tab.layoutParams = layoutParams + binding.closeBtn.layoutParams = layoutParams binding.vp.adapter = friendVPA + binding.closeBtn.setOnClickListener { + finish() + } + TabLayoutMediator(binding.tab, binding.vp) { tab, position -> tab.text = when (position) { 0 -> getString(R.string.groupList) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/FriendListFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/FriendListFragment.kt index 59b288ce1..d9e310ef9 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/FriendListFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/FriendListFragment.kt @@ -1,18 +1,77 @@ package com.droidblossom.archive.presentation.ui.mypage.friend.page +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.RectF import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.core.content.ContextCompat +import androidx.core.graphics.drawable.toBitmap 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.ItemTouchHelper +import androidx.recyclerview.widget.ItemTouchHelper.ACTION_STATE_SWIPE +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView import com.droidblossom.archive.R +import com.droidblossom.archive.databinding.FragmentListFriendBinding +import com.droidblossom.archive.domain.model.friend.Friend +import com.droidblossom.archive.presentation.base.BaseFragment +import com.droidblossom.archive.presentation.ui.mypage.friend.FriendViewModelImpl +import com.droidblossom.archive.presentation.ui.mypage.friend.adapter.FriendRVA +import kotlinx.coroutines.launch -class FriendListFragment : Fragment() { +class FriendListFragment : + BaseFragment(R.layout.fragment_list_friend) { - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - return inflater.inflate(R.layout.fragment_list_friend, container, false) + override val viewModel: FriendViewModelImpl by activityViewModels() + + private val friendRVA by lazy { + FriendRVA(requireContext()){ + + } + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.vm = viewModel + initView() + } + + private fun initView() { + binding.friendRV.adapter = friendRVA + + binding.friendRV.addOnScrollListener(object : RecyclerView.OnScrollListener() { + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + super.onScrollStateChanged(recyclerView, newState) + + if (newState == RecyclerView.SCROLL_STATE_IDLE || newState == RecyclerView.SCROLL_STATE_DRAGGING) { + val layoutManager = recyclerView.layoutManager as LinearLayoutManager + val totalItemCount = layoutManager.itemCount + val lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition() + + if (totalItemCount - lastVisibleItemPosition <= 3) { + viewModel.getFriendList() + } + } + } + }) } + + override fun observeData() { + viewLifecycleOwner.lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.friendListUI.collect{ friends -> + friendRVA.submitList(friends) + } + } + } + } + } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/activity_friend.xml b/frontend/ARchive/app/src/main/res/layout/activity_friend.xml index 59829006b..724576892 100644 --- a/frontend/ARchive/app/src/main/res/layout/activity_friend.xml +++ b/frontend/ARchive/app/src/main/res/layout/activity_friend.xml @@ -13,6 +13,18 @@ android:background="@color/main_bg_1" tools:context=".presentation.ui.mypage.friend.FriendActivity"> + + + @@ -71,7 +83,7 @@ app:strokeColor="@color/main_3" android:elevation="2dp" app:cardElevation="1dp" - android:layout_marginBottom="60dp"> + android:layout_marginBottom="48dp"> Date: Mon, 8 Apr 2024 10:56:50 +0900 Subject: [PATCH 163/301] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC=20=EB=A6=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20vm?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/mypage/friend/FriendViewModel.kt | 15 ++++ .../ui/mypage/friend/FriendViewModelImpl.kt | 78 ++++++++++++++++++- 2 files changed, 92 insertions(+), 1 deletion(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModel.kt index 8bb07d5ae..f7c181d21 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModel.kt @@ -1,4 +1,19 @@ package com.droidblossom.archive.presentation.ui.mypage.friend +import com.droidblossom.archive.domain.model.friend.Friend +import com.droidblossom.archive.domain.model.friend.FriendsSearchResponse +import kotlinx.coroutines.flow.StateFlow + interface FriendViewModel { + + val isFriendSearchOpen : StateFlow + val friendList: StateFlow> + val friendListUI: StateFlow> + + fun openSearchFriend() + fun closeSearchFriend() + fun searchFriend() + fun getFriendList() + fun changeDeleteOpen(previousPosition: Int?, currentPosition: Int) + } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModelImpl.kt index 461241649..09102e32b 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModelImpl.kt @@ -1,12 +1,88 @@ package com.droidblossom.archive.presentation.ui.mypage.friend +import androidx.lifecycle.viewModelScope +import com.droidblossom.archive.domain.model.friend.Friend +import com.droidblossom.archive.domain.usecase.friend.FriendDeleteUseCase +import com.droidblossom.archive.domain.usecase.friend.FriendsPageUseCase import com.droidblossom.archive.presentation.base.BaseViewModel +import com.droidblossom.archive.util.DateUtils +import com.droidblossom.archive.util.onFail +import com.droidblossom.archive.util.onSuccess import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel class FriendViewModelImpl @Inject constructor( - + private val friendsPageUseCase: FriendsPageUseCase, + private val friendDeleteUseCase: FriendDeleteUseCase ) : BaseViewModel(), FriendViewModel { + + private val _isFriendSearchOpen = MutableStateFlow(false) + override val isFriendSearchOpen: StateFlow + get() = _isFriendSearchOpen + + + private val _friendListUI = MutableStateFlow>(listOf()) + override val friendListUI: StateFlow> + get() = _friendListUI + + private val _friendList = MutableStateFlow>(listOf()) + override val friendList: StateFlow> + get() = _friendList + + private val friendHasNextPage = MutableStateFlow(true) + + private val friendLastCreatedTime = MutableStateFlow(DateUtils.dataServerString) + + override fun openSearchFriend() { + viewModelScope.launch { + _isFriendSearchOpen.emit(true) + } + } + + override fun closeSearchFriend() { + viewModelScope.launch { + _isFriendSearchOpen.emit(false) + } + } + + override fun searchFriend(){ + + } + + override fun getFriendList() { + viewModelScope.launch { + if (friendHasNextPage.value) { + friendsPageUseCase(15, friendLastCreatedTime.value).collect { result -> + result.onSuccess { + friendHasNextPage.value = it.hasNext + _friendListUI.emit(_friendListUI.value + it.friends) + _friendList.emit(friendList.value + it.friends) + friendLastCreatedTime.value = it.friends.last().createdAt + }.onFail { +// .emit( +// NotificationViewModel.NotificationEvent.ShowToastMessage( +// "알림 불러오기 실패" +// ) +// ) + } + } + } + } + } + + override fun changeDeleteOpen(previousPosition: Int?, currentPosition: Int) { + viewModelScope.launch { + val newList = _friendListUI.value + previousPosition?.let { + newList[it].isOpenDelete = false + } + newList[currentPosition].isOpenDelete = true + _friendListUI.emit(newList) + } + } } \ No newline at end of file From 99d6aef37589d1c2d3c80fbec0339f05cdc95846 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Mon, 8 Apr 2024 12:59:17 +0900 Subject: [PATCH 164/301] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=95=20usecase=20&=20=EC=B9=9C=EA=B5=AC=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20usecase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/friend/response/FriendResponseDto.kt | 2 +- .../archive/domain/model/friend/Friend.kt | 2 +- .../ui/mypage/friend/FriendViewModel.kt | 2 +- .../ui/mypage/friend/FriendViewModelImpl.kt | 15 +++++++++++++++ .../ui/mypage/friend/adapter/FriendRVA.kt | 19 +++++++++---------- .../mypage/friend/page/FriendListFragment.kt | 12 ++++++++---- 6 files changed, 35 insertions(+), 17 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendResponseDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendResponseDto.kt index 0b3d0c7cf..67d8c2fbe 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendResponseDto.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendResponseDto.kt @@ -4,7 +4,7 @@ import com.droidblossom.archive.domain.model.friend.Friend data class FriendResponseDto( val createdAt: String, - val id: Int, + val id: Long, val nickname: String, val profileUrl: String ) { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/Friend.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/Friend.kt index 9406c9b6c..2eb45efcd 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/Friend.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/Friend.kt @@ -2,7 +2,7 @@ package com.droidblossom.archive.domain.model.friend data class Friend( val createdAt: String, - val id: Int, + val id: Long, val nickname: String, val profileUrl: String, var isOpenDelete : Boolean, diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModel.kt index f7c181d21..6f560bcb0 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModel.kt @@ -15,5 +15,5 @@ interface FriendViewModel { fun searchFriend() fun getFriendList() fun changeDeleteOpen(previousPosition: Int?, currentPosition: Int) - + fun deleteFriend(friend : Friend) } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModelImpl.kt index 09102e32b..526eedeaf 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModelImpl.kt @@ -11,6 +11,7 @@ import com.droidblossom.archive.util.onSuccess import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import javax.inject.Inject @@ -85,4 +86,18 @@ class FriendViewModelImpl @Inject constructor( _friendListUI.emit(newList) } } + + override fun deleteFriend(friend: Friend) { + viewModelScope.launch { + friendDeleteUseCase(friend.id).collect{ result -> + result.onSuccess { + val list = friendListUI.value.toMutableList() + list.remove(friend) + _friendListUI.emit(list) + }.onFail { + + } + } + } + } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/adapter/FriendRVA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/adapter/FriendRVA.kt index 3134140dd..2ef93e567 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/adapter/FriendRVA.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/adapter/FriendRVA.kt @@ -16,7 +16,11 @@ import com.droidblossom.archive.databinding.ItemFriendBinding import com.droidblossom.archive.domain.model.friend.Friend -class FriendRVA(private val context: Context, private val click: (Int?,Int) -> Unit) : +class FriendRVA( + private val context: Context, + private val click: (Int?, Int) -> Unit, + private val delete: (Friend) -> Unit +) : ListAdapter(differ) { private var previousClickedPosition: Int? = null @@ -49,6 +53,7 @@ class FriendRVA(private val context: Context, private val click: (Int?,Int) -> U Log.d("아이템 ", "onScroll() called : $p0") return true } + override fun onLongPress(p0: MotionEvent) = Unit override fun onFling(p0: MotionEvent?, p1: MotionEvent, p2: Float, p3: Float) = true }) @@ -57,15 +62,9 @@ class FriendRVA(private val context: Context, private val click: (Int?,Int) -> U return@setOnTouchListener false } - - binding.root.setOnGenericMotionListener { _, motionEvent -> - when (motionEvent.action) { - MotionEvent.ACTION_SCROLL -> { - Log.d("아이탬", "tqtqt") - } - } - - return@setOnGenericMotionListener false + binding.deleteBtn.setOnClickListener { + delete(data) + notifyItemRemoved(bindingAdapterPosition) } } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/FriendListFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/FriendListFragment.kt index d9e310ef9..ad6b10a07 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/FriendListFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/FriendListFragment.kt @@ -33,9 +33,13 @@ class FriendListFragment : override val viewModel: FriendViewModelImpl by activityViewModels() private val friendRVA by lazy { - FriendRVA(requireContext()){ - - } + FriendRVA(requireContext(), + { prev, curr -> + viewModel.changeDeleteOpen(prev, curr) + }, { friend -> + viewModel.deleteFriend(friend) + } + ) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -67,7 +71,7 @@ class FriendListFragment : override fun observeData() { viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { - viewModel.friendListUI.collect{ friends -> + viewModel.friendListUI.collect { friends -> friendRVA.submitList(friends) } } From 9a17e29fb3ecc97a0aab37ae1f6b7e1fe3bed910 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Mon, 8 Apr 2024 13:50:23 +0900 Subject: [PATCH 165/301] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC=20=EC=9D=B4?= =?UTF-8?q?=EB=B2=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/mypage/friend/FriendViewModel.kt | 9 ++++++ .../ui/mypage/friend/FriendViewModelImpl.kt | 29 ++++++++++++++----- .../mypage/friend/page/FriendListFragment.kt | 15 ++++++++++ 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModel.kt index 6f560bcb0..211f66f5f 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModel.kt @@ -2,10 +2,15 @@ package com.droidblossom.archive.presentation.ui.mypage.friend import com.droidblossom.archive.domain.model.friend.Friend import com.droidblossom.archive.domain.model.friend.FriendsSearchResponse +import com.droidblossom.archive.presentation.ui.mypage.friend.addfriend.AddFriendViewModel +import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow interface FriendViewModel { + val friendEvent : SharedFlow + + val isFriendSearchOpen : StateFlow val friendList: StateFlow> val friendListUI: StateFlow> @@ -16,4 +21,8 @@ interface FriendViewModel { fun getFriendList() fun changeDeleteOpen(previousPosition: Int?, currentPosition: Int) fun deleteFriend(friend : Friend) + + sealed class FriendEvent { + data class ShowToastMessage(val message : String) : FriendViewModel.FriendEvent() + } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModelImpl.kt index 526eedeaf..2eb838e6b 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModelImpl.kt @@ -5,12 +5,17 @@ import com.droidblossom.archive.domain.model.friend.Friend import com.droidblossom.archive.domain.usecase.friend.FriendDeleteUseCase import com.droidblossom.archive.domain.usecase.friend.FriendsPageUseCase import com.droidblossom.archive.presentation.base.BaseViewModel +import com.droidblossom.archive.presentation.ui.home.notification.NotificationViewModel +import com.droidblossom.archive.presentation.ui.mypage.friend.addfriend.AddFriendViewModel import com.droidblossom.archive.util.DateUtils import com.droidblossom.archive.util.onFail import com.droidblossom.archive.util.onSuccess import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import javax.inject.Inject @@ -21,8 +26,12 @@ class FriendViewModelImpl @Inject constructor( private val friendDeleteUseCase: FriendDeleteUseCase ) : BaseViewModel(), FriendViewModel { + private val _friendEvent = MutableSharedFlow() + override val friendEvent: SharedFlow + get() = _friendEvent.asSharedFlow() private val _isFriendSearchOpen = MutableStateFlow(false) + override val isFriendSearchOpen: StateFlow get() = _isFriendSearchOpen @@ -51,7 +60,7 @@ class FriendViewModelImpl @Inject constructor( } } - override fun searchFriend(){ + override fun searchFriend() { } @@ -65,11 +74,11 @@ class FriendViewModelImpl @Inject constructor( _friendList.emit(friendList.value + it.friends) friendLastCreatedTime.value = it.friends.last().createdAt }.onFail { -// .emit( -// NotificationViewModel.NotificationEvent.ShowToastMessage( -// "알림 불러오기 실패" -// ) -// ) + _friendEvent.emit( + FriendViewModel.FriendEvent.ShowToastMessage( + "친구 리스트 불러오기 실패. 잠시후 시도해 주세요" + ) + ) } } } @@ -89,13 +98,17 @@ class FriendViewModelImpl @Inject constructor( override fun deleteFriend(friend: Friend) { viewModelScope.launch { - friendDeleteUseCase(friend.id).collect{ result -> + friendDeleteUseCase(friend.id).collect { result -> result.onSuccess { val list = friendListUI.value.toMutableList() list.remove(friend) _friendListUI.emit(list) }.onFail { - + _friendEvent.emit( + FriendViewModel.FriendEvent.ShowToastMessage( + "친구 삭제 실패. 잠시후 시도해 주세요" + ) + ) } } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/FriendListFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/FriendListFragment.kt index ad6b10a07..b65e9c657 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/FriendListFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/FriendListFragment.kt @@ -23,6 +23,7 @@ import com.droidblossom.archive.R import com.droidblossom.archive.databinding.FragmentListFriendBinding import com.droidblossom.archive.domain.model.friend.Friend import com.droidblossom.archive.presentation.base.BaseFragment +import com.droidblossom.archive.presentation.ui.mypage.friend.FriendViewModel import com.droidblossom.archive.presentation.ui.mypage.friend.FriendViewModelImpl import com.droidblossom.archive.presentation.ui.mypage.friend.adapter.FriendRVA import kotlinx.coroutines.launch @@ -76,6 +77,20 @@ class FriendListFragment : } } } + + viewLifecycleOwner.lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.friendEvent.collect{ event -> + when(event){ + is FriendViewModel.FriendEvent.ShowToastMessage -> { + showToastMessage(event.message) + } + + else -> {} + } + } + } + } } } \ No newline at end of file From ff95205c84b6566f21450c42e135799b7869703e Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Mon, 8 Apr 2024 14:05:46 +0900 Subject: [PATCH 166/301] =?UTF-8?q?feat:=20=EA=B7=B8=EB=A3=B9=20=EB=A6=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20init?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/mypage/friend/FriendViewModel.kt | 11 +- .../ui/mypage/friend/FriendViewModelImpl.kt | 25 ++++ .../mypage/friend/page/FriendListFragment.kt | 14 +- .../mypage/friend/page/GroupListFragment.kt | 48 ++++++- .../main/res/layout/fragment_list_group.xml | 134 +++++++++++++++++- 5 files changed, 206 insertions(+), 26 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModel.kt index 211f66f5f..3e427ea6c 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModel.kt @@ -10,11 +10,15 @@ interface FriendViewModel { val friendEvent : SharedFlow - + //friend val isFriendSearchOpen : StateFlow val friendList: StateFlow> val friendListUI: StateFlow> + //group + val isGroupSearchOpen : StateFlow + + //friend fun openSearchFriend() fun closeSearchFriend() fun searchFriend() @@ -22,6 +26,11 @@ interface FriendViewModel { fun changeDeleteOpen(previousPosition: Int?, currentPosition: Int) fun deleteFriend(friend : Friend) + //group + fun openSearchGroup() + fun closeSearchGroup() + fun searchGroup() + sealed class FriendEvent { data class ShowToastMessage(val message : String) : FriendViewModel.FriendEvent() } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModelImpl.kt index 2eb838e6b..996a50c9a 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModelImpl.kt @@ -30,6 +30,7 @@ class FriendViewModelImpl @Inject constructor( override val friendEvent: SharedFlow get() = _friendEvent.asSharedFlow() + //friend private val _isFriendSearchOpen = MutableStateFlow(false) override val isFriendSearchOpen: StateFlow @@ -48,6 +49,13 @@ class FriendViewModelImpl @Inject constructor( private val friendLastCreatedTime = MutableStateFlow(DateUtils.dataServerString) + //group + private val _isGroupSearchOpen = MutableStateFlow(false) + + override val isGroupSearchOpen: StateFlow + get() = _isGroupSearchOpen + + //friend override fun openSearchFriend() { viewModelScope.launch { _isFriendSearchOpen.emit(true) @@ -113,4 +121,21 @@ class FriendViewModelImpl @Inject constructor( } } } + + //group + override fun openSearchGroup() { + viewModelScope.launch { + _isGroupSearchOpen.emit(true) + } + } + + override fun closeSearchGroup() { + viewModelScope.launch { + _isGroupSearchOpen.emit(false) + } + } + + override fun searchGroup() { + + } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/FriendListFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/FriendListFragment.kt index b65e9c657..79ff35857 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/FriendListFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/FriendListFragment.kt @@ -1,27 +1,15 @@ package com.droidblossom.archive.presentation.ui.mypage.friend.page -import android.graphics.Bitmap -import android.graphics.Canvas -import android.graphics.Paint -import android.graphics.RectF import android.os.Bundle -import android.view.LayoutInflater import android.view.View -import android.view.ViewGroup -import androidx.core.content.ContextCompat -import androidx.core.graphics.drawable.toBitmap -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.ItemTouchHelper -import androidx.recyclerview.widget.ItemTouchHelper.ACTION_STATE_SWIPE import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.droidblossom.archive.R import com.droidblossom.archive.databinding.FragmentListFriendBinding -import com.droidblossom.archive.domain.model.friend.Friend import com.droidblossom.archive.presentation.base.BaseFragment import com.droidblossom.archive.presentation.ui.mypage.friend.FriendViewModel import com.droidblossom.archive.presentation.ui.mypage.friend.FriendViewModelImpl @@ -85,7 +73,7 @@ class FriendListFragment : is FriendViewModel.FriendEvent.ShowToastMessage -> { showToastMessage(event.message) } - + else -> {} } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/GroupListFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/GroupListFragment.kt index 5a97e9677..78517e7a6 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/GroupListFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/GroupListFragment.kt @@ -5,14 +5,50 @@ 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.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView import com.droidblossom.archive.R +import com.droidblossom.archive.databinding.FragmentListFriendBinding +import com.droidblossom.archive.databinding.FragmentListGroupBinding +import com.droidblossom.archive.presentation.base.BaseFragment +import com.droidblossom.archive.presentation.ui.mypage.friend.FriendViewModel +import com.droidblossom.archive.presentation.ui.mypage.friend.FriendViewModelImpl +import com.droidblossom.archive.presentation.ui.mypage.friend.adapter.FriendRVA +import kotlinx.coroutines.launch -class GroupListFragment : Fragment() { +class GroupListFragment : + BaseFragment(R.layout.fragment_list_group) { - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - return inflater.inflate(R.layout.fragment_list_group, container, false) + override val viewModel: FriendViewModelImpl by activityViewModels() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.vm = viewModel + initView() + } + + private fun initView() { + + } + + override fun observeData() { + + viewLifecycleOwner.lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.friendEvent.collect{ event -> + when(event){ + is FriendViewModel.FriendEvent.ShowToastMessage -> { + showToastMessage(event.message) + } + + else -> {} + } + } + } + } } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_list_group.xml b/frontend/ARchive/app/src/main/res/layout/fragment_list_group.xml index 1d0a028db..1dbdeb15b 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_list_group.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_list_group.xml @@ -1,11 +1,133 @@ - + - + + + + + + + + android:background="@color/main_bg_1" + tools:context=".presentation.ui.social.page.friend.SocialFriendFragment"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file + + \ No newline at end of file From 7233b779da9bbaebe79fac05c54bc3589bd9989a Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Mon, 8 Apr 2024 16:29:24 +0900 Subject: [PATCH 167/301] =?UTF-8?q?feat:=20=EC=A3=BC=EC=86=8C=EB=A1=9D=20?= =?UTF-8?q?=EA=B6=8C=ED=95=9C=20=EC=B2=B4=ED=81=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../friend/addfriend/AddFriendActivity.kt | 17 +++++++++ .../friend/addfriend/AddFriendViewModel.kt | 1 + .../addfriend/AddFriendViewModelImpl.kt | 8 ++++ .../addfriend/SearchFriendNumberFragment.kt | 37 +++++++++++++++---- 4 files changed, 56 insertions(+), 7 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendActivity.kt index 144fecc82..3f9cf3db0 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendActivity.kt @@ -2,10 +2,13 @@ package com.droidblossom.archive.presentation.ui.mypage.friend.addfriend import android.content.Context import android.content.Intent +import android.content.pm.PackageManager import androidx.activity.viewModels +import androidx.core.content.ContentProviderCompat.requireContext import com.droidblossom.archive.R import com.droidblossom.archive.databinding.ActivityAddFriendBinding import com.droidblossom.archive.presentation.base.BaseActivity +import com.droidblossom.archive.util.ContactsUtils import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint @@ -15,6 +18,20 @@ class AddFriendActivity : BaseActivity, + grantResults: IntArray + ) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + + if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { + viewModel.contactsSearch(ContactsUtils.getContacts(this)) + } else { + showToastMessage("권한이 필요합니다.") + } + } + companion object { const val ADDFRIEND = "add_friend" diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModel.kt index dc6e7282b..5b4bf4ca4 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModel.kt @@ -30,6 +30,7 @@ interface AddFriendViewModel { sealed class AddEvent { data class ShowToastMessage(val message : String) : AddEvent() + object OpenLoading : AddEvent() object CloseLoading : AddEvent() } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt index 819f6dffc..ec01416da 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt @@ -131,6 +131,7 @@ class AddFriendViewModelImpl @Inject constructor( fun contactsSearch(phones: List) { viewModelScope.launch { + _addEvent.emit(AddFriendViewModel.AddEvent.OpenLoading) friendsSearchPhoneUseCase(FriendsSearchPhoneRequest(phones.filter { it.length == 11 && it.substring(0,3) == "010" })).collect { result -> @@ -145,4 +146,11 @@ class AddFriendViewModelImpl @Inject constructor( } } } + + + fun closeLoading(){ + viewModelScope.launch { + _addEvent.emit(AddFriendViewModel.AddEvent.CloseLoading) + } + } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNumberFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNumberFragment.kt index 9c0c7ea49..bb88a0f4b 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNumberFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNumberFragment.kt @@ -1,6 +1,7 @@ package com.droidblossom.archive.presentation.ui.mypage.friend.addfriend import android.annotation.SuppressLint +import android.content.pm.PackageManager import android.graphics.Rect import android.os.Bundle import android.view.MotionEvent @@ -8,6 +9,8 @@ import android.view.View import android.view.ViewGroup import android.view.inputmethod.EditorInfo import android.view.inputmethod.InputMethodManager +import androidx.core.app.ActivityCompat +import androidx.core.content.ContextCompat import androidx.core.widget.addTextChangedListener import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle @@ -15,7 +18,6 @@ import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.NavController import androidx.navigation.Navigation -import com.droidblossom.archive.R import com.droidblossom.archive.databinding.FragmentFriendSearchNumberBinding import com.droidblossom.archive.presentation.base.BaseFragment import com.droidblossom.archive.presentation.ui.mypage.friend.addfriend.adapter.AddFriendRVA @@ -26,26 +28,25 @@ import kotlinx.coroutines.launch @AndroidEntryPoint class SearchFriendNumberFragment : - BaseFragment(R.layout.fragment_friend_search_number) { + BaseFragment(com.droidblossom.archive.R.layout.fragment_friend_search_number) { override val viewModel: AddFriendViewModelImpl by viewModels() lateinit var navController: NavController private val addFriendRVA by lazy { - AddFriendRVA{ position -> + AddFriendRVA { position -> viewModel.checkAddFriendList(position) } } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding.vm = viewModel navController = Navigation.findNavController(view) initView() - - showLoading(requireContext()) - viewModel.contactsSearch(ContactsUtils.getContacts(requireContext())) + permissionCheck() } @SuppressLint("ClickableViewAccessibility") @@ -125,6 +126,12 @@ class SearchFriendNumberFragment : is AddFriendViewModel.AddEvent.CloseLoading -> { dismissLoading() } + + is AddFriendViewModel.AddEvent.OpenLoading -> { + showLoading(requireContext()) + } + + else -> {} } } } @@ -132,10 +139,26 @@ class SearchFriendNumberFragment : viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { - viewModel.addFriendListUI.collect{ friends -> + viewModel.addFriendListUI.collect { friends -> addFriendRVA.submitList(friends) } } } } + + private fun permissionCheck() { + val status = ContextCompat.checkSelfPermission( + requireContext(), + "android.permission.READ_CONTACTS" + ) + if (status == PackageManager.PERMISSION_GRANTED) { + viewModel.contactsSearch(ContactsUtils.getContacts(requireContext())) + } else { + ActivityCompat.requestPermissions( + requireActivity(), + arrayOf("android.permission.READ_CONTACTS"), + 100 + ) + } + } } \ No newline at end of file From 39937f6bd3149e1f0541618f9cfc46f77f014a9f Mon Sep 17 00:00:00 2001 From: comst19 Date: Tue, 9 Apr 2024 12:07:57 +0900 Subject: [PATCH 168/301] =?UTF-8?q?refact:=20request=20param=20=EA=B3=B5?= =?UTF-8?q?=ED=86=B5=ED=99=94=20-=20createdAt=20->=20created=5Fat,=20frien?= =?UTF-8?q?d-tag=20->=20friend=5Ftag?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/data/source/remote/api/CapsuleSkinService.kt | 2 +- .../archive/data/source/remote/api/FriendService.kt | 6 +++--- .../archive/data/source/remote/api/MemberService.kt | 2 +- .../archive/data/source/remote/api/SecretService.kt | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/CapsuleSkinService.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/CapsuleSkinService.kt index be17ccd79..c6babe0c1 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/CapsuleSkinService.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/CapsuleSkinService.kt @@ -24,7 +24,7 @@ interface CapsuleSkinService { @GET("capsule-skins") suspend fun getCapsuleSkinsPageApi( @Query("size") size : Int, - @Query("createdAt") createdAt: String + @Query("created_at") createdAt: String ) : Response> @POST("capsule-skins") diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt index 06c7e3a7f..e66b68080 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt @@ -29,7 +29,7 @@ interface FriendService { @GET("friends/search") suspend fun postFriendsSearchApi( - @Query("friend-tag") friendTag : String + @Query("friend_tag") friendTag : String ) : Response> @POST("friends/search/phone") @@ -40,13 +40,13 @@ interface FriendService { @GET("friends") suspend fun getFriendsPageApi( @Query("size") size : Int, - @Query("createdAt") createdAt : String, + @Query("created_at") createdAt : String, ) : Response> @GET("friends/requests") suspend fun getFriendsRequestsPageApi( @Query("size") size : Int, - @Query("createdAt") createdAt : String, + @Query("created_at") createdAt : String, ) : Response> @DELETE("friends/{friend_id}") diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/MemberService.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/MemberService.kt index f15ab10ac..b85c9e933 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/MemberService.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/MemberService.kt @@ -44,7 +44,7 @@ interface MemberService { @GET("me/notifications") suspend fun getNotifications( @Query("size") size : Int, - @Query("createdAt") createdAt : String + @Query("created_at") createdAt : String ): Response> diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/SecretService.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/SecretService.kt index 6945cd642..53705f910 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/SecretService.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/SecretService.kt @@ -20,7 +20,7 @@ interface SecretService { @GET("secret/capsules") suspend fun getSecretCapsulePageApi( @Query("size") size : Int, - @Query("createdAt") createdAt: String + @Query("created_at") createdAt: String ) : Response> @POST("capsules/secret") From 0b5b5b09c11ea72a6da0a7cd3291467c0ecfec59 Mon Sep 17 00:00:00 2001 From: comst19 Date: Tue, 9 Apr 2024 12:43:00 +0900 Subject: [PATCH 169/301] =?UTF-8?q?design:=20=EC=BA=A1=EC=8A=90=20?= =?UTF-8?q?=EC=9A=94=EC=95=BD,=20=EB=94=94=ED=85=8C=EC=9D=BC=20=EB=94=94?= =?UTF-8?q?=EC=9E=90=EC=9D=B8=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/common/CapsuleDetailResponseDto.kt | 2 + .../domain/model/common/CapsuleDetail.kt | 3 +- .../res/layout/activity_capsule_detail.xml | 61 +++++++++++-------- .../fragment_capsule_preview_dialog.xml | 51 +++++++++------- 4 files changed, 67 insertions(+), 50 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/common/CapsuleDetailResponseDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/common/CapsuleDetailResponseDto.kt index 4d6a6a53a..05ae00134 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/common/CapsuleDetailResponseDto.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/common/CapsuleDetailResponseDto.kt @@ -4,6 +4,7 @@ import com.droidblossom.archive.domain.model.common.CapsuleDetail data class CapsuleDetailResponseDto( val address: String, + val roadName: String, val capsuleSkinUrl: String, val content: String?, val createdDate: String, @@ -18,6 +19,7 @@ data class CapsuleDetailResponseDto( ){ fun toModel() = CapsuleDetail( address = this.address, + roadName = this.roadName, capsuleSkinUrl = this.capsuleSkinUrl, content = this.content ?: "", createdDate = this.createdDate, diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/common/CapsuleDetail.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/common/CapsuleDetail.kt index affa279d4..873893dad 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/common/CapsuleDetail.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/common/CapsuleDetail.kt @@ -3,6 +3,7 @@ package com.droidblossom.archive.domain.model.common // 모든 캡슐 디테일로 통합될 예정 maybe data class CapsuleDetail( val address: String, + val roadName: String, val capsuleSkinUrl: String, val content: String, val createdDate: String, @@ -15,5 +16,5 @@ data class CapsuleDetail( val title: String, val capsuleType: String ){ - constructor() : this("","","","",null,"",false, listOf(), listOf(),"","","") + constructor() : this("","", "","","",null,"",false, listOf(), listOf(),"","","") } diff --git a/frontend/ARchive/app/src/main/res/layout/activity_capsule_detail.xml b/frontend/ARchive/app/src/main/res/layout/activity_capsule_detail.xml index 39a25f9af..f248c10f5 100644 --- a/frontend/ARchive/app/src/main/res/layout/activity_capsule_detail.xml +++ b/frontend/ARchive/app/src/main/res/layout/activity_capsule_detail.xml @@ -52,33 +52,13 @@ android:layout_marginStart="16dp" app:civ_border_color="@color/white" app:civ_border_overlay="true" + android:layout_marginTop="36dp" app:civ_circle_background_color="@color/white" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="@id/capsuleTypeCardView" + app:layout_constraintTop_toBottomOf="@id/closeBtn" bind:imageUrl="@{vm.capsuleDetail.profileUrl}" bind:placeholder="@{@drawable/app_symbol}" /> - - - - - - - + app:layout_constraintTop_toBottomOf="@id/userProfileImg"> + + + + + + + @@ -192,14 +199,14 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="8dp" - android:layout_marginEnd="42dp" android:ellipsize="end" android:lines="1" + android:layout_marginEnd="@dimen/margin" android:text="@{vm.capsuleDetail.nickname}" android:textAppearance="@style/TextAppearance.App.caption4" android:textColor="@color/gray_700" app:layout_constraintBottom_toBottomOf="@id/userProfileImg" - app:layout_constraintEnd_toStartOf="@id/capsuleTypeCardView" + app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@id/userProfileImg" app:layout_constraintTop_toTopOf="@id/userProfileImg" /> diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_capsule_preview_dialog.xml b/frontend/ARchive/app/src/main/res/layout/fragment_capsule_preview_dialog.xml index e7d134b92..067689067 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_capsule_preview_dialog.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_capsule_preview_dialog.xml @@ -55,23 +55,48 @@ android:layout_height="wrap_content" android:text="@{vm.capsuleSummaryResponse.title}" android:textAppearance="@style/TextAppearance.App.h2" + android:maxLines="1" + android:ellipsize="middle" android:textColor="@color/white" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" + app:layout_constraintStart_toEndOf="@id/capsuleTypeCardView" app:layout_constraintTop_toTopOf="parent" /> + + + + + + + app:layout_constraintTop_toBottomOf="@id/capsuleTitleTextView" + bind:displayCreationDateFormatted="@{vm.capsuleSummaryResponse.createdAt}" /> - - - - - Date: Tue, 9 Apr 2024 14:20:08 +0900 Subject: [PATCH 170/301] =?UTF-8?q?refact:=20=EC=95=8C=EB=A6=BC=EB=A6=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20api=20=EC=9A=94=EA=B5=AC=EC=82=AC=ED=95=AD?= =?UTF-8?q?=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/member/response/NoficationListResponseDto.kt | 9 +++++++-- .../archive/domain/model/member/NotiCategoryName.kt | 5 +++++ .../archive/domain/model/member/NotificationModel.kt | 4 +++- .../presentation/ui/skin/skinmake/SkinMakeFragment.kt | 2 ++ .../app/src/main/res/layout/item_notification.xml | 3 +++ 5 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/member/NotiCategoryName.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/member/response/NoficationListResponseDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/member/response/NoficationListResponseDto.kt index ce49c8d5c..f052f7b7a 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/member/response/NoficationListResponseDto.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/member/response/NoficationListResponseDto.kt @@ -1,17 +1,22 @@ package com.droidblossom.archive.data.dto.member.response +import com.droidblossom.archive.domain.model.member.NotiCategoryName import com.droidblossom.archive.domain.model.member.NotificationModel data class NoficationListResponseDto( val createdAt: String, val imageUrl: String, val text: String, - val title: String + val title: String, + val categoryName: String, + val status: String? ) { fun toModel() = NotificationModel( createdAt = this.createdAt, imageUrl = this.imageUrl, text = this.text, - title = this.title + title = this.title, + categoryName = NotiCategoryName.valueOf(categoryName), + status = this.status ?: "", ) } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/member/NotiCategoryName.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/member/NotiCategoryName.kt new file mode 100644 index 000000000..09bbf43e6 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/member/NotiCategoryName.kt @@ -0,0 +1,5 @@ +package com.droidblossom.archive.domain.model.member + +enum class NotiCategoryName { + CAPSULE_SKIN, FRIEND_REQUEST, FRIEND_ACCEPT, GROUP_REQUEST, GROUP_ACCEPT +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/member/NotificationModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/member/NotificationModel.kt index 2fb43d844..0e1d1d03c 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/member/NotificationModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/member/NotificationModel.kt @@ -4,5 +4,7 @@ data class NotificationModel( val createdAt: String, val imageUrl: String, val text: String, - val title: String + val title: String, + val categoryName : NotiCategoryName, + val status : String ) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeFragment.kt index ac05389d2..098698830 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeFragment.kt @@ -62,6 +62,8 @@ class SkinMakeFragment : BaseFragment { navController.navigate(R.id.action_skinMakeFragment_to_skinMakeSuccessFragment) } + else -> {} + } } } diff --git a/frontend/ARchive/app/src/main/res/layout/item_notification.xml b/frontend/ARchive/app/src/main/res/layout/item_notification.xml index 7c8a62912..66b9135ae 100644 --- a/frontend/ARchive/app/src/main/res/layout/item_notification.xml +++ b/frontend/ARchive/app/src/main/res/layout/item_notification.xml @@ -6,6 +6,8 @@ + + @@ -88,6 +90,7 @@ android:layout_marginVertical="8dp" android:layout_marginEnd="8dp" android:backgroundTint="@color/main_bg_2" + android:visibility="@{item.imageUrl.isEmpty ?View.GONE :View.VISIBLE}" app:cardCornerRadius="8dp" app:cardElevation="0dp" app:layout_constraintBottom_toBottomOf="parent" From 624915e0317c2d81c083fae0a62e7e58dddd4277 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Tue, 9 Apr 2024 14:43:50 +0900 Subject: [PATCH 171/301] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC=20=EC=9A=94?= =?UTF-8?q?=EC=B2=AD=20page=20init?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ARchive/app/src/main/AndroidManifest.xml | 4 +- .../presentation/ui/mypage/MyPageFragment.kt | 12 --- .../friendaccept/FriendAcceptActivity.kt | 61 +++++++++++++++ .../friendaccept/FriendAcceptViewModel.kt | 14 ++++ .../friendaccept/FriendAcceptViewModelImpl.kt | 20 +++++ .../res/layout/activity_friend_accept.xml | 75 +++++++++++++++++++ .../app/src/main/res/values/strings.xml | 3 + 7 files changed, 176 insertions(+), 13 deletions(-) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptActivity.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptViewModel.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptViewModelImpl.kt create mode 100644 frontend/ARchive/app/src/main/res/layout/activity_friend_accept.xml diff --git a/frontend/ARchive/app/src/main/AndroidManifest.xml b/frontend/ARchive/app/src/main/AndroidManifest.xml index d8b51f5b4..28bc1c0fb 100644 --- a/frontend/ARchive/app/src/main/AndroidManifest.xml +++ b/frontend/ARchive/app/src/main/AndroidManifest.xml @@ -7,7 +7,6 @@ - @@ -33,6 +32,9 @@ android:theme="@style/Theme.ARchive" android:usesCleartextTraffic="true" tools:targetApi="31"> + diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt index e0cba75b2..7e10d0bc8 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt @@ -1,34 +1,22 @@ package com.droidblossom.archive.presentation.ui.mypage import android.os.Bundle -import android.util.Log import android.view.View import android.view.ViewGroup -import android.widget.AbsListView -import android.widget.AbsListView.OnScrollListener -import androidx.core.content.ContentProviderCompat.requireContext -import androidx.core.content.ContextCompat.startActivity import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle -import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView -import androidx.recyclerview.widget.SimpleItemAnimator import com.droidblossom.archive.R import com.droidblossom.archive.databinding.FragmentMyPageBinding -import com.droidblossom.archive.domain.model.common.MyCapsule import com.droidblossom.archive.presentation.base.BaseFragment import com.droidblossom.archive.presentation.ui.capsule.CapsuleDetailActivity -import com.droidblossom.archive.presentation.ui.home.HomeFragment -import com.droidblossom.archive.presentation.ui.home.createcapsule.adapter.SkinRVA import com.droidblossom.archive.presentation.ui.home.dialog.CapsulePreviewDialogFragment import com.droidblossom.archive.presentation.ui.mypage.adapter.MyCapsuleRVA import com.droidblossom.archive.presentation.ui.mypage.friend.FriendActivity import com.droidblossom.archive.presentation.ui.mypage.setting.SettingActivity -import com.droidblossom.archive.presentation.ui.skin.SkinFragment -import com.droidblossom.archive.util.DateUtils import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptActivity.kt new file mode 100644 index 000000000..ca2d98072 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptActivity.kt @@ -0,0 +1,61 @@ +package com.droidblossom.archive.presentation.ui.mypage.friendaccept + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.view.ViewGroup +import androidx.activity.viewModels +import com.droidblossom.archive.R +import com.droidblossom.archive.databinding.ActivityFriendAcceptBinding +import com.droidblossom.archive.presentation.base.BaseActivity +import com.droidblossom.archive.presentation.ui.mypage.friend.adapter.FriendVPA +import com.google.android.material.tabs.TabLayoutMediator +import dagger.hilt.android.AndroidEntryPoint + + +@AndroidEntryPoint +class FriendAcceptActivity : + BaseActivity(R.layout.activity_friend_accept) { + override val viewModel: FriendAcceptViewModelImpl by viewModels() + + private val friendVPA by lazy { + FriendVPA(this) + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + initView() + + } + + private fun initView(){ + val layoutParams = binding.closeBtn.layoutParams as ViewGroup.MarginLayoutParams + layoutParams.topMargin += getStatusBarHeight() + binding.closeBtn.layoutParams = layoutParams + + binding.vp.adapter = friendVPA + + binding.closeBtn.setOnClickListener { + finish() + } + + TabLayoutMediator(binding.tab, binding.vp) { tab, position -> + tab.text = when (position) { + 0 -> getString(R.string.groupAccept) + 1 -> getString(R.string.friendAccept) + else -> null + } + }.attach() + } + + override fun observeData() { + + } + + companion object { + const val FRIENDACCEPT = "friend_accept" + + fun newIntent(context: Context) = + Intent(context, FriendAcceptActivity::class.java) + } +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptViewModel.kt new file mode 100644 index 000000000..68efca7b9 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptViewModel.kt @@ -0,0 +1,14 @@ +package com.droidblossom.archive.presentation.ui.mypage.friendaccept + +import com.droidblossom.archive.presentation.ui.mypage.friend.FriendViewModel +import kotlinx.coroutines.flow.SharedFlow + +interface FriendAcceptViewModel { + + val friendAcceptEvent : SharedFlow + + + sealed class FriendAcceptEvent { + data class ShowToastMessage(val message : String) : FriendAcceptEvent() + } +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptViewModelImpl.kt new file mode 100644 index 000000000..e62b3c11f --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptViewModelImpl.kt @@ -0,0 +1,20 @@ +package com.droidblossom.archive.presentation.ui.mypage.friendaccept + +import com.droidblossom.archive.presentation.base.BaseViewModel +import com.droidblossom.archive.presentation.ui.mypage.friend.FriendViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.asSharedFlow +import javax.inject.Inject + +@HiltViewModel +class FriendAcceptViewModelImpl @Inject constructor( +) : BaseViewModel(), FriendAcceptViewModel { + + private val _friendAcceptEvent = MutableSharedFlow() + override val friendAcceptEvent: SharedFlow + get() = _friendAcceptEvent.asSharedFlow() + + +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/activity_friend_accept.xml b/frontend/ARchive/app/src/main/res/layout/activity_friend_accept.xml new file mode 100644 index 000000000..8e47469aa --- /dev/null +++ b/frontend/ARchive/app/src/main/res/layout/activity_friend_accept.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/values/strings.xml b/frontend/ARchive/app/src/main/res/values/strings.xml index acab3c4c5..823970636 100644 --- a/frontend/ARchive/app/src/main/res/values/strings.xml +++ b/frontend/ARchive/app/src/main/res/values/strings.xml @@ -47,4 +47,7 @@ 그룹 리스트 친구 리스트 + + 그룹 요청 + 친구 요청 \ No newline at end of file From 5181393a83cc6977160738f66f9d41fc1edf9b80 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Tue, 9 Apr 2024 14:57:02 +0900 Subject: [PATCH 172/301] =?UTF-8?q?feat:=20=EC=95=8C=EB=A6=BC=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20->=20=EC=88=98=EB=9D=BD=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../home/notification/NotificationActivity.kt | 18 +++++++++++++++ .../notification/adapter/NotificationRVA.kt | 4 ++-- .../friendaccept/FriendAcceptActivity.kt | 22 +++++++++---------- 3 files changed, 31 insertions(+), 13 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationActivity.kt index 55dadf868..f9d8e6d2d 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationActivity.kt @@ -12,8 +12,10 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.droidblossom.archive.R import com.droidblossom.archive.databinding.ActivityNotificationBinding +import com.droidblossom.archive.domain.model.member.NotiCategoryName import com.droidblossom.archive.presentation.base.BaseActivity import com.droidblossom.archive.presentation.ui.home.notification.adapter.NotificationRVA +import com.droidblossom.archive.presentation.ui.mypage.friendaccept.FriendAcceptActivity import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch @@ -25,7 +27,23 @@ class NotificationActivity : private val notificationRVA by lazy { NotificationRVA { + when (it.categoryName) { + NotiCategoryName.CAPSULE_SKIN -> { + } + + NotiCategoryName.FRIEND_REQUEST, + NotiCategoryName.GROUP_REQUEST -> { + startActivity(FriendAcceptActivity.newIntent(this)) + } + + NotiCategoryName.FRIEND_ACCEPT, + NotiCategoryName.GROUP_ACCEPT -> { + + } + + else ->{} + } } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/adapter/NotificationRVA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/adapter/NotificationRVA.kt index fce42f8d5..6b843203d 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/adapter/NotificationRVA.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/adapter/NotificationRVA.kt @@ -8,7 +8,7 @@ import androidx.recyclerview.widget.RecyclerView import com.droidblossom.archive.databinding.ItemNotificationBinding import com.droidblossom.archive.domain.model.member.NotificationModel -class NotificationRVA(val onClick: () -> Unit) : +class NotificationRVA(val onClick: (NotificationModel) -> Unit) : ListAdapter(differ) { inner class ItemViewHolder( @@ -17,7 +17,7 @@ class NotificationRVA(val onClick: () -> Unit) : fun bind(data: NotificationModel) { binding.item = data binding.root.setOnClickListener { - onClick() + onClick(data) } } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptActivity.kt index ca2d98072..02833ae5d 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptActivity.kt @@ -18,9 +18,9 @@ class FriendAcceptActivity : BaseActivity(R.layout.activity_friend_accept) { override val viewModel: FriendAcceptViewModelImpl by viewModels() - private val friendVPA by lazy { - FriendVPA(this) - } +// private val friendVPA by lazy { +// FriendVPA(this) +// } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -33,19 +33,19 @@ class FriendAcceptActivity : layoutParams.topMargin += getStatusBarHeight() binding.closeBtn.layoutParams = layoutParams - binding.vp.adapter = friendVPA +// binding.vp.adapter = friendVPA binding.closeBtn.setOnClickListener { finish() } - TabLayoutMediator(binding.tab, binding.vp) { tab, position -> - tab.text = when (position) { - 0 -> getString(R.string.groupAccept) - 1 -> getString(R.string.friendAccept) - else -> null - } - }.attach() +// TabLayoutMediator(binding.tab, binding.vp) { tab, position -> +// tab.text = when (position) { +// 0 -> getString(R.string.groupAccept) +// 1 -> getString(R.string.friendAccept) +// else -> null +// } +// }.attach() } override fun observeData() { From 5e8b22f9df1be56250a820add1d1d9961fa818a9 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Tue, 9 Apr 2024 18:46:39 +0900 Subject: [PATCH 173/301] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EC=9D=B4=EB=AF=B8=20=EC=B9=9C=EA=B5=AC=20&=20=20?= =?UTF-8?q?=EC=9A=94=EC=B2=AD=20=EB=B3=B4=EB=82=B8=EC=83=81=ED=83=9C=20?= =?UTF-8?q?=ED=95=84=ED=84=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/friend/response/FriendsSearchResponseDto.kt | 5 ++++- .../domain/model/friend/FriendsSearchResponse.kt | 4 +++- .../com/droidblossom/archive/util/BindingAdapter.kt | 13 +++++++++++++ .../app/src/main/res/layout/item_add_friend.xml | 11 +++++++---- 4 files changed, 27 insertions(+), 6 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendsSearchResponseDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendsSearchResponseDto.kt index cff2e9914..4b735dcb8 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendsSearchResponseDto.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendsSearchResponseDto.kt @@ -7,7 +7,8 @@ data class FriendsSearchResponseDto( val id: Long, val profileUrl: String, val nickname: String, - val isFriend: Boolean + val isFriend: Boolean, + val isFriendRequest :Boolean, ) : Serializable { fun toModel() = FriendsSearchResponse( @@ -15,6 +16,8 @@ data class FriendsSearchResponseDto( profileUrl = this.profileUrl, nickname = this.nickname, isFriend = this.isFriend, + isFriendRequest = this.isFriendRequest, + name = "", isChecked = false ) } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendsSearchResponse.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendsSearchResponse.kt index f3f8e8302..494d880ac 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendsSearchResponse.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendsSearchResponse.kt @@ -5,5 +5,7 @@ data class FriendsSearchResponse ( val profileUrl : String, val nickname : String, val isFriend : Boolean, - var isChecked : Boolean + var isFriendRequest : Boolean, + var isChecked : Boolean, + val name : String ) \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/BindingAdapter.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/BindingAdapter.kt index 42dd79bec..80cde05b7 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/BindingAdapter.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/BindingAdapter.kt @@ -206,4 +206,17 @@ fun TabLayout.setTabItemMargin(marginEndDp: Int) { tab.layoutParams = layoutParams } requestLayout() +} + +@BindingAdapter(value = ["bind:isFriend", "bind:isRequest","bind:friendName"], requireAll = false) +fun TextView.addFriendText(isFriend: Boolean, isRequest : Boolean, name : String) { + if (isFriend || isRequest){ + if (isFriend){ + this.text = "이미 친구입니다." + } else { + this.text = "요천을 보냈습니다." + } + }else { + this.text = name + } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/item_add_friend.xml b/frontend/ARchive/app/src/main/res/layout/item_add_friend.xml index 4213d679b..c1941fdad 100644 --- a/frontend/ARchive/app/src/main/res/layout/item_add_friend.xml +++ b/frontend/ARchive/app/src/main/res/layout/item_add_friend.xml @@ -17,8 +17,8 @@ - + android:layout_height="wrap_content" + android:clickable="@{item.friend || item.friendRequest ? false : true}"> + app:layout_constraintTop_toBottomOf="@id/nameT" + bind:friendName="@{item.name}" + bind:isFriend="@{item.friend}" + bind:isRequest="@{item.friendRequest}" /> Date: Tue, 9 Apr 2024 20:12:04 +0900 Subject: [PATCH 174/301] =?UTF-8?q?feat:=20=EA=B3=B5=EA=B0=9C=20=EC=BA=A1?= =?UTF-8?q?=EC=8A=90=20=EB=94=94=ED=85=8C=EC=9D=BC=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?api=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/capsule/CapsuleDetailActivity.kt | 4 ++-- .../ui/capsule/CapsuleDetailViewModel.kt | 1 + .../ui/capsule/CapsuleDetailViewModelImpl.kt | 18 +++++++++++++++++- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/capsule/CapsuleDetailActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/capsule/CapsuleDetailActivity.kt index 1399abe02..20299058c 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/capsule/CapsuleDetailActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/capsule/CapsuleDetailActivity.kt @@ -62,11 +62,11 @@ class CapsuleDetailActivity : } HomeFragment.CapsuleType.GROUP -> { - + } HomeFragment.CapsuleType.PUBLIC -> { - + viewModel.getPublicCapsuleDetail(capsuleInd) } null -> {} diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/capsule/CapsuleDetailViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/capsule/CapsuleDetailViewModel.kt index db0241327..a7d72262c 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/capsule/CapsuleDetailViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/capsule/CapsuleDetailViewModel.kt @@ -10,6 +10,7 @@ interface CapsuleDetailViewModel { val capsuleDetail :StateFlow fun getSecretCapsuleDetail(id:Long) + fun getPublicCapsuleDetail(id:Long) sealed class DetailEvent { data class ShowToastMessage(val message : String) : DetailEvent() diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/capsule/CapsuleDetailViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/capsule/CapsuleDetailViewModelImpl.kt index 0fdbd993e..039fce63e 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/capsule/CapsuleDetailViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/capsule/CapsuleDetailViewModelImpl.kt @@ -3,6 +3,7 @@ package com.droidblossom.archive.presentation.ui.capsule import android.util.Log import androidx.lifecycle.viewModelScope import com.droidblossom.archive.domain.model.common.CapsuleDetail +import com.droidblossom.archive.domain.usecase.open.PublicCapsuleDetailUseCase import com.droidblossom.archive.domain.usecase.secret.SecretCapsuleDetailUseCase import com.droidblossom.archive.presentation.base.BaseViewModel import com.droidblossom.archive.util.onFail @@ -18,7 +19,8 @@ import javax.inject.Inject @HiltViewModel class CapsuleDetailViewModelImpl @Inject constructor( - private val secretCapsuleDetailUseCase: SecretCapsuleDetailUseCase + private val secretCapsuleDetailUseCase: SecretCapsuleDetailUseCase, + private val publicCapsuleDetailUseCase: PublicCapsuleDetailUseCase ): BaseViewModel(), CapsuleDetailViewModel { private val _detailEvent = MutableSharedFlow() @@ -42,4 +44,18 @@ class CapsuleDetailViewModelImpl @Inject constructor( } } } + + override fun getPublicCapsuleDetail(id: Long) { + viewModelScope.launch { + publicCapsuleDetailUseCase(id).collect{ result -> + result.onSuccess { detail -> + Log.d("디테일","${detail}") + _capsuleDetail.emit(detail) + }.onFail { + Log.d("디테일","${it}") + _detailEvent.emit(CapsuleDetailViewModel.DetailEvent.ShowToastMessage("상세정보 불러오기 실패")) + } + } + } + } } \ No newline at end of file From ce201840fd1c7ae4dd9d4e639246005eb1a9ec94 Mon Sep 17 00:00:00 2001 From: comst19 Date: Wed, 10 Apr 2024 00:40:31 +0900 Subject: [PATCH 175/301] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC=EA=B0=80=20?= =?UTF-8?q?=EB=A7=8C=EB=93=A0=20=EA=B3=B5=EA=B0=9C=20=EC=BA=A1=EC=8A=90=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=20api=20=EC=85=8B?= =?UTF-8?q?=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/common/CapsuleDetailResponseDto.kt | 16 +++++++++ .../request/PublicCapsuleSliceRequestDto.kt | 6 ++++ .../response/PublicCapsuleSliceResponseDto.kt | 15 ++++++++ .../data/repository/PublicRepositoryImpl.kt | 7 ++++ .../data/source/remote/api/PublicService.kt | 8 +++++ .../domain/model/common/SocialCapsules.kt | 18 ++++++++++ .../model/open/PublicCapsuleSliceResponse.kt | 8 +++++ .../domain/repository/PublicRepository.kt | 4 +++ .../usecase/open/PublicCapsulePageUseCase.kt | 34 +++++++++++++++++++ 9 files changed, 116 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/open/request/PublicCapsuleSliceRequestDto.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/open/response/PublicCapsuleSliceResponseDto.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/common/SocialCapsules.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/open/PublicCapsuleSliceResponse.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/open/PublicCapsulePageUseCase.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/common/CapsuleDetailResponseDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/common/CapsuleDetailResponseDto.kt index 05ae00134..a125570a5 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/common/CapsuleDetailResponseDto.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/common/CapsuleDetailResponseDto.kt @@ -1,6 +1,7 @@ package com.droidblossom.archive.data.dto.common import com.droidblossom.archive.domain.model.common.CapsuleDetail +import com.droidblossom.archive.domain.model.common.SocialCapsules data class CapsuleDetailResponseDto( val address: String, @@ -32,4 +33,19 @@ data class CapsuleDetailResponseDto( title = this.title, capsuleType=this.capsuleType ) + + fun toSocialCapsuleModel() = SocialCapsules( + title = this.title, + content = this.content ?: "", + createdDate = this.createdDate, + dueDate = this.dueDate, + profileUrl = this.profileUrl, + capsuleSkinUrl = this.capsuleSkinUrl, + nickNameOrGroupName = this.nickname, + roadName = this.roadName, + address = this.address, + hasThumbnail = !(imageUrls.isNullOrEmpty() && videoUrls.isNullOrEmpty()), + thumbnailImage = imageUrls?.firstOrNull() ?: videoUrls?.firstOrNull(), + isOpened = false + ) } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/open/request/PublicCapsuleSliceRequestDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/open/request/PublicCapsuleSliceRequestDto.kt new file mode 100644 index 000000000..eaf60cc29 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/open/request/PublicCapsuleSliceRequestDto.kt @@ -0,0 +1,6 @@ +package com.droidblossom.archive.data.dto.open.request + +data class PublicCapsuleSliceRequestDto ( + val size : Int, + val createdAt: String +) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/open/response/PublicCapsuleSliceResponseDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/open/response/PublicCapsuleSliceResponseDto.kt new file mode 100644 index 000000000..c7fd91b28 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/open/response/PublicCapsuleSliceResponseDto.kt @@ -0,0 +1,15 @@ +package com.droidblossom.archive.data.dto.open.response + +import com.droidblossom.archive.data.dto.common.CapsuleDetailResponseDto +import com.droidblossom.archive.domain.model.open.PublicCapsuleSliceResponse +import com.droidblossom.archive.domain.model.s3.S3Urls + +data class PublicCapsuleSliceResponseDto( + val publicCapsules: List, + val hasNext: Boolean +){ + fun toModel()= PublicCapsuleSliceResponse( + publicCapsules = this.publicCapsules.map { it.toSocialCapsuleModel() }, + hasNext = false + ) +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/PublicRepositoryImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/PublicRepositoryImpl.kt index ce2ca8933..14cea5e7f 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/PublicRepositoryImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/PublicRepositoryImpl.kt @@ -5,9 +5,12 @@ import com.droidblossom.archive.data.dto.common.CapsuleCreateRequestDto import com.droidblossom.archive.data.dto.common.CapsuleDetailResponseDto import com.droidblossom.archive.data.dto.common.CapsuleSummaryResponseDto import com.droidblossom.archive.data.dto.common.toModel +import com.droidblossom.archive.data.dto.open.request.PublicCapsuleSliceRequestDto +import com.droidblossom.archive.data.dto.open.response.PublicCapsuleSliceResponseDto import com.droidblossom.archive.data.source.remote.api.PublicService import com.droidblossom.archive.domain.model.common.CapsuleDetail import com.droidblossom.archive.domain.model.common.CapsuleSummaryResponse +import com.droidblossom.archive.domain.model.open.PublicCapsuleSliceResponse import com.droidblossom.archive.domain.repository.PublicRepository import com.droidblossom.archive.util.RetrofitResult import com.droidblossom.archive.util.apiHandler @@ -27,4 +30,8 @@ class PublicRepositoryImpl @Inject constructor( override suspend fun getPublicCapsuleDetail(capsuleId: Long): RetrofitResult { return apiHandler({ api.getPublicCapsuleDetailApi(capsuleId) }) { response: ResponseBody -> response.result.toModel()} } + + override suspend fun getPublicCapsulesPage(request: PublicCapsuleSliceRequestDto): RetrofitResult { + return apiHandler({ api.getPublicCapsulesPageApi(request.size, request.createdAt) }){ response: ResponseBody -> response.result.toModel() } + } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/PublicService.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/PublicService.kt index 974656806..9d6365ef8 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/PublicService.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/PublicService.kt @@ -4,11 +4,13 @@ import com.droidblossom.archive.data.dto.ResponseBody import com.droidblossom.archive.data.dto.common.CapsuleCreateRequestDto import com.droidblossom.archive.data.dto.common.CapsuleDetailResponseDto import com.droidblossom.archive.data.dto.common.CapsuleSummaryResponseDto +import com.droidblossom.archive.data.dto.open.response.PublicCapsuleSliceResponseDto import retrofit2.Response import retrofit2.http.Body import retrofit2.http.GET import retrofit2.http.POST import retrofit2.http.Path +import retrofit2.http.Query interface PublicService { @@ -27,4 +29,10 @@ interface PublicService { @Path("capsule_id") capsuleId : Long, ) : Response> + @GET("public/capsules") + suspend fun getPublicCapsulesPageApi( + @Query("size") size : Int, + @Query("createdAt") createdAt: String + ) : Response> + } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/common/SocialCapsules.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/common/SocialCapsules.kt new file mode 100644 index 000000000..4c7fd28a4 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/common/SocialCapsules.kt @@ -0,0 +1,18 @@ +package com.droidblossom.archive.domain.model.common + +data class SocialCapsules ( + val title:String, + val content:String, + val createdDate:String, + val dueDate:String?, + val profileUrl:String, + val capsuleSkinUrl: String, + val nickNameOrGroupName:String, + val thumbnailImage:String?, + val roadName: String, + val address: String, + val hasThumbnail: Boolean, + val isOpened:Boolean, +) + +//val capsuleType: String, \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/open/PublicCapsuleSliceResponse.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/open/PublicCapsuleSliceResponse.kt new file mode 100644 index 000000000..833cad2e2 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/open/PublicCapsuleSliceResponse.kt @@ -0,0 +1,8 @@ +package com.droidblossom.archive.domain.model.open + +import com.droidblossom.archive.domain.model.common.SocialCapsules + +data class PublicCapsuleSliceResponse ( + val publicCapsules: List, + val hasNext: Boolean +) \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/PublicRepository.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/PublicRepository.kt index 2582b7712..7794d61b9 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/PublicRepository.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/PublicRepository.kt @@ -1,8 +1,10 @@ package com.droidblossom.archive.domain.repository import com.droidblossom.archive.data.dto.common.CapsuleCreateRequestDto +import com.droidblossom.archive.data.dto.open.request.PublicCapsuleSliceRequestDto import com.droidblossom.archive.domain.model.common.CapsuleDetail import com.droidblossom.archive.domain.model.common.CapsuleSummaryResponse +import com.droidblossom.archive.domain.model.open.PublicCapsuleSliceResponse import com.droidblossom.archive.util.RetrofitResult interface PublicRepository { @@ -13,4 +15,6 @@ interface PublicRepository { suspend fun getPublicCapsuleDetail (capsuleId: Long) : RetrofitResult + suspend fun getPublicCapsulesPage(request: PublicCapsuleSliceRequestDto) : RetrofitResult + } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/open/PublicCapsulePageUseCase.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/open/PublicCapsulePageUseCase.kt new file mode 100644 index 000000000..c61d93b67 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/open/PublicCapsulePageUseCase.kt @@ -0,0 +1,34 @@ +package com.droidblossom.archive.domain.usecase.open + +import android.util.Log +import com.droidblossom.archive.data.dto.open.request.PublicCapsuleSliceRequestDto +import com.droidblossom.archive.data.dto.secret.request.SecretCapsulePageRequestDto +import com.droidblossom.archive.domain.model.open.PublicCapsuleSliceResponse +import com.droidblossom.archive.domain.model.secret.SecretCapsulePage +import com.droidblossom.archive.domain.repository.PublicRepository +import com.droidblossom.archive.util.RetrofitResult +import com.droidblossom.archive.util.onException +import com.droidblossom.archive.util.onFail +import com.droidblossom.archive.util.onSuccess +import kotlinx.coroutines.flow.flow +import javax.inject.Inject + +class PublicCapsulePageUseCase @Inject constructor( + private val repository: PublicRepository +) { + suspend operator fun invoke(request: PublicCapsuleSliceRequestDto) = + flow> { + try { + emit(repository.getPublicCapsulesPage(request).onSuccess { + + }.onFail { + + }.onException { + throw Exception(it) + }) + } catch (e: Exception) { + Log.d("예외확인", "$e") + e.printStackTrace() + } + } +} \ No newline at end of file From 6375e83d5b2a4b3231140b36def57c0a2abb5264 Mon Sep 17 00:00:00 2001 From: comst19 Date: Wed, 10 Apr 2024 00:42:16 +0900 Subject: [PATCH 176/301] =?UTF-8?q?chore:=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20?= =?UTF-8?q?=EB=B0=94=EC=9D=B8=EB=94=A9=20=EC=95=84=EC=9D=B4=ED=85=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../res/layout/item_social_capsule_close.xml | 10 +++++----- .../res/layout/item_social_capsule_open.xml | 18 ++++++++++-------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/frontend/ARchive/app/src/main/res/layout/item_social_capsule_close.xml b/frontend/ARchive/app/src/main/res/layout/item_social_capsule_close.xml index c7010bc11..f9fb0a0fd 100644 --- a/frontend/ARchive/app/src/main/res/layout/item_social_capsule_close.xml +++ b/frontend/ARchive/app/src/main/res/layout/item_social_capsule_close.xml @@ -7,7 +7,7 @@ + type="com.droidblossom.archive.domain.model.common.SocialCapsules" /> + + type="com.droidblossom.archive.domain.model.common.SocialCapsules" /> @@ -168,7 +170,7 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" tool:text="마유로" - android:text="@{item.capsuleLocation}" + android:text="@{item.roadName.isEmpty ? item.address : item.roadName}" android:layout_marginEnd="8dp"/> From e37b34657a44b25f688095f081aeb1ed85b1d220 Mon Sep 17 00:00:00 2001 From: comst19 Date: Wed, 10 Apr 2024 00:42:51 +0900 Subject: [PATCH 177/301] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC=EC=9D=98=20?= =?UTF-8?q?=EA=B3=B5=EA=B0=9C=20=EC=BA=A1=EC=8A=90=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?api=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../page/friend/SocialFriendFragment.kt | 43 ++++++++++++- .../page/friend/SocialFriendViewModel.kt | 12 +++- .../page/friend/SocialFriendViewModelImpl.kt | 60 ++++++++++++++++++- 3 files changed, 112 insertions(+), 3 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendFragment.kt index f32c1a969..30142310e 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendFragment.kt @@ -22,14 +22,18 @@ import com.droidblossom.archive.R import com.droidblossom.archive.databinding.FragmentSocialFriendBinding import com.droidblossom.archive.databinding.FragmentSocialGroupBinding import com.droidblossom.archive.presentation.base.BaseFragment +import com.droidblossom.archive.presentation.ui.mypage.MyPageViewModel +import com.droidblossom.archive.presentation.ui.mypage.setting.SettingActivity import com.droidblossom.archive.presentation.ui.social.adapter.SocialFriendCapsuleRVA import com.droidblossom.archive.presentation.ui.social.adapter.TestSocialFriendModel import com.droidblossom.archive.presentation.ui.social.page.group.SocialGroupViewModelImpl import com.droidblossom.archive.util.SpaceItemDecoration import com.droidblossom.archive.util.updateTopConstraintsForSearch +import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch +@AndroidEntryPoint class SocialFriendFragment : BaseFragment(R.layout.fragment_social_friend) { override val viewModel: SocialFriendViewModelImpl by viewModels() @@ -58,6 +62,28 @@ class SocialFriendFragment : BaseFragment + when (event) { + is SocialFriendViewModel.SocialFriendEvent.ShowToastMessage -> { + showToastMessage(event.message) + } + + else -> {} + } + } + } + } + + viewLifecycleOwner.lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED){ + viewModel.publicCapsules.collect{ publicCapsule -> + socialFriendCapsuleRVA.submitList(publicCapsule) + } + } + } } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -72,7 +98,22 @@ class SocialFriendFragment : BaseFragment{ diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModel.kt index 75329ffb2..6121e028d 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModel.kt @@ -1,13 +1,23 @@ package com.droidblossom.archive.presentation.ui.social.page.friend +import com.droidblossom.archive.domain.model.common.SocialCapsules +import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow interface SocialFriendViewModel { - + val socialFriendEvents : SharedFlow + val publicCapsules : StateFlow> val isSearchOpen : StateFlow + val hasNextPage : StateFlow + val lastCreatedTime : StateFlow + fun socialFriendEvent(event: SocialFriendEvent) fun openSearchFriendCapsule() fun closeSearchFriendCapsule() fun searchFriendCapsule() + fun getPublicCapsulePage() + sealed class SocialFriendEvent{ + data class ShowToastMessage(val message : String) : SocialFriendEvent() + } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModelImpl.kt index d3437aacf..86311355b 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModelImpl.kt @@ -1,23 +1,57 @@ package com.droidblossom.archive.presentation.ui.social.page.friend +import android.util.Log import androidx.lifecycle.viewModelScope +import com.droidblossom.archive.data.dto.open.request.PublicCapsuleSliceRequestDto +import com.droidblossom.archive.domain.model.common.MyCapsule +import com.droidblossom.archive.domain.model.common.SocialCapsules +import com.droidblossom.archive.domain.model.secret.SecretCapsulePageRequest +import com.droidblossom.archive.domain.usecase.open.PublicCapsulePageUseCase import com.droidblossom.archive.presentation.base.BaseViewModel +import com.droidblossom.archive.presentation.ui.auth.AuthViewModel +import com.droidblossom.archive.presentation.ui.mypage.MyPageViewModel +import com.droidblossom.archive.util.DateUtils +import com.droidblossom.archive.util.onFail +import com.droidblossom.archive.util.onSuccess import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel class SocialFriendViewModelImpl @Inject constructor( - + private val publicCapsulePageUseCase: PublicCapsulePageUseCase ): BaseViewModel(), SocialFriendViewModel { + private val _socialFriendEvents = MutableSharedFlow() + override val socialFriendEvents: SharedFlow + get() =_socialFriendEvents.asSharedFlow() + + private val _publicCapsules = MutableStateFlow(listOf()) + override val publicCapsules: StateFlow> + get() = _publicCapsules private val _isSearchOpen = MutableStateFlow(false) override val isSearchOpen: StateFlow get() = _isSearchOpen + private val _hasNextPage = MutableStateFlow(true) + override val hasNextPage: StateFlow + get() = _hasNextPage + private val _lastCreatedTime = MutableStateFlow(DateUtils.dataServerString) + override val lastCreatedTime: StateFlow + get() = _lastCreatedTime + + override fun socialFriendEvent(event: SocialFriendViewModel.SocialFriendEvent) { + viewModelScope.launch { + _socialFriendEvents.emit(event) + } + } + override fun openSearchFriendCapsule() { viewModelScope.launch { _isSearchOpen.emit(true) @@ -30,6 +64,30 @@ class SocialFriendViewModelImpl @Inject constructor( } } + init { + getPublicCapsulePage() + } + override fun getPublicCapsulePage(){ + viewModelScope.launch { + if (hasNextPage.value) { + publicCapsulePageUseCase( + PublicCapsuleSliceRequestDto( + 15, + lastCreatedTime.value + ) + ).collect { result -> + result.onSuccess { + _hasNextPage.value = it.hasNext + _publicCapsules.emit(publicCapsules.value + it.publicCapsules) + _lastCreatedTime.value = publicCapsules.value.last().createdDate + }.onFail { + socialFriendEvent(SocialFriendViewModel.SocialFriendEvent.ShowToastMessage("공개캡슐 불러오기 실패")) + } + } + } + } + } + override fun searchFriendCapsule(){ } From 899f742d93216f74278457b40d1eed79a1e0a2f4 Mon Sep 17 00:00:00 2001 From: comst19 Date: Wed, 10 Apr 2024 00:44:55 +0900 Subject: [PATCH 178/301] =?UTF-8?q?chore:=20SocialFriendCapsuleRVA=20?= =?UTF-8?q?=EC=95=84=EC=9D=B4=ED=85=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/social/adapter/SocialFriendCapsuleRVA.kt | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/adapter/SocialFriendCapsuleRVA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/adapter/SocialFriendCapsuleRVA.kt index 472e2a39e..c9375f731 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/adapter/SocialFriendCapsuleRVA.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/adapter/SocialFriendCapsuleRVA.kt @@ -7,14 +7,15 @@ import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView import com.droidblossom.archive.databinding.ItemSocialCapsuleCloseBinding import com.droidblossom.archive.databinding.ItemSocialCapsuleOpenBinding +import com.droidblossom.archive.domain.model.common.SocialCapsules -class SocialFriendCapsuleRVA() : ListAdapter(differ) { +class SocialFriendCapsuleRVA() : ListAdapter(differ) { inner class OpenedCapsuleViewHolder( private val binding: ItemSocialCapsuleOpenBinding ) : RecyclerView.ViewHolder(binding.root){ - fun bindOpenedCapsule(data : TestSocialFriendModel){ + fun bindOpenedCapsule(data : SocialCapsules){ binding.item = data } @@ -24,7 +25,7 @@ class SocialFriendCapsuleRVA() : ListAdapter() { - override fun areItemsTheSame(oldItem: TestSocialFriendModel, newItem: TestSocialFriendModel): Boolean { - return oldItem.id == newItem.id + val differ = object : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: SocialCapsules, newItem: SocialCapsules): Boolean { + return oldItem === newItem } - override fun areContentsTheSame(oldItem: TestSocialFriendModel, newItem: TestSocialFriendModel): Boolean { + override fun areContentsTheSame(oldItem: SocialCapsules, newItem: SocialCapsules): Boolean { return oldItem == newItem } } From 76abdd9b287b48140bd59e131e769b33e9f35d22 Mon Sep 17 00:00:00 2001 From: comst19 Date: Wed, 10 Apr 2024 00:46:48 +0900 Subject: [PATCH 179/301] =?UTF-8?q?delete:=20=EB=8D=94=EB=AF=B8=20?= =?UTF-8?q?=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EC=83=9D=EC=84=B1=ED=95=A8?= =?UTF-8?q?=EC=88=98=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../page/friend/SocialFriendFragment.kt | 22 ------------------ .../social/page/group/SocialGroupFragment.kt | 23 ------------------- 2 files changed, 45 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendFragment.kt index 30142310e..5e6f380e5 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendFragment.kt @@ -116,28 +116,6 @@ class SocialFriendFragment : BaseFragment{ - - val dummyList = mutableListOf() - - for(i in 1..10){ - val capsule = TestSocialFriendModel( - id = i.toLong(), - capsuleTitle = "comprehensam", - capsuleWriter = "deseruisse", - capsuleContent = "eirmod", - capsuleContentImg = "enim", - capsuleLocation = "discere", - capsuleCreateTime = "amet", - isOpened = i % 2 == 0 - ) - - dummyList.add(capsule) - } - - return dummyList - } - @SuppressLint("ClickableViewAccessibility") fun initSearchEdit(){ binding.searchOpenEditT.setOnEditorActionListener { _, i, _ -> diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupFragment.kt index 086f2ddce..407d041b1 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupFragment.kt @@ -66,32 +66,9 @@ class SocialGroupFragment : BaseFragment{ - - val dummyList = mutableListOf() - - for(i in 1..10){ - val capsule = TestSocialFriendModel( - id = i.toLong(), - capsuleTitle = "comprehensam", - capsuleWriter = "deseruisse", - capsuleContent = "eirmod", - capsuleContentImg = "enim", - capsuleLocation = "discere", - capsuleCreateTime = "amet", - isOpened = i % 2 == 0 - ) - - dummyList.add(capsule) - } - - return dummyList - } - @SuppressLint("ClickableViewAccessibility") fun initSearchEdit(){ binding.searchOpenEditT.setOnEditorActionListener { _, i, _ -> From 6817f5c4623746183123080449bcba015f8a6b3f Mon Sep 17 00:00:00 2001 From: comst19 Date: Wed, 10 Apr 2024 02:27:55 +0900 Subject: [PATCH 180/301] =?UTF-8?q?refact:=20api=20=EC=9D=91=EB=8B=B5=20?= =?UTF-8?q?=EC=8A=A4=ED=8E=99=20=EB=B3=80=EA=B2=BD=EC=97=90=20=EB=94=B0?= =?UTF-8?q?=EB=A5=B8=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/common/CapsuleDetailResponseDto.kt | 15 -------- .../open/response/PublicCapsuleResponseDto.kt | 38 +++++++++++++++++++ .../response/PublicCapsuleSliceResponseDto.kt | 4 +- .../data/source/remote/api/PublicService.kt | 2 +- .../domain/model/common/SocialCapsules.kt | 1 + .../ui/capsule/CapsuleDetailActivity.kt | 2 +- .../social/adapter/SocialFriendCapsuleRVA.kt | 2 +- 7 files changed, 44 insertions(+), 20 deletions(-) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/open/response/PublicCapsuleResponseDto.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/common/CapsuleDetailResponseDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/common/CapsuleDetailResponseDto.kt index a125570a5..36d7c9559 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/common/CapsuleDetailResponseDto.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/common/CapsuleDetailResponseDto.kt @@ -33,19 +33,4 @@ data class CapsuleDetailResponseDto( title = this.title, capsuleType=this.capsuleType ) - - fun toSocialCapsuleModel() = SocialCapsules( - title = this.title, - content = this.content ?: "", - createdDate = this.createdDate, - dueDate = this.dueDate, - profileUrl = this.profileUrl, - capsuleSkinUrl = this.capsuleSkinUrl, - nickNameOrGroupName = this.nickname, - roadName = this.roadName, - address = this.address, - hasThumbnail = !(imageUrls.isNullOrEmpty() && videoUrls.isNullOrEmpty()), - thumbnailImage = imageUrls?.firstOrNull() ?: videoUrls?.firstOrNull(), - isOpened = false - ) } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/open/response/PublicCapsuleResponseDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/open/response/PublicCapsuleResponseDto.kt new file mode 100644 index 000000000..99d507658 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/open/response/PublicCapsuleResponseDto.kt @@ -0,0 +1,38 @@ +package com.droidblossom.archive.data.dto.open.response + +import com.droidblossom.archive.data.dto.common.CapsuleDetailResponseDto +import com.droidblossom.archive.domain.model.common.SocialCapsules +import com.droidblossom.archive.domain.model.open.PublicCapsuleSliceResponse + +data class PublicCapsuleResponseDto ( + val capsuleId: Long, + val address: String, + val roadName: String, + val capsuleSkinUrl: String, + val content: String?, + val createdDate: String, + val profileUrl: String, + val dueDate: String?, + val isOpened: Boolean, + val imageUrls: List?, + val videoUrls: List?, + val nickname: String, + val title: String, + val capsuleType: String, +){ + fun toModel()= SocialCapsules( + capsuleId = this.capsuleId, + title = this.title, + content = this.content ?: "", + createdDate = this.createdDate, + dueDate = this.dueDate, + profileUrl = this.profileUrl, + capsuleSkinUrl = this.capsuleSkinUrl, + nickNameOrGroupName = this.nickname, + roadName = this.roadName, + address = this.address, + hasThumbnail = !(imageUrls.isNullOrEmpty() && videoUrls.isNullOrEmpty()), + thumbnailImage = imageUrls?.firstOrNull() ?: videoUrls?.firstOrNull(), + isOpened = false + ) +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/open/response/PublicCapsuleSliceResponseDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/open/response/PublicCapsuleSliceResponseDto.kt index c7fd91b28..d07b6f9df 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/open/response/PublicCapsuleSliceResponseDto.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/open/response/PublicCapsuleSliceResponseDto.kt @@ -5,11 +5,11 @@ import com.droidblossom.archive.domain.model.open.PublicCapsuleSliceResponse import com.droidblossom.archive.domain.model.s3.S3Urls data class PublicCapsuleSliceResponseDto( - val publicCapsules: List, + val publicCapsules: List, val hasNext: Boolean ){ fun toModel()= PublicCapsuleSliceResponse( - publicCapsules = this.publicCapsules.map { it.toSocialCapsuleModel() }, + publicCapsules = this.publicCapsules.map { it.toModel() }, hasNext = false ) } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/PublicService.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/PublicService.kt index 9d6365ef8..3644761e1 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/PublicService.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/PublicService.kt @@ -32,7 +32,7 @@ interface PublicService { @GET("public/capsules") suspend fun getPublicCapsulesPageApi( @Query("size") size : Int, - @Query("createdAt") createdAt: String + @Query("created_at") createdAt: String ) : Response> } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/common/SocialCapsules.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/common/SocialCapsules.kt index 4c7fd28a4..0b60f61f6 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/common/SocialCapsules.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/common/SocialCapsules.kt @@ -1,6 +1,7 @@ package com.droidblossom.archive.domain.model.common data class SocialCapsules ( + val capsuleId:Long, val title:String, val content:String, val createdDate:String, diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/capsule/CapsuleDetailActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/capsule/CapsuleDetailActivity.kt index 20299058c..8bb50696c 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/capsule/CapsuleDetailActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/capsule/CapsuleDetailActivity.kt @@ -62,7 +62,7 @@ class CapsuleDetailActivity : } HomeFragment.CapsuleType.GROUP -> { - + } HomeFragment.CapsuleType.PUBLIC -> { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/adapter/SocialFriendCapsuleRVA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/adapter/SocialFriendCapsuleRVA.kt index c9375f731..91981e09e 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/adapter/SocialFriendCapsuleRVA.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/adapter/SocialFriendCapsuleRVA.kt @@ -64,7 +64,7 @@ class SocialFriendCapsuleRVA() : ListAdapter() { override fun areItemsTheSame(oldItem: SocialCapsules, newItem: SocialCapsules): Boolean { - return oldItem === newItem + return oldItem.capsuleId == newItem.capsuleId } override fun areContentsTheSame(oldItem: SocialCapsules, newItem: SocialCapsules): Boolean { From 3815d1757ce4da254df770be2165dac823510dd9 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Wed, 10 Apr 2024 23:22:15 +0900 Subject: [PATCH 181/301] =?UTF-8?q?feat=20:=20=EC=B9=9C=EC=B6=94=20?= =?UTF-8?q?=EC=9A=94=EC=B2=AD=ED=9B=84=20=EC=9A=94=EC=B2=AD=EC=83=81?= =?UTF-8?q?=ED=83=9C=EB=A1=9C=20=EB=B3=80=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../friend/addfriend/AddFriendViewModel.kt | 1 + .../addfriend/AddFriendViewModelImpl.kt | 27 +++++++++++++++---- .../addfriend/SearchFriendNicknameFragment.kt | 4 +++ .../addfriend/SearchFriendNumberFragment.kt | 8 +++++- 4 files changed, 34 insertions(+), 6 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModel.kt index 5b4bf4ca4..dea7de6ea 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModel.kt @@ -32,5 +32,6 @@ interface AddFriendViewModel { data class ShowToastMessage(val message : String) : AddEvent() object OpenLoading : AddEvent() object CloseLoading : AddEvent() + object NotificationChange : AddEvent() } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt index ec01416da..337da4ad1 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt @@ -58,6 +58,8 @@ class AddFriendViewModelImpl @Inject constructor( override val isSearchNumOpen: StateFlow get() = _isSearchNumOpen + + //Name override fun requestFriends() { viewModelScope.launch { checkedList.value.forEach { friend -> @@ -68,7 +70,11 @@ class AddFriendViewModelImpl @Inject constructor( _addEvent.emit(AddFriendViewModel.AddEvent.ShowToastMessage("친구 요청 오류 발생")) } } + changeRequestUI(friend.id) } + _checkedList.emit(listOf()) + _addEvent.emit(AddFriendViewModel.AddEvent.NotificationChange) + } } @@ -109,8 +115,6 @@ class AddFriendViewModelImpl @Inject constructor( } } - //Name - //Num override fun searchNum() { @@ -133,7 +137,7 @@ class AddFriendViewModelImpl @Inject constructor( viewModelScope.launch { _addEvent.emit(AddFriendViewModel.AddEvent.OpenLoading) friendsSearchPhoneUseCase(FriendsSearchPhoneRequest(phones.filter { - it.length == 11 && it.substring(0,3) == "010" + it.length == 11 && it.substring(0, 3) == "010" })).collect { result -> result.onSuccess { response -> _addFriendList.emit(response.friends) @@ -147,10 +151,23 @@ class AddFriendViewModelImpl @Inject constructor( } } - - fun closeLoading(){ + fun closeLoading() { viewModelScope.launch { _addEvent.emit(AddFriendViewModel.AddEvent.CloseLoading) } } + + private fun changeRequestUI(id: Long) { + val newList = addFriendListUI.value + val index = newList.indexOfFirst { + it.id == id + } + if (index != -1) { + newList[index].isFriendRequest = true + newList[index].isChecked = false + viewModelScope.launch { + _addFriendListUI.emit(newList) + } + } + } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNicknameFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNicknameFragment.kt index 0d18e4afd..aa0cdc423 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNicknameFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNicknameFragment.kt @@ -88,6 +88,10 @@ class SearchFriendNicknameFragment : showToastMessage(event.message) } + is AddFriendViewModel.AddEvent.NotificationChange -> { + addFriendRVA.notifyDataSetChanged() + } + else -> {} } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNumberFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNumberFragment.kt index bb88a0f4b..8d743b282 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNumberFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNumberFragment.kt @@ -23,7 +23,9 @@ import com.droidblossom.archive.presentation.base.BaseFragment import com.droidblossom.archive.presentation.ui.mypage.friend.addfriend.adapter.AddFriendRVA import com.droidblossom.archive.util.ContactsUtils import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch +import okhttp3.internal.notify @AndroidEntryPoint @@ -49,7 +51,7 @@ class SearchFriendNumberFragment : permissionCheck() } - @SuppressLint("ClickableViewAccessibility") + @SuppressLint("ClickableViewAccessibility", "NotifyDataSetChanged") private fun initView() { val layoutParams = binding.closeBtn.layoutParams as ViewGroup.MarginLayoutParams @@ -131,6 +133,10 @@ class SearchFriendNumberFragment : showLoading(requireContext()) } + is AddFriendViewModel.AddEvent.NotificationChange -> { + addFriendRVA.notifyDataSetChanged() + } + else -> {} } } From 93da436009a13b545b9b5275912b66b897e43f9d Mon Sep 17 00:00:00 2001 From: comst19 Date: Thu, 11 Apr 2024 01:33:49 +0900 Subject: [PATCH 182/301] =?UTF-8?q?fix:=20thrubnailImage=EA=B0=80=20null?= =?UTF-8?q?=EC=9D=BC=20=EB=95=8C=20=EC=98=A4=EB=A5=98=20=EB=B0=9C=EC=83=9D?= =?UTF-8?q?=20=EB=AC=B8=EC=A0=9C=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/dto/open/response/PublicCapsuleResponseDto.kt | 4 ++-- .../archive/domain/model/common/SocialCapsules.kt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/open/response/PublicCapsuleResponseDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/open/response/PublicCapsuleResponseDto.kt index 99d507658..168c2e184 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/open/response/PublicCapsuleResponseDto.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/open/response/PublicCapsuleResponseDto.kt @@ -32,7 +32,7 @@ data class PublicCapsuleResponseDto ( roadName = this.roadName, address = this.address, hasThumbnail = !(imageUrls.isNullOrEmpty() && videoUrls.isNullOrEmpty()), - thumbnailImage = imageUrls?.firstOrNull() ?: videoUrls?.firstOrNull(), - isOpened = false + thumbnailImage = imageUrls?.firstOrNull() ?: videoUrls?.firstOrNull() ?: "", + isOpened = this.isOpened ) } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/common/SocialCapsules.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/common/SocialCapsules.kt index 0b60f61f6..71be14ef2 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/common/SocialCapsules.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/common/SocialCapsules.kt @@ -9,7 +9,7 @@ data class SocialCapsules ( val profileUrl:String, val capsuleSkinUrl: String, val nickNameOrGroupName:String, - val thumbnailImage:String?, + val thumbnailImage:String, val roadName: String, val address: String, val hasThumbnail: Boolean, From 2c97553f33e2066562f325e7757b0bacc897a16b Mon Sep 17 00:00:00 2001 From: comst19 Date: Thu, 11 Apr 2024 10:14:09 +0900 Subject: [PATCH 183/301] =?UTF-8?q?design:=20=EC=97=B4=EB=A6=B0=20?= =?UTF-8?q?=EC=BA=A1=EC=8A=90=20=EB=94=94=EC=9E=90=EC=9D=B8=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/res/layout/item_social_capsule_open.xml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/frontend/ARchive/app/src/main/res/layout/item_social_capsule_open.xml b/frontend/ARchive/app/src/main/res/layout/item_social_capsule_open.xml index 5d4601788..db204f113 100644 --- a/frontend/ARchive/app/src/main/res/layout/item_social_capsule_open.xml +++ b/frontend/ARchive/app/src/main/res/layout/item_social_capsule_open.xml @@ -76,7 +76,7 @@ android:ellipsize="end" android:maxLines="1" android:text="@{item.nickNameOrGroupName}" - android:textAppearance="@style/TextAppearance.App.caption2" + android:textAppearance="@style/TextAppearance.App.caption4" android:textColor="@color/gray_700" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" @@ -112,6 +112,7 @@ android:maxLines="2" android:ellipsize="end" android:text="@{item.content}" + android:textColor="@color/black" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" @@ -126,6 +127,8 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" app:strokeWidth="@null" + android:background="@color/white" + android:visibility="@{item.hasThumbnail ? View.VISIBLE : View.GONE}" app:cardCornerRadius="19.5dp" android:layout_marginTop="@dimen/margin" app:layout_constraintTop_toBottomOf="@id/capsuleContentTextView" @@ -135,7 +138,6 @@ android:id="@+id/capsuleContentImageView" android:layout_width="match_parent" android:layout_height="match_parent" - android:visibility="@{item.hasThumbnail ? View.VISIBLE : View.GONE}" bind:baseImg="@{@drawable/img}" bind:url="@{item.thumbnailImage}" android:scaleType="fitXY"/> @@ -163,8 +165,8 @@ android:layout_height="wrap_content" android:ellipsize="end" android:maxLines="1" - android:textAppearance="@style/TextAppearance.App.caption2" - android:textColor="@color/gray_700" + android:textAppearance="@style/TextAppearance.App.caption4" + android:textColor="@color/gray_600" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@id/middleGuideline2" app:layout_constraintStart_toStartOf="parent" @@ -180,8 +182,8 @@ android:gravity="end" android:ellipsize="end" android:maxLines="1" - android:textAppearance="@style/TextAppearance.App.caption2" - android:textColor="@color/gray_700" + android:textAppearance="@style/TextAppearance.App.caption4" + android:textColor="@color/gray_600" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@id/middleGuideline2" From bdf2df529c017d6f3bd1ddcd1927fc47e2855bc8 Mon Sep 17 00:00:00 2001 From: comst19 Date: Thu, 11 Apr 2024 12:02:45 +0900 Subject: [PATCH 184/301] =?UTF-8?q?feat:=20=EC=86=8C=EC=85=9C=20=EB=A6=AC?= =?UTF-8?q?=EC=82=AC=EC=9D=B4=ED=81=B4=EB=9F=AC=EB=B7=B0=20=EC=95=84?= =?UTF-8?q?=EC=9D=B4=ED=85=9C=EC=97=90=20=ED=81=B4=EB=A6=AD=20=EC=9D=B4?= =?UTF-8?q?=EB=B2=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/social/adapter/SocialFriendCapsuleRVA.kt | 12 +++++++++++- .../social/page/friend/SocialFriendFragment.kt | 17 ++++++++++++++++- .../ui/social/page/group/SocialGroupFragment.kt | 17 ++++++++++++++++- 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/adapter/SocialFriendCapsuleRVA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/adapter/SocialFriendCapsuleRVA.kt index 91981e09e..7d480bb00 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/adapter/SocialFriendCapsuleRVA.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/adapter/SocialFriendCapsuleRVA.kt @@ -8,8 +8,12 @@ import androidx.recyclerview.widget.RecyclerView import com.droidblossom.archive.databinding.ItemSocialCapsuleCloseBinding import com.droidblossom.archive.databinding.ItemSocialCapsuleOpenBinding import com.droidblossom.archive.domain.model.common.SocialCapsules +import com.droidblossom.archive.presentation.ui.home.HomeFragment -class SocialFriendCapsuleRVA() : ListAdapter(differ) { +class SocialFriendCapsuleRVA( + private val openCapsuleClick: (Long) -> Unit, + private val closeCapsuleClick:() -> Unit +) : ListAdapter(differ) { inner class OpenedCapsuleViewHolder( private val binding: ItemSocialCapsuleOpenBinding @@ -17,6 +21,9 @@ class SocialFriendCapsuleRVA() : ListAdapter() private val socialFriendCapsuleRVA by lazy { - SocialFriendCapsuleRVA() + SocialFriendCapsuleRVA( + { id -> + startActivity( + CapsuleDetailActivity.newIntent( + requireContext(), + id, + HomeFragment.CapsuleType.PUBLIC + ) + ) + }, + { + showToastMessage("개봉되지 않은 캡슐입니다.") + } + ) } override fun observeData() { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupFragment.kt index 407d041b1..a318ccf2c 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupFragment.kt @@ -16,6 +16,8 @@ import androidx.lifecycle.repeatOnLifecycle import com.droidblossom.archive.R import com.droidblossom.archive.databinding.FragmentSocialGroupBinding import com.droidblossom.archive.presentation.base.BaseFragment +import com.droidblossom.archive.presentation.ui.capsule.CapsuleDetailActivity +import com.droidblossom.archive.presentation.ui.home.HomeFragment import com.droidblossom.archive.presentation.ui.social.adapter.SocialFriendCapsuleRVA import com.droidblossom.archive.presentation.ui.social.adapter.TestSocialFriendModel import com.droidblossom.archive.util.SpaceItemDecoration @@ -29,7 +31,20 @@ class SocialGroupFragment : BaseFragment() private val socialFriendCapsuleRVA by lazy { - SocialFriendCapsuleRVA() + SocialFriendCapsuleRVA( + { id -> + startActivity( + CapsuleDetailActivity.newIntent( + requireContext(), + id, + HomeFragment.CapsuleType.GROUP + ) + ) + }, + { + showToastMessage("개봉되지 않은 캡슐입니다.") + } + ) } override fun observeData() { viewLifecycleOwner.lifecycleScope.launch { From 41c9f465c3cd4dde48649851959395d9acbdf493 Mon Sep 17 00:00:00 2001 From: comst19 Date: Thu, 11 Apr 2024 14:44:57 +0900 Subject: [PATCH 185/301] =?UTF-8?q?build:=20Swiperefreshlayout=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/ARchive/app/build.gradle | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frontend/ARchive/app/build.gradle b/frontend/ARchive/app/build.gradle index df3b7b11b..b35078fd1 100644 --- a/frontend/ARchive/app/build.gradle +++ b/frontend/ARchive/app/build.gradle @@ -186,6 +186,9 @@ dependencies { // TedNaverMapClustering: https://github.com/ParkSangGwon/TedNaverMapClustering //implementation 'io.github.ParkSangGwon:tedclustering-naver:x.y.z' implementation 'io.github.ParkSangGwon:tedclustering-naver:1.0.2' + + // Swiperefreshlayout: https://developer.android.com/jetpack/androidx/releases/swiperefreshlayout?hl=ko#kts + implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0") } kapt { correctErrorTypes true From 1e059feedb5adc5043b57be50b2e4c2839a62030 Mon Sep 17 00:00:00 2001 From: comst19 Date: Thu, 11 Apr 2024 14:46:31 +0900 Subject: [PATCH 186/301] =?UTF-8?q?feat:=20=EC=95=84=EB=9E=98=EB=A1=9C=20?= =?UTF-8?q?=EC=8A=A4=ED=81=AC=EB=A1=A4=20=EC=8B=9C=20=EC=B5=9C=EA=B7=BC=20?= =?UTF-8?q?PublicCapsule=20=EB=B0=9B=EC=95=84=EC=98=A4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../page/friend/SocialFriendFragment.kt | 24 ++++++++--------- .../page/friend/SocialFriendViewModel.kt | 2 ++ .../page/friend/SocialFriendViewModelImpl.kt | 27 ++++++++++++++++--- .../res/layout/fragment_social_friend.xml | 26 ++++++++++++------ 4 files changed, 55 insertions(+), 24 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendFragment.kt index 9d48ca3f0..9476367c2 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendFragment.kt @@ -3,12 +3,8 @@ package com.droidblossom.archive.presentation.ui.social.page.friend import android.annotation.SuppressLint import android.graphics.Rect import android.os.Bundle -import android.util.TypedValue -import androidx.fragment.app.Fragment -import android.view.LayoutInflater import android.view.MotionEvent import android.view.View -import android.view.ViewGroup import android.view.inputmethod.EditorInfo import android.view.inputmethod.InputMethodManager import androidx.constraintlayout.widget.ConstraintLayout @@ -20,15 +16,10 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.droidblossom.archive.R import com.droidblossom.archive.databinding.FragmentSocialFriendBinding -import com.droidblossom.archive.databinding.FragmentSocialGroupBinding import com.droidblossom.archive.presentation.base.BaseFragment import com.droidblossom.archive.presentation.ui.capsule.CapsuleDetailActivity import com.droidblossom.archive.presentation.ui.home.HomeFragment -import com.droidblossom.archive.presentation.ui.mypage.MyPageViewModel -import com.droidblossom.archive.presentation.ui.mypage.setting.SettingActivity import com.droidblossom.archive.presentation.ui.social.adapter.SocialFriendCapsuleRVA -import com.droidblossom.archive.presentation.ui.social.adapter.TestSocialFriendModel -import com.droidblossom.archive.presentation.ui.social.page.group.SocialGroupViewModelImpl import com.droidblossom.archive.util.SpaceItemDecoration import com.droidblossom.archive.util.updateTopConstraintsForSearch import dagger.hilt.android.AndroidEntryPoint @@ -61,7 +52,7 @@ class SocialFriendFragment : BaseFragment - socialFriendCapsuleRVA.submitList(publicCapsule) + viewModel.publicCapsules.collect{ publicCapsules -> + socialFriendCapsuleRVA.submitList(publicCapsules){ + if (binding.socialFriendSwipeRefreshLayout.isRefreshing){ + binding.socialFriendSwipeRefreshLayout.isRefreshing = false + binding.socialFriendRV.scrollToPosition(0) + } + } } } } @@ -113,6 +109,9 @@ class SocialFriendFragment : BaseFragment get() = _lastCreatedTime + init { + getPublicCapsulePage() + } + override fun socialFriendEvent(event: SocialFriendViewModel.SocialFriendEvent) { viewModelScope.launch { _socialFriendEvents.emit(event) @@ -64,9 +68,7 @@ class SocialFriendViewModelImpl @Inject constructor( } } - init { - getPublicCapsulePage() - } + override fun getPublicCapsulePage(){ viewModelScope.launch { if (hasNextPage.value) { @@ -88,6 +90,25 @@ class SocialFriendViewModelImpl @Inject constructor( } } + override fun getLatestPublicCapsule(){ + viewModelScope.launch { + publicCapsulePageUseCase( + PublicCapsuleSliceRequestDto( + 15, + DateUtils.dataServerString + ) + ).collect { result -> + result.onSuccess { + _hasNextPage.value = it.hasNext + _publicCapsules.emit(it.publicCapsules) + _lastCreatedTime.value = publicCapsules.value.last().createdDate + }.onFail { + socialFriendEvent(SocialFriendViewModel.SocialFriendEvent.ShowToastMessage("공개캡슐 불러오기 실패")) + } + } + } + } + override fun searchFriendCapsule(){ } diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_social_friend.xml b/frontend/ARchive/app/src/main/res/layout/fragment_social_friend.xml index f7c68f36b..1228f5f31 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_social_friend.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_social_friend.xml @@ -108,19 +108,29 @@ - - + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/searchBtn"> + + + + + + From bd0555462434bf75553b3f4d1995fcea95bef301 Mon Sep 17 00:00:00 2001 From: comst19 Date: Thu, 11 Apr 2024 14:46:59 +0900 Subject: [PATCH 187/301] =?UTF-8?q?feat:=20=EC=95=84=EB=9E=98=EB=A1=9C=20?= =?UTF-8?q?=EC=8A=A4=ED=81=AC=EB=A1=A4=20=EC=8B=9C=20=EC=B5=9C=EA=B7=BC=20?= =?UTF-8?q?GroupCapsule=20=EB=B0=9B=EC=95=84=EC=98=A4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../social/page/group/SocialGroupFragment.kt | 40 ++++++++++- .../social/page/group/SocialGroupViewModel.kt | 8 +++ .../page/group/SocialGroupViewModelImpl.kt | 67 +++++++++++++++++++ .../main/res/layout/fragment_social_group.xml | 21 ++++-- 4 files changed, 126 insertions(+), 10 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupFragment.kt index a318ccf2c..db84dc6a7 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupFragment.kt @@ -3,6 +3,7 @@ package com.droidblossom.archive.presentation.ui.social.page.group import android.annotation.SuppressLint import android.graphics.Rect import android.os.Bundle +import android.util.Log import android.util.TypedValue import android.view.MotionEvent import android.view.View @@ -13,8 +14,11 @@ import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView import com.droidblossom.archive.R import com.droidblossom.archive.databinding.FragmentSocialGroupBinding +import com.droidblossom.archive.domain.model.common.SocialCapsules import com.droidblossom.archive.presentation.base.BaseFragment import com.droidblossom.archive.presentation.ui.capsule.CapsuleDetailActivity import com.droidblossom.archive.presentation.ui.home.HomeFragment @@ -50,7 +54,7 @@ class SocialGroupFragment : BaseFragment + socialFriendCapsuleRVA.submitList(groupCapsules){ + if (binding.socialFriendSwipeRefreshLayout.isRefreshing){ + binding.socialFriendSwipeRefreshLayout.isRefreshing = false + binding.socialGroupRV.scrollToPosition(0) + } + } + } + } + } } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding.vm = viewModel - initSearchEdit() initRVA() + initSearchEdit() } private fun initRVA() { binding.socialGroupRV.adapter = socialFriendCapsuleRVA - val spaceInPixels = resources.getDimensionPixelSize(R.dimen.margin) binding.socialGroupRV.addItemDecoration(SpaceItemDecoration(spaceBottom = spaceInPixels)) + binding.socialFriendSwipeRefreshLayout.setOnRefreshListener { + viewModel.getLatestGroupCapsule() + } + + binding.socialGroupRV.addOnScrollListener(object : RecyclerView.OnScrollListener() { + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + super.onScrollStateChanged(recyclerView, newState) + + if (newState == RecyclerView.SCROLL_STATE_IDLE || newState == RecyclerView.SCROLL_STATE_DRAGGING) { + val layoutManager = recyclerView.layoutManager as LinearLayoutManager + val totalItemCount = layoutManager.itemCount + val lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition() + if (totalItemCount - lastVisibleItemPosition <= 5) { + viewModel.getGroupCapsulePage() + } + } + } + }) } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupViewModel.kt index 42f090dc2..2b1f61180 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupViewModel.kt @@ -1,13 +1,21 @@ package com.droidblossom.archive.presentation.ui.social.page.group +import com.droidblossom.archive.domain.model.common.SocialCapsules import kotlinx.coroutines.flow.StateFlow interface SocialGroupViewModel { + val groupCapsules : StateFlow> val isSearchOpen : StateFlow + val hasNextPage : StateFlow + val lastCreatedTime : StateFlow fun openSearchGroupCapsule() fun closeSearchGroupCapsule() fun searchGroupCapsule() + + fun getGroupCapsulePage() + + fun getLatestGroupCapsule() } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupViewModelImpl.kt index 7a5395976..4956b2d0a 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupViewModelImpl.kt @@ -1,7 +1,13 @@ package com.droidblossom.archive.presentation.ui.social.page.group import androidx.lifecycle.viewModelScope +import com.droidblossom.archive.data.dto.open.request.PublicCapsuleSliceRequestDto +import com.droidblossom.archive.domain.model.common.SocialCapsules import com.droidblossom.archive.presentation.base.BaseViewModel +import com.droidblossom.archive.presentation.ui.social.page.friend.SocialFriendViewModel +import com.droidblossom.archive.util.DateUtils +import com.droidblossom.archive.util.onFail +import com.droidblossom.archive.util.onSuccess import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -18,6 +24,21 @@ class SocialGroupViewModelImpl @Inject constructor( override val isSearchOpen: StateFlow get() = _isSearchOpen + private val _groupCapsules = MutableStateFlow(listOf()) + override val groupCapsules: StateFlow> + get() = _groupCapsules + + private val _hasNextPage = MutableStateFlow(true) + override val hasNextPage: StateFlow + get() = _hasNextPage + private val _lastCreatedTime = MutableStateFlow(DateUtils.dataServerString) + override val lastCreatedTime: StateFlow + get() = _lastCreatedTime + + init { + //getGroupCapsulePage() + } + override fun openSearchGroupCapsule() { viewModelScope.launch { _isSearchOpen.emit(true) @@ -34,4 +55,50 @@ class SocialGroupViewModelImpl @Inject constructor( } + override fun getGroupCapsulePage(){ + viewModelScope.launch { + if (hasNextPage.value) { + /* + publicCapsulePageUseCase( + PublicCapsuleSliceRequestDto( + 15, + lastCreatedTime.value + ) + ).collect { result -> + result.onSuccess { + _hasNextPage.value = it.hasNext + _publicCapsules.emit(publicCapsules.value + it.publicCapsules) + _lastCreatedTime.value = publicCapsules.value.last().createdDate + }.onFail { + socialFriendEvent(SocialFriendViewModel.SocialFriendEvent.ShowToastMessage("공개캡슐 불러오기 실패")) + } + } + + */ + } + } + } + + override fun getLatestGroupCapsule(){ + viewModelScope.launch { + /* + publicCapsulePageUseCase( + PublicCapsuleSliceRequestDto( + 15, + DateUtils.dataServerString + ) + ).collect { result -> + result.onSuccess { + _hasNextPage.value = it.hasNext + _publicCapsules.emit(it.publicCapsules) + _lastCreatedTime.value = publicCapsules.value.last().createdDate + }.onFail { + socialFriendEvent(SocialFriendViewModel.SocialFriendEvent.ShowToastMessage("공개캡슐 불러오기 실패")) + } + } + + */ + } + } + } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_social_group.xml b/frontend/ARchive/app/src/main/res/layout/fragment_social_group.xml index 01fe941a1..3d81248c8 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_social_group.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_social_group.xml @@ -107,20 +107,27 @@ - + app:layout_constraintTop_toBottomOf="@id/searchBtn"> + + + From 6dc5b7a8f851530cca9b822cc7e4aa690aedaf90 Mon Sep 17 00:00:00 2001 From: comst19 Date: Fri, 12 Apr 2024 19:39:12 +0900 Subject: [PATCH 188/301] =?UTF-8?q?refact:=20=ED=9A=8C=EC=9B=90=20?= =?UTF-8?q?=EC=83=81=EC=84=B8=EC=A0=95=EB=B3=B4=20=EC=A1=B0=ED=9A=8C=20api?= =?UTF-8?q?=20response=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/dto/member/response/MemberDetailResponseDto.kt | 8 ++++++-- .../archive/domain/model/member/MemberDetail.kt | 4 +++- .../archive/presentation/ui/mypage/MyPageViewModelImpl.kt | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/member/response/MemberDetailResponseDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/member/response/MemberDetailResponseDto.kt index bf2424dde..28d431471 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/member/response/MemberDetailResponseDto.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/member/response/MemberDetailResponseDto.kt @@ -6,12 +6,16 @@ import java.io.Serializable data class MemberDetailResponseDto( val nickname : String, val profileUrl : String, - val phone : String + val tag : String, + val friendCount: Int, + val groupCount: Int ) : Serializable { fun toModel() = MemberDetail( nickname = this.nickname, profileUrl = this.profileUrl, - phone = this.phone + tag = this.tag, + friendCount = this.friendCount, + groupCount = this.groupCount, ) } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/member/MemberDetail.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/member/MemberDetail.kt index 69d401588..496a9fca5 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/member/MemberDetail.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/member/MemberDetail.kt @@ -3,5 +3,7 @@ package com.droidblossom.archive.domain.model.member data class MemberDetail( val nickname : String, val profileUrl : String, - val phone : String + val tag : String, + val friendCount: Int, + val groupCount: Int ) \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt index 69adafffb..ec703e793 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt @@ -35,7 +35,7 @@ class MyPageViewModelImpl @Inject constructor( get() =_myPageEvents.asSharedFlow() - private val _myInfo = MutableStateFlow(MemberDetail("USER", "", "")) + private val _myInfo = MutableStateFlow(MemberDetail("USER", "", "",0,0)) override val myInfo: StateFlow get() = _myInfo From 3ad6dcf216a1bc42e518d7e11e3a0ac5cd8c78d5 Mon Sep 17 00:00:00 2001 From: comst19 Date: Mon, 15 Apr 2024 23:25:25 +0900 Subject: [PATCH 189/301] =?UTF-8?q?refact:=20=EA=B7=BC=EC=B2=98=20?= =?UTF-8?q?=EC=BA=A1=EC=8A=90=20=EC=A1=B0=ED=9A=8C=20AR=20=EB=B2=84?= =?UTF-8?q?=EC=A0=84=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/data/repository/CapsuleRepositoryImpl.kt | 6 +++--- .../archive/data/source/remote/api/CapsuleService.kt | 4 ++-- .../archive/domain/repository/CapsuleRepository.kt | 2 +- ...NearbyCapsulesUseCase.kt => NearbyCapsulesARUseCase.kt} | 4 ++-- .../archive/presentation/ui/camera/CameraViewModelImpl.kt | 7 +++---- .../archive/presentation/ui/home/HomeViewModelImpl.kt | 6 +++--- 6 files changed, 14 insertions(+), 15 deletions(-) rename frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule/{NearbyCapsulesUseCase.kt => NearbyCapsulesARUseCase.kt} (85%) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/CapsuleRepositoryImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/CapsuleRepositoryImpl.kt index 8509098f7..0e6f3b453 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/CapsuleRepositoryImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/CapsuleRepositoryImpl.kt @@ -23,13 +23,13 @@ class CapsuleRepositoryImpl @Inject constructor( return apiHandler({ api.patchCapsuleOpenApi(capsuleId = capsuleId) }) { response: ResponseBody -> response.result.toModel() } } - override suspend fun NearbyCapsules( + override suspend fun nearbyCapsulesAR( latitude: Double, longitude: Double, distance: Double, - capsule_type: String + capsuleType: String ): RetrofitResult { - return apiHandler({ api.getNearbyCapsulesApi(latitude = latitude, longitude = longitude, distance= distance, capsule_type= capsule_type) }) { response : ResponseBody -> response.result.toModel() } + return apiHandler({ api.getNearbyCapsulesApi(latitude = latitude, longitude = longitude, distance= distance, capsuleType= capsuleType) }) { response : ResponseBody -> response.result.toModel() } } override suspend fun getAddress( diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/CapsuleService.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/CapsuleService.kt index de77a9958..4da96a603 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/CapsuleService.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/CapsuleService.kt @@ -18,12 +18,12 @@ interface CapsuleService { @Path("capsule_id") capsuleId : Long, ) : Response> - @GET("capsules/nearby") + @GET("capsules/nearby/ar") suspend fun getNearbyCapsulesApi( @Query("latitude") latitude : Double, @Query("longitude") longitude : Double, @Query("distance") distance : Double, - @Query("capsule_type") capsule_type : String + @Query("capsule_type") capsuleType : String ) : Response> @GET("map/full-address") diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/CapsuleRepository.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/CapsuleRepository.kt index dc6e944d4..23f7d3c8f 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/CapsuleRepository.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/CapsuleRepository.kt @@ -12,7 +12,7 @@ interface CapsuleRepository { capsuleId : Long ): RetrofitResult - suspend fun NearbyCapsules( + suspend fun nearbyCapsulesAR( latitude: Double, longitude: Double, distance: Double, diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule/NearbyCapsulesUseCase.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule/NearbyCapsulesARUseCase.kt similarity index 85% rename from frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule/NearbyCapsulesUseCase.kt rename to frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule/NearbyCapsulesARUseCase.kt index 4d8ea98c3..2f6ddce15 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule/NearbyCapsulesUseCase.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule/NearbyCapsulesARUseCase.kt @@ -8,14 +8,14 @@ import com.droidblossom.archive.util.onSuccess import kotlinx.coroutines.flow.flow import javax.inject.Inject -class NearbyCapsulesUseCase @Inject constructor( +class NearbyCapsulesARUseCase @Inject constructor( private val repository: CapsuleRepository ) { suspend operator fun invoke(latitude: Double, longitude: Double, distance: Double, capsuleType: String) = flow { try { - emit(repository.NearbyCapsules(latitude, longitude, distance, capsuleType).onSuccess { + emit(repository.nearbyCapsulesAR(latitude, longitude, distance, capsuleType).onSuccess { Log.d("성패", "$it") }.onFail { Log.d("실패", "$it") diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModelImpl.kt index 659690685..4c46ee8ac 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModelImpl.kt @@ -1,9 +1,8 @@ package com.droidblossom.archive.presentation.ui.camera -import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope import com.droidblossom.archive.domain.model.common.CapsuleMarker -import com.droidblossom.archive.domain.usecase.capsule.NearbyCapsulesUseCase +import com.droidblossom.archive.domain.usecase.capsule.NearbyCapsulesARUseCase import com.droidblossom.archive.presentation.base.BaseViewModel import com.droidblossom.archive.util.onFail import com.droidblossom.archive.util.onSuccess @@ -20,7 +19,7 @@ import javax.inject.Inject @HiltViewModel class CameraViewModelImpl@Inject constructor( - private val nearbyCapsulesUseCase: NearbyCapsulesUseCase + private val nearbyCapsulesARUseCase: NearbyCapsulesARUseCase ) : BaseViewModel(), CameraViewModel { private val _cameraEvents = MutableSharedFlow() @@ -56,7 +55,7 @@ class CameraViewModelImpl@Inject constructor( override fun getCapsules(latitude: Double, longitude: Double) : List { viewModelScope.launch { - nearbyCapsulesUseCase(latitude,longitude,1.0,"ALL").collect{ result-> + nearbyCapsulesARUseCase(latitude,longitude,0.5,"ALL").collect{ result-> result.onSuccess { _capsuleList.emit(it.capsules) _capsuleListSize.value = _capsuleList.value.size diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeViewModelImpl.kt index 944a31f11..979f50f49 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeViewModelImpl.kt @@ -3,7 +3,7 @@ package com.droidblossom.archive.presentation.ui.home import android.util.Log import androidx.lifecycle.viewModelScope import com.droidblossom.archive.domain.model.common.CapsuleMarker -import com.droidblossom.archive.domain.usecase.capsule.NearbyCapsulesUseCase +import com.droidblossom.archive.domain.usecase.capsule.NearbyCapsulesARUseCase import com.droidblossom.archive.presentation.base.BaseViewModel import com.droidblossom.archive.util.onFail import com.droidblossom.archive.util.onSuccess @@ -18,7 +18,7 @@ import javax.inject.Inject @HiltViewModel class HomeViewModelImpl @Inject constructor( - private val nearbyCapsulesUseCase: NearbyCapsulesUseCase, + private val nearbyCapsulesARUseCase: NearbyCapsulesARUseCase, ) : BaseViewModel(), HomeViewModel { private val _homeEvents = MutableSharedFlow() @@ -109,7 +109,7 @@ class HomeViewModelImpl @Inject constructor( override fun getNearbyCapsules(latitude: Double, longitude: Double, distance: Double, capsuleType: String) { Log.d("티티","$latitude, $longitude, $distance, $capsuleType") viewModelScope.launch { - nearbyCapsulesUseCase(latitude,longitude, distance, capsuleType ).collect{result-> + nearbyCapsulesARUseCase(latitude,longitude, distance, capsuleType ).collect{ result-> result.onSuccess { _capsuleList.emit(it.capsules) Log.d("티티","getNearbyCapsules 성공") From 5f25c4dc9c315034b7025e81f3f945e24bca3875 Mon Sep 17 00:00:00 2001 From: comst19 Date: Tue, 16 Apr 2024 01:32:44 +0900 Subject: [PATCH 190/301] =?UTF-8?q?feat:=20=EA=B7=BC=EC=B2=98=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20Home(=EC=A7=80=EB=8F=84)=20api=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../response/NearbyCapsuleResponseDto.kt | 10 ++++-- .../data/dto/common/CapsuleSummaryDto.kt | 12 +++++-- .../data/repository/CapsuleRepositoryImpl.kt | 17 +++++++--- .../data/source/remote/api/CapsuleService.kt | 10 +++++- .../CapsuleAnchor.kt} | 4 +-- .../domain/model/capsule/CapsuleAnchors.kt | 5 +++ .../domain/model/capsule/CapsuleMarker.kt | 10 ++++++ .../domain/model/capsule/CapsuleMarkers.kt | 5 +++ .../domain/model/capsule/NearbyCapsule.kt | 8 ----- .../domain/repository/CapsuleRepository.kt | 11 +++++-- .../capsule/GetCapsuleImagesUseCase.kt | 3 +- .../capsule/NearbyCapsulesHomeUseCase.kt | 32 +++++++++++++++++++ .../presentation/ui/camera/ARContentNode.kt | 4 +-- .../presentation/ui/camera/CameraFragment.kt | 5 ++- .../presentation/ui/camera/CameraViewModel.kt | 7 ++-- .../ui/camera/CameraViewModelImpl.kt | 10 +++--- .../presentation/ui/home/HomeFragment.kt | 15 +++------ .../presentation/ui/home/HomeViewModel.kt | 3 +- .../presentation/ui/home/HomeViewModelImpl.kt | 11 +++---- .../presentation/ui/home/MapCapsuleMarker.kt | 7 ++-- 20 files changed, 129 insertions(+), 60 deletions(-) rename frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/{common/CapsuleMarker.kt => capsule/CapsuleAnchor.kt} (73%) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/capsule/CapsuleAnchors.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/capsule/CapsuleMarker.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/capsule/CapsuleMarkers.kt delete mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/capsule/NearbyCapsule.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule/NearbyCapsulesHomeUseCase.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/capsule/response/NearbyCapsuleResponseDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/capsule/response/NearbyCapsuleResponseDto.kt index a6a692807..a63caccd8 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/capsule/response/NearbyCapsuleResponseDto.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/capsule/response/NearbyCapsuleResponseDto.kt @@ -1,12 +1,16 @@ package com.droidblossom.archive.data.dto.capsule.response import com.droidblossom.archive.data.dto.common.CapsuleSummaryDto -import com.droidblossom.archive.domain.model.capsule.NearbyCapsule +import com.droidblossom.archive.domain.model.capsule.CapsuleAnchors +import com.droidblossom.archive.domain.model.capsule.CapsuleMarkers data class NearbyCapsuleResponseDto ( val capsules : List ){ - fun toModel() = NearbyCapsule( - capsules = this.capsules.map { it.toModel() } + fun toMarkerModel() = CapsuleMarkers( + capsuleMarkers = this.capsules.map { it.toMarkerModel() } + ) + fun toAnchorModel() = CapsuleAnchors( + capsuleAnchors = this.capsules.map { it.toAnchorModel() } ) } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/common/CapsuleSummaryDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/common/CapsuleSummaryDto.kt index 3b20356c7..dbff81d36 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/common/CapsuleSummaryDto.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/common/CapsuleSummaryDto.kt @@ -1,6 +1,7 @@ package com.droidblossom.archive.data.dto.common -import com.droidblossom.archive.domain.model.common.CapsuleMarker +import com.droidblossom.archive.domain.model.capsule.CapsuleAnchor +import com.droidblossom.archive.domain.model.capsule.CapsuleMarker import com.droidblossom.archive.presentation.ui.home.HomeFragment data class CapsuleSummaryDto( @@ -13,11 +14,18 @@ data class CapsuleSummaryDto( val dueDate: String, val capsuleType: String, ){ - fun toModel() = CapsuleMarker( + fun toAnchorModel() = CapsuleAnchor( id = this.id, longitude = this.longitude, latitude = this.latitude, capsuleType = HomeFragment.CapsuleType.valueOf(capsuleType), skinUrl = this.capsuleSkinUrl ) + + fun toMarkerModel() = CapsuleMarker( + id = this.id, + longitude = this.longitude, + latitude = this.latitude, + capsuleType = HomeFragment.CapsuleType.valueOf(capsuleType), + ) } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/CapsuleRepositoryImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/CapsuleRepositoryImpl.kt index 0e6f3b453..18f409265 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/CapsuleRepositoryImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/CapsuleRepositoryImpl.kt @@ -5,11 +5,11 @@ import com.droidblossom.archive.data.dto.capsule.response.AddressDataDto import com.droidblossom.archive.data.dto.capsule.response.CapsuleImagesDto import com.droidblossom.archive.data.dto.capsule.response.CapsuleOpenedResponseDto import com.droidblossom.archive.data.dto.capsule.response.NearbyCapsuleResponseDto -import com.droidblossom.archive.data.dto.common.toModel import com.droidblossom.archive.data.source.remote.api.CapsuleService import com.droidblossom.archive.domain.model.capsule.CapsuleImages import com.droidblossom.archive.domain.model.capsule.CapsuleOpenedResponse -import com.droidblossom.archive.domain.model.capsule.NearbyCapsule +import com.droidblossom.archive.domain.model.capsule.CapsuleAnchors +import com.droidblossom.archive.domain.model.capsule.CapsuleMarkers import com.droidblossom.archive.domain.model.common.AddressData import com.droidblossom.archive.domain.repository.CapsuleRepository import com.droidblossom.archive.util.RetrofitResult @@ -23,13 +23,22 @@ class CapsuleRepositoryImpl @Inject constructor( return apiHandler({ api.patchCapsuleOpenApi(capsuleId = capsuleId) }) { response: ResponseBody -> response.result.toModel() } } + override suspend fun nearbyCapsulesHome( + latitude: Double, + longitude: Double, + distance: Double, + capsuleType: String + ): RetrofitResult { + return apiHandler({ api.getNearbyCapsulesHomeApi(latitude = latitude, longitude = longitude, distance= distance, capsuleType= capsuleType) }) { response : ResponseBody -> response.result.toMarkerModel() } + } + override suspend fun nearbyCapsulesAR( latitude: Double, longitude: Double, distance: Double, capsuleType: String - ): RetrofitResult { - return apiHandler({ api.getNearbyCapsulesApi(latitude = latitude, longitude = longitude, distance= distance, capsuleType= capsuleType) }) { response : ResponseBody -> response.result.toModel() } + ): RetrofitResult { + return apiHandler({ api.getNearbyCapsulesARApi(latitude = latitude, longitude = longitude, distance= distance, capsuleType= capsuleType) }) { response : ResponseBody -> response.result.toAnchorModel() } } override suspend fun getAddress( diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/CapsuleService.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/CapsuleService.kt index 4da96a603..bfcd7ad2f 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/CapsuleService.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/CapsuleService.kt @@ -18,8 +18,16 @@ interface CapsuleService { @Path("capsule_id") capsuleId : Long, ) : Response> + @GET("capsules/nearby") + suspend fun getNearbyCapsulesHomeApi( + @Query("latitude") latitude : Double, + @Query("longitude") longitude : Double, + @Query("distance") distance : Double, + @Query("capsule_type") capsuleType : String + ) : Response> + @GET("capsules/nearby/ar") - suspend fun getNearbyCapsulesApi( + suspend fun getNearbyCapsulesARApi( @Query("latitude") latitude : Double, @Query("longitude") longitude : Double, @Query("distance") distance : Double, diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/common/CapsuleMarker.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/capsule/CapsuleAnchor.kt similarity index 73% rename from frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/common/CapsuleMarker.kt rename to frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/capsule/CapsuleAnchor.kt index 088b5b7ac..89df1c99b 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/common/CapsuleMarker.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/capsule/CapsuleAnchor.kt @@ -1,8 +1,8 @@ -package com.droidblossom.archive.domain.model.common +package com.droidblossom.archive.domain.model.capsule import com.droidblossom.archive.presentation.ui.home.HomeFragment -data class CapsuleMarker( +data class CapsuleAnchor( val id : Long, val longitude : Double, val latitude : Double, diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/capsule/CapsuleAnchors.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/capsule/CapsuleAnchors.kt new file mode 100644 index 000000000..92c9081f2 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/capsule/CapsuleAnchors.kt @@ -0,0 +1,5 @@ +package com.droidblossom.archive.domain.model.capsule + +data class CapsuleAnchors( + val capsuleAnchors : List +) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/capsule/CapsuleMarker.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/capsule/CapsuleMarker.kt new file mode 100644 index 000000000..f1ef2f5f2 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/capsule/CapsuleMarker.kt @@ -0,0 +1,10 @@ +package com.droidblossom.archive.domain.model.capsule + +import com.droidblossom.archive.presentation.ui.home.HomeFragment + +data class CapsuleMarker ( + val id : Long, + val longitude : Double, + val latitude : Double, + val capsuleType : HomeFragment.CapsuleType, +) \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/capsule/CapsuleMarkers.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/capsule/CapsuleMarkers.kt new file mode 100644 index 000000000..f59a8eda4 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/capsule/CapsuleMarkers.kt @@ -0,0 +1,5 @@ +package com.droidblossom.archive.domain.model.capsule + +data class CapsuleMarkers( + val capsuleMarkers : List +) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/capsule/NearbyCapsule.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/capsule/NearbyCapsule.kt deleted file mode 100644 index 2b72dcbb1..000000000 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/capsule/NearbyCapsule.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.droidblossom.archive.domain.model.capsule - -import com.droidblossom.archive.data.dto.common.CapsuleSummaryDto -import com.droidblossom.archive.domain.model.common.CapsuleMarker - -data class NearbyCapsule( - val capsules : List -) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/CapsuleRepository.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/CapsuleRepository.kt index 23f7d3c8f..71289e227 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/CapsuleRepository.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/CapsuleRepository.kt @@ -2,7 +2,8 @@ package com.droidblossom.archive.domain.repository import com.droidblossom.archive.domain.model.capsule.CapsuleImages import com.droidblossom.archive.domain.model.capsule.CapsuleOpenedResponse -import com.droidblossom.archive.domain.model.capsule.NearbyCapsule +import com.droidblossom.archive.domain.model.capsule.CapsuleAnchors +import com.droidblossom.archive.domain.model.capsule.CapsuleMarkers import com.droidblossom.archive.domain.model.common.AddressData import com.droidblossom.archive.util.RetrofitResult @@ -12,12 +13,18 @@ interface CapsuleRepository { capsuleId : Long ): RetrofitResult + suspend fun nearbyCapsulesHome( + latitude: Double, + longitude: Double, + distance: Double, + capsuleType: String + ): RetrofitResult suspend fun nearbyCapsulesAR( latitude: Double, longitude: Double, distance: Double, capsuleType: String - ): RetrofitResult + ): RetrofitResult suspend fun getAddress( latitude: Double, diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule/GetCapsuleImagesUseCase.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule/GetCapsuleImagesUseCase.kt index 06458bf8c..55d040eeb 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule/GetCapsuleImagesUseCase.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule/GetCapsuleImagesUseCase.kt @@ -30,4 +30,5 @@ class GetCapsuleImagesUseCase @Inject constructor( } } -} \ No newline at end of file +} + diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule/NearbyCapsulesHomeUseCase.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule/NearbyCapsulesHomeUseCase.kt new file mode 100644 index 000000000..c87216c68 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule/NearbyCapsulesHomeUseCase.kt @@ -0,0 +1,32 @@ +package com.droidblossom.archive.domain.usecase.capsule + +import android.util.Log +import com.droidblossom.archive.domain.repository.CapsuleRepository +import com.droidblossom.archive.util.onException +import com.droidblossom.archive.util.onFail +import com.droidblossom.archive.util.onSuccess +import kotlinx.coroutines.flow.flow +import javax.inject.Inject + +class NearbyCapsulesHomeUseCase @Inject constructor( + private val repository: CapsuleRepository +) { + + suspend operator fun invoke(latitude: Double, longitude: Double, distance: Double, capsuleType: String) = + flow { + try { + emit(repository.nearbyCapsulesHome(latitude, longitude, distance, capsuleType).onSuccess { + Log.d("성패", "$it") + }.onFail { + Log.d("실패", "$it") + }.onException { + Log.d("실패", "$it") + throw Exception(it) + }) + } catch (e: Exception) { + Log.d("예외확인", "$e") + e.printStackTrace() + } + } + +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/ARContentNode.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/ARContentNode.kt index fde472817..c0065baa2 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/ARContentNode.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/ARContentNode.kt @@ -5,7 +5,7 @@ import android.view.LayoutInflater import com.bumptech.glide.Glide import com.droidblossom.archive.R import com.droidblossom.archive.databinding.ItemCapsuleSkinBinding -import com.droidblossom.archive.domain.model.common.CapsuleMarker +import com.droidblossom.archive.domain.model.capsule.CapsuleAnchor import com.droidblossom.archive.presentation.ui.home.dialog.CapsulePreviewDialogFragment import com.droidblossom.archive.util.FragmentManagerProvider import com.google.ar.sceneform.rendering.ViewAttachmentManager @@ -18,7 +18,7 @@ class ARContentNode( val arscene: ARSceneView, val viewAttManager: ViewAttachmentManager, val fragmentManagerProvider: FragmentManagerProvider, - val capsule: CapsuleMarker, + val capsule: CapsuleAnchor, val layoutInflater: LayoutInflater, val context: Context, val onLoaded: (node: ViewNode) -> Unit diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt index 96779ef11..5cc4684cc 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt @@ -4,7 +4,6 @@ import android.Manifest import android.os.Bundle import android.util.Log import android.view.View -import androidx.core.content.ContentProviderCompat.requireContext import androidx.fragment.app.FragmentManager import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle @@ -12,7 +11,7 @@ import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import com.droidblossom.archive.R import com.droidblossom.archive.databinding.FragmentCameraBinding -import com.droidblossom.archive.domain.model.common.CapsuleMarker +import com.droidblossom.archive.domain.model.capsule.CapsuleAnchor import com.droidblossom.archive.presentation.base.BaseFragment import com.droidblossom.archive.presentation.ui.home.dialog.CapsulePreviewDialogFragment import com.droidblossom.archive.util.FragmentManagerProvider @@ -148,7 +147,7 @@ class CameraFragment : } - private fun addAnchorNode(anchor: Anchor, capsule: CapsuleMarker) { + private fun addAnchorNode(anchor: Anchor, capsule: CapsuleAnchor) { Log.d("CameraFragmentAR", "addAnchorNode added") arSceneView.let { sceneView -> viewAttachmentManager.let { attachManager -> diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModel.kt index 9186b6645..005a474e4 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModel.kt @@ -1,19 +1,18 @@ package com.droidblossom.archive.presentation.ui.camera -import com.droidblossom.archive.domain.model.common.CapsuleMarker +import com.droidblossom.archive.domain.model.capsule.CapsuleAnchor import io.github.sceneview.ar.node.AnchorNode -import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow interface CameraViewModel { - val capsuleList:StateFlow> + val capsuleList:StateFlow> val cameraEvents: SharedFlow val capsuleListSize: SharedFlow val anchorNodes: StateFlow> - fun getCapsules(latitude: Double, longitude: Double,): List + fun getCapsules(latitude: Double, longitude: Double,): List fun cameraEvent(event : CameraEvent) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModelImpl.kt index 4c46ee8ac..a14548e96 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModelImpl.kt @@ -1,7 +1,7 @@ package com.droidblossom.archive.presentation.ui.camera import androidx.lifecycle.viewModelScope -import com.droidblossom.archive.domain.model.common.CapsuleMarker +import com.droidblossom.archive.domain.model.capsule.CapsuleAnchor import com.droidblossom.archive.domain.usecase.capsule.NearbyCapsulesARUseCase import com.droidblossom.archive.presentation.base.BaseViewModel import com.droidblossom.archive.util.onFail @@ -26,8 +26,8 @@ class CameraViewModelImpl@Inject constructor( override val cameraEvents: SharedFlow get() = _cameraEvents.asSharedFlow() - private val _capsuleList = MutableStateFlow(listOf()) - override val capsuleList: StateFlow> + private val _capsuleList = MutableStateFlow(listOf()) + override val capsuleList: StateFlow> get() = _capsuleList private val _capsuleListSize = MutableStateFlow(-1) @@ -53,11 +53,11 @@ class CameraViewModelImpl@Inject constructor( } } - override fun getCapsules(latitude: Double, longitude: Double) : List { + override fun getCapsules(latitude: Double, longitude: Double) : List { viewModelScope.launch { nearbyCapsulesARUseCase(latitude,longitude,0.5,"ALL").collect{ result-> result.onSuccess { - _capsuleList.emit(it.capsules) + _capsuleList.emit(it.capsuleAnchors) _capsuleListSize.value = _capsuleList.value.size }.onFail { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt index d5806b279..5ba133ed4 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt @@ -1,27 +1,22 @@ package com.droidblossom.archive.presentation.ui.home -import android.graphics.Point -import android.location.Location import android.os.Bundle -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.TextView import androidx.core.content.ContextCompat -import androidx.core.graphics.toPointF import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import com.droidblossom.archive.R import com.droidblossom.archive.databinding.FragmentHomeBinding -import com.droidblossom.archive.domain.model.common.CapsuleMarker +import com.droidblossom.archive.domain.model.capsule.CapsuleMarker import com.droidblossom.archive.presentation.base.BaseFragment import com.droidblossom.archive.presentation.ui.home.createcapsule.CreateCapsuleActivity import com.droidblossom.archive.presentation.ui.home.dialog.CapsulePreviewDialogFragment import com.droidblossom.archive.presentation.ui.home.notification.NotificationActivity -import com.droidblossom.archive.util.CapsuleTypeUtils import com.droidblossom.archive.util.LocationUtil import com.naver.maps.geometry.LatLng import com.naver.maps.geometry.LatLngBounds @@ -30,9 +25,7 @@ import com.naver.maps.map.LocationTrackingMode import com.naver.maps.map.MapFragment import com.naver.maps.map.NaverMap import com.naver.maps.map.OnMapReadyCallback -import com.naver.maps.map.Projection import com.naver.maps.map.overlay.Marker -import com.naver.maps.map.overlay.Overlay import com.naver.maps.map.overlay.OverlayImage import com.naver.maps.map.util.FusedLocationSource import dagger.hilt.android.AndroidEntryPoint @@ -204,14 +197,14 @@ class HomeFragment : BaseFragment(R.layo .markerClickListener { capsuleMarker -> viewModel.homeEvent( HomeViewModel.HomeEvent.ShowCapsulePreviewDialog( - capsuleMarker.capsuleMarker.id.toString(), - capsuleMarker.capsuleMarker.capsuleType.toString() + capsuleMarker.capsuleAnchor.id.toString(), + capsuleMarker.capsuleAnchor.capsuleType.toString() ) ) }.customMarker { capsuleMarker -> Marker().apply { - icon = when (capsuleMarker.capsuleMarker.capsuleType) { + icon = when (capsuleMarker.capsuleAnchor.capsuleType) { CapsuleType.SECRET -> OverlayImage.fromResource(R.drawable.ic_marker_pin_secret) CapsuleType.GROUP -> OverlayImage.fromResource(R.drawable.ic_marker_pin_group) CapsuleType.PUBLIC -> OverlayImage.fromResource(R.drawable.ic_marker_pin_public) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeViewModel.kt index 228d57ac9..307a0a4b4 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeViewModel.kt @@ -1,7 +1,6 @@ package com.droidblossom.archive.presentation.ui.home -import com.droidblossom.archive.domain.model.common.CapsuleMarker -import com.droidblossom.archive.presentation.ui.auth.AuthViewModel +import com.droidblossom.archive.domain.model.capsule.CapsuleMarker import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeViewModelImpl.kt index 979f50f49..65eca73e5 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeViewModelImpl.kt @@ -2,8 +2,8 @@ package com.droidblossom.archive.presentation.ui.home import android.util.Log import androidx.lifecycle.viewModelScope -import com.droidblossom.archive.domain.model.common.CapsuleMarker -import com.droidblossom.archive.domain.usecase.capsule.NearbyCapsulesARUseCase +import com.droidblossom.archive.domain.model.capsule.CapsuleMarker +import com.droidblossom.archive.domain.usecase.capsule.NearbyCapsulesHomeUseCase import com.droidblossom.archive.presentation.base.BaseViewModel import com.droidblossom.archive.util.onFail import com.droidblossom.archive.util.onSuccess @@ -18,7 +18,7 @@ import javax.inject.Inject @HiltViewModel class HomeViewModelImpl @Inject constructor( - private val nearbyCapsulesARUseCase: NearbyCapsulesARUseCase, + private val nearbyCapsulesHomeUseCase: NearbyCapsulesHomeUseCase ) : BaseViewModel(), HomeViewModel { private val _homeEvents = MutableSharedFlow() @@ -109,10 +109,9 @@ class HomeViewModelImpl @Inject constructor( override fun getNearbyCapsules(latitude: Double, longitude: Double, distance: Double, capsuleType: String) { Log.d("티티","$latitude, $longitude, $distance, $capsuleType") viewModelScope.launch { - nearbyCapsulesARUseCase(latitude,longitude, distance, capsuleType ).collect{ result-> + nearbyCapsulesHomeUseCase(latitude,longitude, distance, capsuleType ).collect{ result-> result.onSuccess { - _capsuleList.emit(it.capsules) - Log.d("티티","getNearbyCapsules 성공") + _capsuleList.emit(it.capsuleMarkers) }.onFail { Log.d("티티","getNearbyCapsules 실패") } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/MapCapsuleMarker.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/MapCapsuleMarker.kt index 9a88e7062..5baf87ab7 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/MapCapsuleMarker.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/MapCapsuleMarker.kt @@ -1,15 +1,14 @@ package com.droidblossom.archive.presentation.ui.home -import com.droidblossom.archive.domain.model.common.CapsuleMarker -import com.naver.maps.geometry.LatLng +import com.droidblossom.archive.domain.model.capsule.CapsuleMarker import ted.gun0912.clustering.clustering.TedClusterItem import ted.gun0912.clustering.geometry.TedLatLng data class MapCapsuleMarker( - val capsuleMarker: CapsuleMarker + val capsuleAnchor: CapsuleMarker ) : TedClusterItem { - override fun getTedLatLng(): TedLatLng = TedLatLng(capsuleMarker.latitude,capsuleMarker.longitude) + override fun getTedLatLng(): TedLatLng = TedLatLng(capsuleAnchor.latitude,capsuleAnchor.longitude) } \ No newline at end of file From 30eb79cc5a8ce528842e5dc4ba9aef50c0f5b141 Mon Sep 17 00:00:00 2001 From: comst19 Date: Thu, 18 Apr 2024 17:43:16 +0900 Subject: [PATCH 191/301] =?UTF-8?q?design:=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20UI=20Update?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ui/mypage/MyPageFragment.kt | 4 +- .../presentation/ui/mypage/MyPageViewModel.kt | 1 + .../ui/mypage/MyPageViewModelImpl.kt | 9 ++ .../ui/mypage/adapter/MyCapsuleRVA.kt | 5 - .../archive/util/BindingAdapter.kt | 21 +++ .../main/res/drawable/ic_add_person_24.xml | 32 ++++ .../res/drawable/ic_setting_24_main_1.xml | 21 +++ .../src/main/res/layout/fragment_my_page.xml | 152 ++++++++++++++++-- 8 files changed, 225 insertions(+), 20 deletions(-) create mode 100644 frontend/ARchive/app/src/main/res/drawable/ic_add_person_24.xml create mode 100644 frontend/ARchive/app/src/main/res/drawable/ic_setting_24_main_1.xml diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt index 7e10d0bc8..a2a7bb782 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt @@ -46,8 +46,6 @@ class MyPageFragment : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding.vm = viewModel - viewModel.getMe() - viewModel.getSecretCapsulePage() parentFragmentManager.setFragmentResultListener("capsuleState", viewLifecycleOwner) { key, bundle -> val capsuleIndex = bundle.getInt("capsuleIndex") @@ -139,7 +137,7 @@ class MyPageFragment : override fun onHiddenChanged(hidden: Boolean) { super.onHiddenChanged(hidden) if (!hidden) { - viewModel.clearCapsules() + viewModel.load() } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModel.kt index cabccefb6..db83a5f66 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModel.kt @@ -21,6 +21,7 @@ interface MyPageViewModel { fun updateCapsuleOpenState(capsuleIndex: Int, capsuleId: Long) fun clickSetting() + fun load() sealed class MyPageEvent { data class ShowToastMessage(val message : String) : MyPageEvent() diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt index ec703e793..cc94edc02 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt @@ -54,6 +54,15 @@ class MyPageViewModelImpl @Inject constructor( override val lastCreatedTime: StateFlow get() = _lastCreatedTime + init { + load() + } + + override fun load(){ + getMe() + clearCapsules() + } + override fun getMe() { viewModelScope.launch { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/MyCapsuleRVA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/MyCapsuleRVA.kt index 376bad1d4..a750ee954 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/MyCapsuleRVA.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/MyCapsuleRVA.kt @@ -1,15 +1,10 @@ package com.droidblossom.archive.presentation.ui.mypage.adapter -import android.graphics.Color import android.view.LayoutInflater import android.view.ViewGroup -import androidx.annotation.ColorRes -import androidx.core.content.res.ResourcesCompat.getColor import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView -import com.amazonaws.util.ClassLoaderHelper.getResource -import com.droidblossom.archive.R import com.droidblossom.archive.databinding.ItemMyCapsuleBinding import com.droidblossom.archive.domain.model.common.MyCapsule import com.droidblossom.archive.presentation.ui.home.HomeFragment diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/BindingAdapter.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/BindingAdapter.kt index 80cde05b7..4b4490831 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/BindingAdapter.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/BindingAdapter.kt @@ -219,4 +219,25 @@ fun TextView.addFriendText(isFriend: Boolean, isRequest : Boolean, name : String }else { this.text = name } +} + +@SuppressLint("SetTextI18n") +@BindingAdapter(value = ["bind:count", "bind:showDecimal"], requireAll = true) +fun TextView.formatCountWithK(count: Int, showDecimal: Boolean) { + + if (count < 1000) { + this.text = count.toString() + }else{ + if (showDecimal) { + val thousands = count / 1000 + val remainder = (count % 1000) / 100 + if (remainder == 0) { + this.text = "${thousands}K" + } else { + this.text = "${thousands}.${remainder}K" + } + } else { + this.text = "${count / 1000}K" + } + } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/drawable/ic_add_person_24.xml b/frontend/ARchive/app/src/main/res/drawable/ic_add_person_24.xml new file mode 100644 index 000000000..494343f06 --- /dev/null +++ b/frontend/ARchive/app/src/main/res/drawable/ic_add_person_24.xml @@ -0,0 +1,32 @@ + + + + + + + + + diff --git a/frontend/ARchive/app/src/main/res/drawable/ic_setting_24_main_1.xml b/frontend/ARchive/app/src/main/res/drawable/ic_setting_24_main_1.xml new file mode 100644 index 000000000..4c6c95000 --- /dev/null +++ b/frontend/ARchive/app/src/main/res/drawable/ic_setting_24_main_1.xml @@ -0,0 +1,21 @@ + + + + + diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_my_page.xml b/frontend/ARchive/app/src/main/res/layout/fragment_my_page.xml index 615d74ae2..69dbb8a7f 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_my_page.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_my_page.xml @@ -15,7 +15,7 @@ + - + android:backgroundTint="@color/main_1" + android:src="@drawable/ic_setting_24_main_1" /> + + + + + + + + + + + + + + + + + + + + + + Date: Fri, 19 Apr 2024 20:13:30 +0900 Subject: [PATCH 192/301] =?UTF-8?q?feat:=20LongClick=20=ED=95=98=EB=A9=B4?= =?UTF-8?q?=20Tag=20=EB=B3=B5=EC=82=AC=EA=B8=B0=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/presentation/base/BaseActivity.kt | 10 ++++++++++ .../archive/presentation/base/BaseFragment.kt | 9 +++++++++ .../presentation/ui/mypage/MyPageFragment.kt | 16 ++++++++++++++++ .../droidblossom/archive/util/ClipboardUtil.kt | 13 +++++++++++++ 4 files changed, 48 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/ClipboardUtil.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseActivity.kt index d2f685eb4..8d30cb9ac 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseActivity.kt @@ -1,7 +1,10 @@ package com.droidblossom.archive.presentation.base +import android.content.ClipData +import android.content.ClipboardManager import android.content.Context import android.graphics.Color +import android.os.Build import android.os.Bundle import android.util.Log import android.view.View @@ -13,6 +16,7 @@ import androidx.databinding.DataBindingUtil import androidx.databinding.ViewDataBinding import com.droidblossom.archive.presentation.customview.HomeSnackBarSmall import com.droidblossom.archive.presentation.customview.LoadingDialog +import com.droidblossom.archive.util.ClipboardUtil import kotlinx.coroutines.Job import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe @@ -97,6 +101,12 @@ abstract class BaseActivity(@LayoutRes v super.onDestroy() } + fun copyText(label:String, text: String) { + ClipboardUtil.copyTextToClipboard(this, label, text) + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2){ + showToastMessage("클립보드에 복사되었어요.") + } + } fun showToastMessage(message: String) { val toast = Toast.makeText(this, message, Toast.LENGTH_SHORT) toast.show() diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseFragment.kt index c84e25b88..2660156fb 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseFragment.kt @@ -1,6 +1,7 @@ package com.droidblossom.archive.presentation.base import android.content.Context +import android.os.Build import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -11,6 +12,7 @@ import androidx.databinding.DataBindingUtil import androidx.databinding.ViewDataBinding import androidx.fragment.app.Fragment import com.droidblossom.archive.presentation.customview.LoadingDialog +import com.droidblossom.archive.util.ClipboardUtil import kotlinx.coroutines.Job abstract class BaseFragment(@LayoutRes val layoutResource :Int): Fragment() { @@ -53,6 +55,13 @@ abstract class BaseFragment(@LayoutRes va observeData() } + fun copyText(label:String, text: String) { + ClipboardUtil.copyTextToClipboard(requireContext(), label, text) + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2){ + showToastMessage("클립보드에 복사되었어요.") + } + } + fun showToastMessage(message: String) { val toast = Toast.makeText(activity, message, Toast.LENGTH_SHORT) toast.show() diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt index a2a7bb782..02e079261 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt @@ -1,8 +1,12 @@ package com.droidblossom.archive.presentation.ui.mypage +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context.CLIPBOARD_SERVICE import android.os.Bundle import android.view.View import android.view.ViewGroup +import androidx.core.content.ContextCompat.getSystemService import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope @@ -20,6 +24,7 @@ import com.droidblossom.archive.presentation.ui.mypage.setting.SettingActivity import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch + @AndroidEntryPoint class MyPageFragment : BaseFragment(R.layout.fragment_my_page) { @@ -58,6 +63,7 @@ class MyPageFragment : } initRVA() + initView() // binding.settingBtn.setOnClickListener { // throw RuntimeException("Test Crash") @@ -73,6 +79,16 @@ class MyPageFragment : } } + private fun initView(){ + + with(binding){ + profileTagT.setOnLongClickListener { + copyText("userTag", viewModel.myInfo.value.tag) + true + } + } + } + private fun initRVA() { binding.capsuleRecycleView.adapter = myCapsuleRVA diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/ClipboardUtil.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/ClipboardUtil.kt new file mode 100644 index 000000000..21c492a19 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/ClipboardUtil.kt @@ -0,0 +1,13 @@ +package com.droidblossom.archive.util + +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context + +object ClipboardUtil { + fun copyTextToClipboard(context: Context, label:String, text: String) { + val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager + val clip = ClipData.newPlainText(label, text) + clipboard?.setPrimaryClip(clip) + } +} \ No newline at end of file From 5c543ca089e090d63a15671ea38ade503d6ce522 Mon Sep 17 00:00:00 2001 From: comst19 Date: Fri, 19 Apr 2024 20:20:53 +0900 Subject: [PATCH 193/301] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC,=20=EA=B7=B8?= =?UTF-8?q?=EB=A3=B9=20Layout=EC=97=90=20ClickEvent=EB=A1=9C=20FriendActiv?= =?UTF-8?q?ity=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/presentation/ui/mypage/MyPageFragment.kt | 12 +++++------- .../presentation/ui/mypage/friend/FriendActivity.kt | 9 ++++++--- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt index 02e079261..fe46f163a 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt @@ -1,12 +1,8 @@ package com.droidblossom.archive.presentation.ui.mypage -import android.content.ClipData -import android.content.ClipboardManager -import android.content.Context.CLIPBOARD_SERVICE import android.os.Bundle import android.view.View import android.view.ViewGroup -import androidx.core.content.ContextCompat.getSystemService import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope @@ -73,9 +69,11 @@ class MyPageFragment : layoutParams.topMargin += getStatusBarHeight() binding.profileImg.layoutParams = layoutParams - //임시 : 친구 엑티비티로 이동 - binding.profileImg.setOnClickListener{ - startActivity(FriendActivity.newIntent(requireContext())) + binding.groupLayout.setOnClickListener{ + startActivity(FriendActivity.newIntent(requireContext(), FriendActivity.GROUP)) + } + binding.friendLayout.setOnClickListener{ + startActivity(FriendActivity.newIntent(requireContext(), FriendActivity.FRIEND)) } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendActivity.kt index 76b1b0323..c4be4f59f 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendActivity.kt @@ -76,8 +76,11 @@ class FriendActivity : companion object { const val FRIEND = "friend" - - fun newIntent(context: Context) = - Intent(context, FriendActivity::class.java) + const val GROUP = "group" + const val TYPE_KEY = "type_key" + fun newIntent(context: Context, type: String) = + Intent(context, FriendActivity::class.java).apply { + putExtra(TYPE_KEY, type) + } } } \ No newline at end of file From dd5020f73ef40245ffdb0c2c2fadb2c0c5df8e3f Mon Sep 17 00:00:00 2001 From: comst19 Date: Sun, 21 Apr 2024 18:44:07 +0900 Subject: [PATCH 194/301] =?UTF-8?q?design:=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20UI=20Update=20-=20history?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/res/drawable/ic_add_circle.xml | 21 +++++++++++ .../drawable/my_page_history_background.xml | 12 +++++++ .../src/main/res/layout/fragment_my_page.xml | 36 ++++++++++++++----- .../app/src/main/res/layout/item_story.xml | 30 ++++++++-------- 4 files changed, 75 insertions(+), 24 deletions(-) create mode 100644 frontend/ARchive/app/src/main/res/drawable/ic_add_circle.xml create mode 100644 frontend/ARchive/app/src/main/res/drawable/my_page_history_background.xml diff --git a/frontend/ARchive/app/src/main/res/drawable/ic_add_circle.xml b/frontend/ARchive/app/src/main/res/drawable/ic_add_circle.xml new file mode 100644 index 000000000..e2ef5e871 --- /dev/null +++ b/frontend/ARchive/app/src/main/res/drawable/ic_add_circle.xml @@ -0,0 +1,21 @@ + + + + + diff --git a/frontend/ARchive/app/src/main/res/drawable/my_page_history_background.xml b/frontend/ARchive/app/src/main/res/drawable/my_page_history_background.xml new file mode 100644 index 000000000..f2235dfde --- /dev/null +++ b/frontend/ARchive/app/src/main/res/drawable/my_page_history_background.xml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_my_page.xml b/frontend/ARchive/app/src/main/res/layout/fragment_my_page.xml index 69dbb8a7f..226e9eca3 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_my_page.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_my_page.xml @@ -119,10 +119,10 @@ android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_marginVertical="5dp" - android:minWidth="36dp" android:background="@drawable/corner_radius_30" android:gravity="center" android:maxLines="1" + android:minWidth="36dp" android:padding="@dimen/padding_small" android:textAppearance="@style/TextAppearance.App.caption2" android:textColor="@color/black" @@ -165,10 +165,10 @@ android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_marginVertical="5dp" - android:minWidth="36dp" android:background="@drawable/corner_radius_30" android:gravity="center" android:maxLines="1" + android:minWidth="36dp" android:padding="@dimen/padding_small" android:textAppearance="@style/TextAppearance.App.caption2" android:textColor="@color/black" @@ -205,17 +205,35 @@ - + app:layout_constraintTop_toBottomOf="@id/groupLayout"> + + + + + + + app:layout_constraintTop_toBottomOf="@id/storyLayout" /> + android:src="@drawable/ic_plus_main_40" /> - + + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/cardView" + android:layout_marginTop="8dp"/> \ No newline at end of file From 1dc959548453e717679783a9790b0a9acb420e26 Mon Sep 17 00:00:00 2001 From: comst19 Date: Tue, 23 Apr 2024 21:55:39 +0900 Subject: [PATCH 195/301] =?UTF-8?q?design:=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20UI=20Update=20-=20Spinner?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ui/mypage/MyPageFragment.kt | 53 ++++++++++++++--- .../ui/mypage/adapter/CapsuleTypeSpinner.kt | 58 +++++++++++++++++++ .../main/res/drawable/spinner_background.xml | 23 ++++++++ .../src/main/res/layout/fragment_my_page.xml | 36 +++++------- .../src/main/res/layout/item_capsule_type.xml | 17 ++++++ .../app/src/main/res/layout/item_spinner.xml | 23 ++++++++ .../main/res/layout/item_spinner_dropdown.xml | 33 +++++++++++ .../ARchive/app/src/main/res/values/dimen.xml | 3 + .../app/src/main/res/values/strings.xml | 6 ++ 9 files changed, 222 insertions(+), 30 deletions(-) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/CapsuleTypeSpinner.kt create mode 100644 frontend/ARchive/app/src/main/res/drawable/spinner_background.xml create mode 100644 frontend/ARchive/app/src/main/res/layout/item_capsule_type.xml create mode 100644 frontend/ARchive/app/src/main/res/layout/item_spinner.xml create mode 100644 frontend/ARchive/app/src/main/res/layout/item_spinner_dropdown.xml diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt index fe46f163a..64e6f7715 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt @@ -3,6 +3,7 @@ package com.droidblossom.archive.presentation.ui.mypage import android.os.Bundle import android.view.View import android.view.ViewGroup +import android.widget.AdapterView import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope @@ -14,6 +15,7 @@ import com.droidblossom.archive.databinding.FragmentMyPageBinding import com.droidblossom.archive.presentation.base.BaseFragment import com.droidblossom.archive.presentation.ui.capsule.CapsuleDetailActivity import com.droidblossom.archive.presentation.ui.home.dialog.CapsulePreviewDialogFragment +import com.droidblossom.archive.presentation.ui.mypage.adapter.CapsuleTypeSpinner import com.droidblossom.archive.presentation.ui.mypage.adapter.MyCapsuleRVA import com.droidblossom.archive.presentation.ui.mypage.friend.FriendActivity import com.droidblossom.archive.presentation.ui.mypage.setting.SettingActivity @@ -38,7 +40,12 @@ class MyPageFragment : ) }, { capsuleIndex, id, type -> - val sheet = CapsulePreviewDialogFragment.newInstance(capsuleIndex.toString(), id.toString(), type.toString(), false) + val sheet = CapsulePreviewDialogFragment.newInstance( + capsuleIndex.toString(), + id.toString(), + type.toString(), + false + ) sheet.show(parentFragmentManager, "CapsulePreviewDialog") }, ) @@ -48,12 +55,15 @@ class MyPageFragment : super.onViewCreated(view, savedInstanceState) binding.vm = viewModel - parentFragmentManager.setFragmentResultListener("capsuleState", viewLifecycleOwner) { key, bundle -> + parentFragmentManager.setFragmentResultListener( + "capsuleState", + viewLifecycleOwner + ) { key, bundle -> val capsuleIndex = bundle.getInt("capsuleIndex") val capsuleId = bundle.getLong("capsuleId") val capsuleOpenState = bundle.getBoolean("isOpened") if (capsuleIndex != -1 && capsuleOpenState) { - viewModel.updateCapsuleOpenState(capsuleIndex,capsuleId) + viewModel.updateCapsuleOpenState(capsuleIndex, capsuleId) myCapsuleRVA.notifyItemChanged(capsuleIndex) } } @@ -69,21 +79,41 @@ class MyPageFragment : layoutParams.topMargin += getStatusBarHeight() binding.profileImg.layoutParams = layoutParams - binding.groupLayout.setOnClickListener{ + binding.groupLayout.setOnClickListener { startActivity(FriendActivity.newIntent(requireContext(), FriendActivity.GROUP)) } - binding.friendLayout.setOnClickListener{ + binding.friendLayout.setOnClickListener { startActivity(FriendActivity.newIntent(requireContext(), FriendActivity.FRIEND)) } } - private fun initView(){ + private fun initView() { - with(binding){ + with(binding) { profileTagT.setOnLongClickListener { copyText("userTag", viewModel.myInfo.value.tag) true } + + val data = arrayOf("Secret", "Public", "Group") + val adapter = CapsuleTypeSpinner(requireContext(), data) + capsuleTypeSpinner.adapter = adapter + capsuleTypeSpinner.onItemSelectedListener = + object : AdapterView.OnItemSelectedListener { + override fun onItemSelected( + parent: AdapterView<*>, + view: View, + position: Int, + id: Long + ) { + + } + + override fun onNothingSelected(parent: AdapterView<*>?) { + + } + } + } } @@ -113,7 +143,7 @@ class MyPageFragment : viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.myCapsules.collect { capsule -> - if (capsule.isNotEmpty()){ + if (capsule.isNotEmpty()) { viewModel.updateMyCapsulesUI() } } @@ -133,6 +163,7 @@ class MyPageFragment : is MyPageViewModel.MyPageEvent.ShowToastMessage -> { showToastMessage(event.message) } + is MyPageViewModel.MyPageEvent.ClickSetting -> { startActivity(SettingActivity.newIntent(requireContext())) } @@ -160,4 +191,10 @@ class MyPageFragment : const val TAG = "MY" fun newIntent() = MyPageFragment() } + + enum class SpinnerCapsuleType(val description: String) { + SECRET("Secret"), + PUBLIC("Public"), + GROUP("Group") + } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/CapsuleTypeSpinner.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/CapsuleTypeSpinner.kt new file mode 100644 index 000000000..7545ea15c --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/CapsuleTypeSpinner.kt @@ -0,0 +1,58 @@ +package com.droidblossom.archive.presentation.ui.mypage.adapter + +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.Color +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.BaseAdapter +import androidx.databinding.DataBindingUtil +import com.droidblossom.archive.R +import com.droidblossom.archive.databinding.ItemSpinnerBinding +import com.droidblossom.archive.databinding.ItemSpinnerDropdownBinding + +class CapsuleTypeSpinner(private val context: Context, private val items: Array) : BaseAdapter() { + + private lateinit var spinnerItemBinding: ItemSpinnerBinding + + + private lateinit var spinnerDropdownItemBinding: ItemSpinnerDropdownBinding + + override fun getCount(): Int { + return items.size + } + + override fun getItem(position: Int): String { + return items[position] + } + + override fun getItemId(position: Int): Long { + return position.toLong() + } + + @SuppressLint("ViewHolder") + override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { + spinnerItemBinding = DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.item_spinner, parent, false) + spinnerItemBinding.tvItemName.text = items[position] + + return spinnerItemBinding.root + } + + override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View { + spinnerDropdownItemBinding = DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.item_spinner_dropdown, parent, false) + spinnerDropdownItemBinding.tvDropdownItemName.text = items[position] + + if (items[position] == spinnerItemBinding.tvItemName.text.toString()) { + spinnerDropdownItemBinding.tvDropdownItemName.setTextColor(Color.parseColor("#ED7A2B")) + } else { + spinnerDropdownItemBinding.tvDropdownItemName.setTextColor(Color.parseColor("#000000")) + } + + if (position == (count - 1)) { + spinnerDropdownItemBinding.tvDivider.visibility = View.GONE + } + + return spinnerDropdownItemBinding.root + } +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/drawable/spinner_background.xml b/frontend/ARchive/app/src/main/res/drawable/spinner_background.xml new file mode 100644 index 000000000..22140ee8e --- /dev/null +++ b/frontend/ARchive/app/src/main/res/drawable/spinner_background.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_my_page.xml b/frontend/ARchive/app/src/main/res/layout/fragment_my_page.xml index 226e9eca3..9568212fb 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_my_page.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_my_page.xml @@ -209,11 +209,11 @@ android:id="@+id/storyLayout" android:layout_width="0dp" android:layout_height="wrap_content" - android:paddingVertical="@dimen/padding_small" android:layout_marginStart="16dp" - android:paddingStart="20dp" android:layout_marginTop="24dp" android:background="@drawable/my_page_history_background" + android:paddingVertical="@dimen/padding_small" + android:paddingStart="20dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/groupLayout"> @@ -223,39 +223,31 @@ android:id="@+id/storyRecycleView" android:layout_width="wrap_content" android:layout_height="wrap_content" - tools:itemCount="6" android:background="@drawable/my_page_history_background" android:orientation="horizontal" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" + tools:itemCount="6" tools:listitem="@layout/item_story" /> - - - diff --git a/frontend/ARchive/app/src/main/res/layout/item_capsule_type.xml b/frontend/ARchive/app/src/main/res/layout/item_capsule_type.xml new file mode 100644 index 000000000..046666470 --- /dev/null +++ b/frontend/ARchive/app/src/main/res/layout/item_capsule_type.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/item_spinner.xml b/frontend/ARchive/app/src/main/res/layout/item_spinner.xml new file mode 100644 index 000000000..d531a200b --- /dev/null +++ b/frontend/ARchive/app/src/main/res/layout/item_spinner.xml @@ -0,0 +1,23 @@ + + + + + + + + \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/item_spinner_dropdown.xml b/frontend/ARchive/app/src/main/res/layout/item_spinner_dropdown.xml new file mode 100644 index 000000000..2325a97ab --- /dev/null +++ b/frontend/ARchive/app/src/main/res/layout/item_spinner_dropdown.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/values/dimen.xml b/frontend/ARchive/app/src/main/res/values/dimen.xml index a236ba5bb..c1d0fcb57 100644 --- a/frontend/ARchive/app/src/main/res/values/dimen.xml +++ b/frontend/ARchive/app/src/main/res/values/dimen.xml @@ -24,4 +24,7 @@ 104dp 60dp + + 300dp + 50dp \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/values/strings.xml b/frontend/ARchive/app/src/main/res/values/strings.xml index 823970636..f8d0da4bf 100644 --- a/frontend/ARchive/app/src/main/res/values/strings.xml +++ b/frontend/ARchive/app/src/main/res/values/strings.xml @@ -50,4 +50,10 @@ 그룹 요청 친구 요청 + + + Secret + Public + Group + \ No newline at end of file From bb2689b6b5fac924c1eed13d0ffe78aea128912f Mon Sep 17 00:00:00 2001 From: comst19 Date: Tue, 23 Apr 2024 22:00:53 +0900 Subject: [PATCH 196/301] =?UTF-8?q?design:=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20UI=20Update=20-=20Spinner(1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/ARchive/app/src/main/res/layout/fragment_my_page.xml | 2 +- frontend/ARchive/app/src/main/res/layout/item_spinner.xml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_my_page.xml b/frontend/ARchive/app/src/main/res/layout/fragment_my_page.xml index 9568212fb..d222f1576 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_my_page.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_my_page.xml @@ -240,7 +240,7 @@ android:layout_height="@dimen/common_spinner_height" android:layout_marginHorizontal="@dimen/margin" android:layout_marginTop="24dp" - android:background="@drawable/spinner_background" + android:background="@null" android:dropDownWidth="match_parent" android:dropDownVerticalOffset="@dimen/common_spinner_height" android:spinnerMode="dropdown" diff --git a/frontend/ARchive/app/src/main/res/layout/item_spinner.xml b/frontend/ARchive/app/src/main/res/layout/item_spinner.xml index d531a200b..9c626280b 100644 --- a/frontend/ARchive/app/src/main/res/layout/item_spinner.xml +++ b/frontend/ARchive/app/src/main/res/layout/item_spinner.xml @@ -11,6 +11,7 @@ android:layout_width="match_parent" android:layout_height="@dimen/common_spinner_height" android:ellipsize="marquee" + android:background="@drawable/spinner_background" android:gravity="center_vertical" android:paddingStart="@dimen/padding" android:paddingEnd="@dimen/padding" From 4247068dc9eb79e952986888e2d0d4c6c99671ed Mon Sep 17 00:00:00 2001 From: comst19 Date: Wed, 24 Apr 2024 11:00:26 +0900 Subject: [PATCH 197/301] =?UTF-8?q?design:=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20UI=20Update=20-=20Spinner(2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/ARchiveApplication.kt | 2 +- .../presentation/ui/mypage/MyPageFragment.kt | 56 ++++++++++++------- .../ui/mypage/MyPageViewModelImpl.kt | 2 + .../ui/mypage/adapter/CapsuleTypeSpinner.kt | 28 +++++++--- .../src/main/res/drawable/ic_arrow_up_24.xml | 13 +++++ ...round.xml => spinner_background_close.xml} | 0 .../res/drawable/spinner_background_open.xml | 25 +++++++++ .../drawable/spinner_dropdown_item_last.xml | 8 +++ .../src/main/res/layout/fragment_my_page.xml | 1 + .../app/src/main/res/layout/item_spinner.xml | 4 +- .../main/res/layout/item_spinner_dropdown.xml | 4 +- 11 files changed, 111 insertions(+), 32 deletions(-) create mode 100644 frontend/ARchive/app/src/main/res/drawable/ic_arrow_up_24.xml rename frontend/ARchive/app/src/main/res/drawable/{spinner_background.xml => spinner_background_close.xml} (100%) create mode 100644 frontend/ARchive/app/src/main/res/drawable/spinner_background_open.xml create mode 100644 frontend/ARchive/app/src/main/res/drawable/spinner_dropdown_item_last.xml diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/ARchiveApplication.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/ARchiveApplication.kt index 2a313966c..9b18a81d0 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/ARchiveApplication.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/ARchiveApplication.kt @@ -28,7 +28,7 @@ class ARchiveApplication : Application(), DefaultLifecycleObserver { networkConnectionChecker = NetworkStatusChecker(context) KakaoSdk.init(this, BuildConfig.KAKAO_NATIVE_APP_KEY) //setCrashHandler() - Thread.setDefaultUncaughtExceptionHandler { _, _ -> caughtException() } + //Thread.setDefaultUncaughtExceptionHandler { _, _ -> caughtException() } dummyCoroutines() // 키 값 알아내기 diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt index 64e6f7715..877df2ffe 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt @@ -1,9 +1,12 @@ package com.droidblossom.archive.presentation.ui.mypage import android.os.Bundle +import android.util.Log +import android.view.MotionEvent import android.view.View import android.view.ViewGroup import android.widget.AdapterView +import androidx.core.content.ContextCompat import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope @@ -51,6 +54,11 @@ class MyPageFragment : ) } + private val spinnerAdapter by lazy { + val capsuleTypeList = arrayOf("Secret", "Public", "Group") + CapsuleTypeSpinner(requireContext(), capsuleTypeList) + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding.vm = viewModel @@ -70,6 +78,7 @@ class MyPageFragment : initRVA() initView() + initSpinner() // binding.settingBtn.setOnClickListener { // throw RuntimeException("Test Crash") @@ -94,26 +103,6 @@ class MyPageFragment : copyText("userTag", viewModel.myInfo.value.tag) true } - - val data = arrayOf("Secret", "Public", "Group") - val adapter = CapsuleTypeSpinner(requireContext(), data) - capsuleTypeSpinner.adapter = adapter - capsuleTypeSpinner.onItemSelectedListener = - object : AdapterView.OnItemSelectedListener { - override fun onItemSelected( - parent: AdapterView<*>, - view: View, - position: Int, - id: Long - ) { - - } - - override fun onNothingSelected(parent: AdapterView<*>?) { - - } - } - } } @@ -139,6 +128,32 @@ class MyPageFragment : }) } + private fun initSpinner() { + with(binding) { + capsuleTypeSpinner.adapter = spinnerAdapter + capsuleTypeSpinner.onItemSelectedListener = + object : AdapterView.OnItemSelectedListener { + override fun onItemSelected( + parent: AdapterView<*>, + view: View, + position: Int, + id: Long + ) { + + } + + override fun onNothingSelected(parent: AdapterView<*>?) { + + } + } + capsuleTypeSpinner.viewTreeObserver.addOnWindowFocusChangeListener { hasFocus -> + spinnerAdapter.spinnerIsOpened = hasFocus + spinnerAdapter.notifyDataSetChanged() + } + } + + } + override fun observeData() { viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { @@ -177,6 +192,7 @@ class MyPageFragment : } } } + } override fun onHiddenChanged(hidden: Boolean) { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt index cc94edc02..38bc5fd46 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt @@ -54,6 +54,7 @@ class MyPageViewModelImpl @Inject constructor( override val lastCreatedTime: StateFlow get() = _lastCreatedTime + init { load() } @@ -127,4 +128,5 @@ class MyPageViewModelImpl @Inject constructor( _myPageEvents.emit(MyPageViewModel.MyPageEvent.ClickSetting) } } + } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/CapsuleTypeSpinner.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/CapsuleTypeSpinner.kt index 7545ea15c..627876222 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/CapsuleTypeSpinner.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/CapsuleTypeSpinner.kt @@ -2,11 +2,17 @@ package com.droidblossom.archive.presentation.ui.mypage.adapter import android.annotation.SuppressLint import android.content.Context +import android.content.res.ColorStateList import android.graphics.Color +import android.graphics.Outline +import android.graphics.drawable.GradientDrawable +import android.os.Build import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.view.ViewOutlineProvider import android.widget.BaseAdapter +import androidx.core.content.ContextCompat import androidx.databinding.DataBindingUtil import com.droidblossom.archive.R import com.droidblossom.archive.databinding.ItemSpinnerBinding @@ -14,9 +20,9 @@ import com.droidblossom.archive.databinding.ItemSpinnerDropdownBinding class CapsuleTypeSpinner(private val context: Context, private val items: Array) : BaseAdapter() { + var spinnerIsOpened = false private lateinit var spinnerItemBinding: ItemSpinnerBinding - private lateinit var spinnerDropdownItemBinding: ItemSpinnerDropdownBinding override fun getCount(): Int { @@ -34,23 +40,31 @@ class CapsuleTypeSpinner(private val context: Context, private val items: Array< @SuppressLint("ViewHolder") override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { spinnerItemBinding = DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.item_spinner, parent, false) - spinnerItemBinding.tvItemName.text = items[position] + spinnerItemBinding.spinnerItemName.text = items[position] return spinnerItemBinding.root } override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View { spinnerDropdownItemBinding = DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.item_spinner_dropdown, parent, false) - spinnerDropdownItemBinding.tvDropdownItemName.text = items[position] + spinnerDropdownItemBinding.dropdownItemName.text = items[position] - if (items[position] == spinnerItemBinding.tvItemName.text.toString()) { - spinnerDropdownItemBinding.tvDropdownItemName.setTextColor(Color.parseColor("#ED7A2B")) + if (items[position] == spinnerItemBinding.spinnerItemName.text.toString()) { + spinnerDropdownItemBinding.dropdownItemName.setTextColor(Color.WHITE) } else { - spinnerDropdownItemBinding.tvDropdownItemName.setTextColor(Color.parseColor("#000000")) + spinnerDropdownItemBinding.dropdownItemName.setTextColor(ContextCompat.getColor(context, R.color.gray_300)) } if (position == (count - 1)) { - spinnerDropdownItemBinding.tvDivider.visibility = View.GONE + spinnerDropdownItemBinding.divider.visibility = View.GONE + spinnerDropdownItemBinding.root.setBackgroundResource(R.drawable.spinner_dropdown_item_last) + + } + + if (spinnerIsOpened){ + spinnerItemBinding.spinnerItemName.setBackgroundResource(R.drawable.spinner_background_close) + } else{ + spinnerItemBinding.spinnerItemName.setBackgroundResource(R.drawable.spinner_background_open) } return spinnerDropdownItemBinding.root diff --git a/frontend/ARchive/app/src/main/res/drawable/ic_arrow_up_24.xml b/frontend/ARchive/app/src/main/res/drawable/ic_arrow_up_24.xml new file mode 100644 index 000000000..7c85f9a5b --- /dev/null +++ b/frontend/ARchive/app/src/main/res/drawable/ic_arrow_up_24.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/drawable/spinner_background.xml b/frontend/ARchive/app/src/main/res/drawable/spinner_background_close.xml similarity index 100% rename from frontend/ARchive/app/src/main/res/drawable/spinner_background.xml rename to frontend/ARchive/app/src/main/res/drawable/spinner_background_close.xml diff --git a/frontend/ARchive/app/src/main/res/drawable/spinner_background_open.xml b/frontend/ARchive/app/src/main/res/drawable/spinner_background_open.xml new file mode 100644 index 000000000..7d38881e4 --- /dev/null +++ b/frontend/ARchive/app/src/main/res/drawable/spinner_background_open.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/drawable/spinner_dropdown_item_last.xml b/frontend/ARchive/app/src/main/res/drawable/spinner_dropdown_item_last.xml new file mode 100644 index 000000000..da099ce46 --- /dev/null +++ b/frontend/ARchive/app/src/main/res/drawable/spinner_dropdown_item_last.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_my_page.xml b/frontend/ARchive/app/src/main/res/layout/fragment_my_page.xml index d222f1576..9228b9a20 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_my_page.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_my_page.xml @@ -241,6 +241,7 @@ android:layout_marginHorizontal="@dimen/margin" android:layout_marginTop="24dp" android:background="@null" + android:popupBackground="@null" android:dropDownWidth="match_parent" android:dropDownVerticalOffset="@dimen/common_spinner_height" android:spinnerMode="dropdown" diff --git a/frontend/ARchive/app/src/main/res/layout/item_spinner.xml b/frontend/ARchive/app/src/main/res/layout/item_spinner.xml index 9c626280b..e84abcc6f 100644 --- a/frontend/ARchive/app/src/main/res/layout/item_spinner.xml +++ b/frontend/ARchive/app/src/main/res/layout/item_spinner.xml @@ -7,11 +7,11 @@ android:orientation="vertical"> Date: Wed, 24 Apr 2024 11:00:57 +0900 Subject: [PATCH 198/301] =?UTF-8?q?Revert=20"design:=20=EB=A7=88=EC=9D=B4?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20UI=20Update=20-=20Spinner(2)"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 4247068dc9eb79e952986888e2d0d4c6c99671ed. --- .../archive/ARchiveApplication.kt | 2 +- .../presentation/ui/mypage/MyPageFragment.kt | 56 +++++++------------ .../ui/mypage/MyPageViewModelImpl.kt | 2 - .../ui/mypage/adapter/CapsuleTypeSpinner.kt | 28 +++------- .../src/main/res/drawable/ic_arrow_up_24.xml | 13 ----- ...round_close.xml => spinner_background.xml} | 0 .../res/drawable/spinner_background_open.xml | 25 --------- .../drawable/spinner_dropdown_item_last.xml | 8 --- .../src/main/res/layout/fragment_my_page.xml | 1 - .../app/src/main/res/layout/item_spinner.xml | 4 +- .../main/res/layout/item_spinner_dropdown.xml | 4 +- 11 files changed, 32 insertions(+), 111 deletions(-) delete mode 100644 frontend/ARchive/app/src/main/res/drawable/ic_arrow_up_24.xml rename frontend/ARchive/app/src/main/res/drawable/{spinner_background_close.xml => spinner_background.xml} (100%) delete mode 100644 frontend/ARchive/app/src/main/res/drawable/spinner_background_open.xml delete mode 100644 frontend/ARchive/app/src/main/res/drawable/spinner_dropdown_item_last.xml diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/ARchiveApplication.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/ARchiveApplication.kt index 9b18a81d0..2a313966c 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/ARchiveApplication.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/ARchiveApplication.kt @@ -28,7 +28,7 @@ class ARchiveApplication : Application(), DefaultLifecycleObserver { networkConnectionChecker = NetworkStatusChecker(context) KakaoSdk.init(this, BuildConfig.KAKAO_NATIVE_APP_KEY) //setCrashHandler() - //Thread.setDefaultUncaughtExceptionHandler { _, _ -> caughtException() } + Thread.setDefaultUncaughtExceptionHandler { _, _ -> caughtException() } dummyCoroutines() // 키 값 알아내기 diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt index 877df2ffe..64e6f7715 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt @@ -1,12 +1,9 @@ package com.droidblossom.archive.presentation.ui.mypage import android.os.Bundle -import android.util.Log -import android.view.MotionEvent import android.view.View import android.view.ViewGroup import android.widget.AdapterView -import androidx.core.content.ContextCompat import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope @@ -54,11 +51,6 @@ class MyPageFragment : ) } - private val spinnerAdapter by lazy { - val capsuleTypeList = arrayOf("Secret", "Public", "Group") - CapsuleTypeSpinner(requireContext(), capsuleTypeList) - } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding.vm = viewModel @@ -78,7 +70,6 @@ class MyPageFragment : initRVA() initView() - initSpinner() // binding.settingBtn.setOnClickListener { // throw RuntimeException("Test Crash") @@ -103,6 +94,26 @@ class MyPageFragment : copyText("userTag", viewModel.myInfo.value.tag) true } + + val data = arrayOf("Secret", "Public", "Group") + val adapter = CapsuleTypeSpinner(requireContext(), data) + capsuleTypeSpinner.adapter = adapter + capsuleTypeSpinner.onItemSelectedListener = + object : AdapterView.OnItemSelectedListener { + override fun onItemSelected( + parent: AdapterView<*>, + view: View, + position: Int, + id: Long + ) { + + } + + override fun onNothingSelected(parent: AdapterView<*>?) { + + } + } + } } @@ -128,32 +139,6 @@ class MyPageFragment : }) } - private fun initSpinner() { - with(binding) { - capsuleTypeSpinner.adapter = spinnerAdapter - capsuleTypeSpinner.onItemSelectedListener = - object : AdapterView.OnItemSelectedListener { - override fun onItemSelected( - parent: AdapterView<*>, - view: View, - position: Int, - id: Long - ) { - - } - - override fun onNothingSelected(parent: AdapterView<*>?) { - - } - } - capsuleTypeSpinner.viewTreeObserver.addOnWindowFocusChangeListener { hasFocus -> - spinnerAdapter.spinnerIsOpened = hasFocus - spinnerAdapter.notifyDataSetChanged() - } - } - - } - override fun observeData() { viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { @@ -192,7 +177,6 @@ class MyPageFragment : } } } - } override fun onHiddenChanged(hidden: Boolean) { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt index 38bc5fd46..cc94edc02 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt @@ -54,7 +54,6 @@ class MyPageViewModelImpl @Inject constructor( override val lastCreatedTime: StateFlow get() = _lastCreatedTime - init { load() } @@ -128,5 +127,4 @@ class MyPageViewModelImpl @Inject constructor( _myPageEvents.emit(MyPageViewModel.MyPageEvent.ClickSetting) } } - } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/CapsuleTypeSpinner.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/CapsuleTypeSpinner.kt index 627876222..7545ea15c 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/CapsuleTypeSpinner.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/CapsuleTypeSpinner.kt @@ -2,17 +2,11 @@ package com.droidblossom.archive.presentation.ui.mypage.adapter import android.annotation.SuppressLint import android.content.Context -import android.content.res.ColorStateList import android.graphics.Color -import android.graphics.Outline -import android.graphics.drawable.GradientDrawable -import android.os.Build import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.view.ViewOutlineProvider import android.widget.BaseAdapter -import androidx.core.content.ContextCompat import androidx.databinding.DataBindingUtil import com.droidblossom.archive.R import com.droidblossom.archive.databinding.ItemSpinnerBinding @@ -20,9 +14,9 @@ import com.droidblossom.archive.databinding.ItemSpinnerDropdownBinding class CapsuleTypeSpinner(private val context: Context, private val items: Array) : BaseAdapter() { - var spinnerIsOpened = false private lateinit var spinnerItemBinding: ItemSpinnerBinding + private lateinit var spinnerDropdownItemBinding: ItemSpinnerDropdownBinding override fun getCount(): Int { @@ -40,31 +34,23 @@ class CapsuleTypeSpinner(private val context: Context, private val items: Array< @SuppressLint("ViewHolder") override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { spinnerItemBinding = DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.item_spinner, parent, false) - spinnerItemBinding.spinnerItemName.text = items[position] + spinnerItemBinding.tvItemName.text = items[position] return spinnerItemBinding.root } override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View { spinnerDropdownItemBinding = DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.item_spinner_dropdown, parent, false) - spinnerDropdownItemBinding.dropdownItemName.text = items[position] + spinnerDropdownItemBinding.tvDropdownItemName.text = items[position] - if (items[position] == spinnerItemBinding.spinnerItemName.text.toString()) { - spinnerDropdownItemBinding.dropdownItemName.setTextColor(Color.WHITE) + if (items[position] == spinnerItemBinding.tvItemName.text.toString()) { + spinnerDropdownItemBinding.tvDropdownItemName.setTextColor(Color.parseColor("#ED7A2B")) } else { - spinnerDropdownItemBinding.dropdownItemName.setTextColor(ContextCompat.getColor(context, R.color.gray_300)) + spinnerDropdownItemBinding.tvDropdownItemName.setTextColor(Color.parseColor("#000000")) } if (position == (count - 1)) { - spinnerDropdownItemBinding.divider.visibility = View.GONE - spinnerDropdownItemBinding.root.setBackgroundResource(R.drawable.spinner_dropdown_item_last) - - } - - if (spinnerIsOpened){ - spinnerItemBinding.spinnerItemName.setBackgroundResource(R.drawable.spinner_background_close) - } else{ - spinnerItemBinding.spinnerItemName.setBackgroundResource(R.drawable.spinner_background_open) + spinnerDropdownItemBinding.tvDivider.visibility = View.GONE } return spinnerDropdownItemBinding.root diff --git a/frontend/ARchive/app/src/main/res/drawable/ic_arrow_up_24.xml b/frontend/ARchive/app/src/main/res/drawable/ic_arrow_up_24.xml deleted file mode 100644 index 7c85f9a5b..000000000 --- a/frontend/ARchive/app/src/main/res/drawable/ic_arrow_up_24.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/drawable/spinner_background_close.xml b/frontend/ARchive/app/src/main/res/drawable/spinner_background.xml similarity index 100% rename from frontend/ARchive/app/src/main/res/drawable/spinner_background_close.xml rename to frontend/ARchive/app/src/main/res/drawable/spinner_background.xml diff --git a/frontend/ARchive/app/src/main/res/drawable/spinner_background_open.xml b/frontend/ARchive/app/src/main/res/drawable/spinner_background_open.xml deleted file mode 100644 index 7d38881e4..000000000 --- a/frontend/ARchive/app/src/main/res/drawable/spinner_background_open.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/drawable/spinner_dropdown_item_last.xml b/frontend/ARchive/app/src/main/res/drawable/spinner_dropdown_item_last.xml deleted file mode 100644 index da099ce46..000000000 --- a/frontend/ARchive/app/src/main/res/drawable/spinner_dropdown_item_last.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_my_page.xml b/frontend/ARchive/app/src/main/res/layout/fragment_my_page.xml index 9228b9a20..d222f1576 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_my_page.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_my_page.xml @@ -241,7 +241,6 @@ android:layout_marginHorizontal="@dimen/margin" android:layout_marginTop="24dp" android:background="@null" - android:popupBackground="@null" android:dropDownWidth="match_parent" android:dropDownVerticalOffset="@dimen/common_spinner_height" android:spinnerMode="dropdown" diff --git a/frontend/ARchive/app/src/main/res/layout/item_spinner.xml b/frontend/ARchive/app/src/main/res/layout/item_spinner.xml index e84abcc6f..9c626280b 100644 --- a/frontend/ARchive/app/src/main/res/layout/item_spinner.xml +++ b/frontend/ARchive/app/src/main/res/layout/item_spinner.xml @@ -7,11 +7,11 @@ android:orientation="vertical"> Date: Wed, 24 Apr 2024 11:06:41 +0900 Subject: [PATCH 199/301] =?UTF-8?q?Revert=20"Revert=20"design:=20=EB=A7=88?= =?UTF-8?q?=EC=9D=B4=ED=8E=98=EC=9D=B4=EC=A7=80=20UI=20Update=20-=20Spinne?= =?UTF-8?q?r(2)""?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 6c3f90b0204309c0f5404af96f5ba60eb30cadc8. --- .../archive/ARchiveApplication.kt | 2 +- .../presentation/ui/mypage/MyPageFragment.kt | 56 ++++++++++++------- .../ui/mypage/MyPageViewModelImpl.kt | 2 + .../ui/mypage/adapter/CapsuleTypeSpinner.kt | 28 +++++++--- .../src/main/res/drawable/ic_arrow_up_24.xml | 13 +++++ ...round.xml => spinner_background_close.xml} | 0 .../res/drawable/spinner_background_open.xml | 25 +++++++++ .../drawable/spinner_dropdown_item_last.xml | 8 +++ .../src/main/res/layout/fragment_my_page.xml | 1 + .../app/src/main/res/layout/item_spinner.xml | 4 +- .../main/res/layout/item_spinner_dropdown.xml | 4 +- 11 files changed, 111 insertions(+), 32 deletions(-) create mode 100644 frontend/ARchive/app/src/main/res/drawable/ic_arrow_up_24.xml rename frontend/ARchive/app/src/main/res/drawable/{spinner_background.xml => spinner_background_close.xml} (100%) create mode 100644 frontend/ARchive/app/src/main/res/drawable/spinner_background_open.xml create mode 100644 frontend/ARchive/app/src/main/res/drawable/spinner_dropdown_item_last.xml diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/ARchiveApplication.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/ARchiveApplication.kt index 2a313966c..9b18a81d0 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/ARchiveApplication.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/ARchiveApplication.kt @@ -28,7 +28,7 @@ class ARchiveApplication : Application(), DefaultLifecycleObserver { networkConnectionChecker = NetworkStatusChecker(context) KakaoSdk.init(this, BuildConfig.KAKAO_NATIVE_APP_KEY) //setCrashHandler() - Thread.setDefaultUncaughtExceptionHandler { _, _ -> caughtException() } + //Thread.setDefaultUncaughtExceptionHandler { _, _ -> caughtException() } dummyCoroutines() // 키 값 알아내기 diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt index 64e6f7715..877df2ffe 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt @@ -1,9 +1,12 @@ package com.droidblossom.archive.presentation.ui.mypage import android.os.Bundle +import android.util.Log +import android.view.MotionEvent import android.view.View import android.view.ViewGroup import android.widget.AdapterView +import androidx.core.content.ContextCompat import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope @@ -51,6 +54,11 @@ class MyPageFragment : ) } + private val spinnerAdapter by lazy { + val capsuleTypeList = arrayOf("Secret", "Public", "Group") + CapsuleTypeSpinner(requireContext(), capsuleTypeList) + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding.vm = viewModel @@ -70,6 +78,7 @@ class MyPageFragment : initRVA() initView() + initSpinner() // binding.settingBtn.setOnClickListener { // throw RuntimeException("Test Crash") @@ -94,26 +103,6 @@ class MyPageFragment : copyText("userTag", viewModel.myInfo.value.tag) true } - - val data = arrayOf("Secret", "Public", "Group") - val adapter = CapsuleTypeSpinner(requireContext(), data) - capsuleTypeSpinner.adapter = adapter - capsuleTypeSpinner.onItemSelectedListener = - object : AdapterView.OnItemSelectedListener { - override fun onItemSelected( - parent: AdapterView<*>, - view: View, - position: Int, - id: Long - ) { - - } - - override fun onNothingSelected(parent: AdapterView<*>?) { - - } - } - } } @@ -139,6 +128,32 @@ class MyPageFragment : }) } + private fun initSpinner() { + with(binding) { + capsuleTypeSpinner.adapter = spinnerAdapter + capsuleTypeSpinner.onItemSelectedListener = + object : AdapterView.OnItemSelectedListener { + override fun onItemSelected( + parent: AdapterView<*>, + view: View, + position: Int, + id: Long + ) { + + } + + override fun onNothingSelected(parent: AdapterView<*>?) { + + } + } + capsuleTypeSpinner.viewTreeObserver.addOnWindowFocusChangeListener { hasFocus -> + spinnerAdapter.spinnerIsOpened = hasFocus + spinnerAdapter.notifyDataSetChanged() + } + } + + } + override fun observeData() { viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { @@ -177,6 +192,7 @@ class MyPageFragment : } } } + } override fun onHiddenChanged(hidden: Boolean) { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt index cc94edc02..38bc5fd46 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt @@ -54,6 +54,7 @@ class MyPageViewModelImpl @Inject constructor( override val lastCreatedTime: StateFlow get() = _lastCreatedTime + init { load() } @@ -127,4 +128,5 @@ class MyPageViewModelImpl @Inject constructor( _myPageEvents.emit(MyPageViewModel.MyPageEvent.ClickSetting) } } + } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/CapsuleTypeSpinner.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/CapsuleTypeSpinner.kt index 7545ea15c..627876222 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/CapsuleTypeSpinner.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/CapsuleTypeSpinner.kt @@ -2,11 +2,17 @@ package com.droidblossom.archive.presentation.ui.mypage.adapter import android.annotation.SuppressLint import android.content.Context +import android.content.res.ColorStateList import android.graphics.Color +import android.graphics.Outline +import android.graphics.drawable.GradientDrawable +import android.os.Build import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.view.ViewOutlineProvider import android.widget.BaseAdapter +import androidx.core.content.ContextCompat import androidx.databinding.DataBindingUtil import com.droidblossom.archive.R import com.droidblossom.archive.databinding.ItemSpinnerBinding @@ -14,9 +20,9 @@ import com.droidblossom.archive.databinding.ItemSpinnerDropdownBinding class CapsuleTypeSpinner(private val context: Context, private val items: Array) : BaseAdapter() { + var spinnerIsOpened = false private lateinit var spinnerItemBinding: ItemSpinnerBinding - private lateinit var spinnerDropdownItemBinding: ItemSpinnerDropdownBinding override fun getCount(): Int { @@ -34,23 +40,31 @@ class CapsuleTypeSpinner(private val context: Context, private val items: Array< @SuppressLint("ViewHolder") override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { spinnerItemBinding = DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.item_spinner, parent, false) - spinnerItemBinding.tvItemName.text = items[position] + spinnerItemBinding.spinnerItemName.text = items[position] return spinnerItemBinding.root } override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View { spinnerDropdownItemBinding = DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.item_spinner_dropdown, parent, false) - spinnerDropdownItemBinding.tvDropdownItemName.text = items[position] + spinnerDropdownItemBinding.dropdownItemName.text = items[position] - if (items[position] == spinnerItemBinding.tvItemName.text.toString()) { - spinnerDropdownItemBinding.tvDropdownItemName.setTextColor(Color.parseColor("#ED7A2B")) + if (items[position] == spinnerItemBinding.spinnerItemName.text.toString()) { + spinnerDropdownItemBinding.dropdownItemName.setTextColor(Color.WHITE) } else { - spinnerDropdownItemBinding.tvDropdownItemName.setTextColor(Color.parseColor("#000000")) + spinnerDropdownItemBinding.dropdownItemName.setTextColor(ContextCompat.getColor(context, R.color.gray_300)) } if (position == (count - 1)) { - spinnerDropdownItemBinding.tvDivider.visibility = View.GONE + spinnerDropdownItemBinding.divider.visibility = View.GONE + spinnerDropdownItemBinding.root.setBackgroundResource(R.drawable.spinner_dropdown_item_last) + + } + + if (spinnerIsOpened){ + spinnerItemBinding.spinnerItemName.setBackgroundResource(R.drawable.spinner_background_close) + } else{ + spinnerItemBinding.spinnerItemName.setBackgroundResource(R.drawable.spinner_background_open) } return spinnerDropdownItemBinding.root diff --git a/frontend/ARchive/app/src/main/res/drawable/ic_arrow_up_24.xml b/frontend/ARchive/app/src/main/res/drawable/ic_arrow_up_24.xml new file mode 100644 index 000000000..7c85f9a5b --- /dev/null +++ b/frontend/ARchive/app/src/main/res/drawable/ic_arrow_up_24.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/drawable/spinner_background.xml b/frontend/ARchive/app/src/main/res/drawable/spinner_background_close.xml similarity index 100% rename from frontend/ARchive/app/src/main/res/drawable/spinner_background.xml rename to frontend/ARchive/app/src/main/res/drawable/spinner_background_close.xml diff --git a/frontend/ARchive/app/src/main/res/drawable/spinner_background_open.xml b/frontend/ARchive/app/src/main/res/drawable/spinner_background_open.xml new file mode 100644 index 000000000..7d38881e4 --- /dev/null +++ b/frontend/ARchive/app/src/main/res/drawable/spinner_background_open.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/drawable/spinner_dropdown_item_last.xml b/frontend/ARchive/app/src/main/res/drawable/spinner_dropdown_item_last.xml new file mode 100644 index 000000000..da099ce46 --- /dev/null +++ b/frontend/ARchive/app/src/main/res/drawable/spinner_dropdown_item_last.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_my_page.xml b/frontend/ARchive/app/src/main/res/layout/fragment_my_page.xml index d222f1576..9228b9a20 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_my_page.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_my_page.xml @@ -241,6 +241,7 @@ android:layout_marginHorizontal="@dimen/margin" android:layout_marginTop="24dp" android:background="@null" + android:popupBackground="@null" android:dropDownWidth="match_parent" android:dropDownVerticalOffset="@dimen/common_spinner_height" android:spinnerMode="dropdown" diff --git a/frontend/ARchive/app/src/main/res/layout/item_spinner.xml b/frontend/ARchive/app/src/main/res/layout/item_spinner.xml index 9c626280b..e84abcc6f 100644 --- a/frontend/ARchive/app/src/main/res/layout/item_spinner.xml +++ b/frontend/ARchive/app/src/main/res/layout/item_spinner.xml @@ -7,11 +7,11 @@ android:orientation="vertical"> Date: Wed, 24 Apr 2024 11:10:52 +0900 Subject: [PATCH 200/301] =?UTF-8?q?design:=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20UI=20Update=20-=20Spinner(last)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../droidblossom/archive/ARchiveApplication.kt | 2 +- .../ui/mypage/adapter/CapsuleTypeSpinner.kt | 1 - .../main/res/layout/item_spinner_dropdown.xml | 17 +++++++++-------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/ARchiveApplication.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/ARchiveApplication.kt index 9b18a81d0..2a313966c 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/ARchiveApplication.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/ARchiveApplication.kt @@ -28,7 +28,7 @@ class ARchiveApplication : Application(), DefaultLifecycleObserver { networkConnectionChecker = NetworkStatusChecker(context) KakaoSdk.init(this, BuildConfig.KAKAO_NATIVE_APP_KEY) //setCrashHandler() - //Thread.setDefaultUncaughtExceptionHandler { _, _ -> caughtException() } + Thread.setDefaultUncaughtExceptionHandler { _, _ -> caughtException() } dummyCoroutines() // 키 값 알아내기 diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/CapsuleTypeSpinner.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/CapsuleTypeSpinner.kt index 627876222..0677946ed 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/CapsuleTypeSpinner.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/CapsuleTypeSpinner.kt @@ -56,7 +56,6 @@ class CapsuleTypeSpinner(private val context: Context, private val items: Array< } if (position == (count - 1)) { - spinnerDropdownItemBinding.divider.visibility = View.GONE spinnerDropdownItemBinding.root.setBackgroundResource(R.drawable.spinner_dropdown_item_last) } diff --git a/frontend/ARchive/app/src/main/res/layout/item_spinner_dropdown.xml b/frontend/ARchive/app/src/main/res/layout/item_spinner_dropdown.xml index 94e752cdc..3a574c005 100644 --- a/frontend/ARchive/app/src/main/res/layout/item_spinner_dropdown.xml +++ b/frontend/ARchive/app/src/main/res/layout/item_spinner_dropdown.xml @@ -7,6 +7,15 @@ android:background="@color/main_2" android:orientation="vertical"> + + + - - \ No newline at end of file From 7edcd94f5fb6117a94e04b9d6d242b352c785700 Mon Sep 17 00:00:00 2001 From: comst19 Date: Thu, 25 Apr 2024 00:32:48 +0900 Subject: [PATCH 201/301] =?UTF-8?q?refact:=20Spinner=20Dropdown=20View=20?= =?UTF-8?q?=EC=9E=AC=EC=82=AC=EC=9A=A9=EC=9C=BC=EB=A1=9C=20=EC=84=B1?= =?UTF-8?q?=EB=8A=A5=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ui/mypage/MyPageFragment.kt | 2 +- .../ui/mypage/adapter/CapsuleTypeSpinner.kt | 72 ++++++++++--------- .../spinner_dropdown_item_background.xml | 5 ++ ...spinner_dropdown_item_background_last.xml} | 0 .../main/res/layout/item_spinner_dropdown.xml | 3 +- 5 files changed, 45 insertions(+), 37 deletions(-) create mode 100644 frontend/ARchive/app/src/main/res/drawable/spinner_dropdown_item_background.xml rename frontend/ARchive/app/src/main/res/drawable/{spinner_dropdown_item_last.xml => spinner_dropdown_item_background_last.xml} (100%) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt index 877df2ffe..b1b36ce0b 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt @@ -55,7 +55,7 @@ class MyPageFragment : } private val spinnerAdapter by lazy { - val capsuleTypeList = arrayOf("Secret", "Public", "Group") + val capsuleTypeList = arrayOf(SpinnerCapsuleType.SECRET, SpinnerCapsuleType.PUBLIC, SpinnerCapsuleType.GROUP) CapsuleTypeSpinner(requireContext(), capsuleTypeList) } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/CapsuleTypeSpinner.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/CapsuleTypeSpinner.kt index 0677946ed..1c578bc53 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/CapsuleTypeSpinner.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/CapsuleTypeSpinner.kt @@ -2,70 +2,74 @@ package com.droidblossom.archive.presentation.ui.mypage.adapter import android.annotation.SuppressLint import android.content.Context -import android.content.res.ColorStateList -import android.graphics.Color -import android.graphics.Outline -import android.graphics.drawable.GradientDrawable -import android.os.Build import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.view.ViewOutlineProvider +import android.widget.AbsListView import android.widget.BaseAdapter import androidx.core.content.ContextCompat import androidx.databinding.DataBindingUtil import com.droidblossom.archive.R import com.droidblossom.archive.databinding.ItemSpinnerBinding import com.droidblossom.archive.databinding.ItemSpinnerDropdownBinding +import com.droidblossom.archive.presentation.ui.mypage.MyPageFragment -class CapsuleTypeSpinner(private val context: Context, private val items: Array) : BaseAdapter() { +class CapsuleTypeSpinner(private val context: Context, private val items: Array) : BaseAdapter() { var spinnerIsOpened = false private lateinit var spinnerItemBinding: ItemSpinnerBinding + private var selectedItemDescription: String? = null - private lateinit var spinnerDropdownItemBinding: ItemSpinnerDropdownBinding + override fun getCount(): Int = items.size - override fun getCount(): Int { - return items.size - } - - override fun getItem(position: Int): String { - return items[position] - } + override fun getItem(position: Int): MyPageFragment.SpinnerCapsuleType = items[position] - override fun getItemId(position: Int): Long { - return position.toLong() - } + override fun getItemId(position: Int): Long = position.toLong() @SuppressLint("ViewHolder") override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { spinnerItemBinding = DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.item_spinner, parent, false) - spinnerItemBinding.spinnerItemName.text = items[position] - + spinnerItemBinding.spinnerItemName.text = getItem(position).description + selectedItemDescription = getItem(position).description return spinnerItemBinding.root } override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View { - spinnerDropdownItemBinding = DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.item_spinner_dropdown, parent, false) - spinnerDropdownItemBinding.dropdownItemName.text = items[position] - if (items[position] == spinnerItemBinding.spinnerItemName.text.toString()) { - spinnerDropdownItemBinding.dropdownItemName.setTextColor(Color.WHITE) + if (spinnerIsOpened) { + spinnerItemBinding.spinnerItemName.setBackgroundResource(R.drawable.spinner_background_close) } else { - spinnerDropdownItemBinding.dropdownItemName.setTextColor(ContextCompat.getColor(context, R.color.gray_300)) + spinnerItemBinding.spinnerItemName.setBackgroundResource(R.drawable.spinner_background_open) } - if (position == (count - 1)) { - spinnerDropdownItemBinding.root.setBackgroundResource(R.drawable.spinner_dropdown_item_last) + val isLastVisibleItem = (position == items.indices.last { getItem(it).description != selectedItemDescription }) - } + if (getItem(position).description == selectedItemDescription) { + val view = convertView ?: View(context) + view.visibility = View.GONE + return view + } else { + val binding: ItemSpinnerDropdownBinding = if (convertView == null || convertView.tag == null) { + DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.item_spinner_dropdown, parent, false) + } else { + convertView.tag as ItemSpinnerDropdownBinding + } - if (spinnerIsOpened){ - spinnerItemBinding.spinnerItemName.setBackgroundResource(R.drawable.spinner_background_close) - } else{ - spinnerItemBinding.spinnerItemName.setBackgroundResource(R.drawable.spinner_background_open) - } + binding.dropdownItemName.text = getItem(position).description - return spinnerDropdownItemBinding.root + if (isLastVisibleItem) { + binding.root.setBackgroundResource(R.drawable.spinner_dropdown_item_background_last) + } else { + binding.root.setBackgroundResource(R.drawable.spinner_dropdown_item_background) + } + + if (getItem(position).description != selectedItemDescription) { + binding.dropdownItemName.setTextColor(ContextCompat.getColor(context, R.color.gray_300)) + } + + binding.root.tag = binding + binding.root.visibility = View.VISIBLE + return binding.root + } } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/drawable/spinner_dropdown_item_background.xml b/frontend/ARchive/app/src/main/res/drawable/spinner_dropdown_item_background.xml new file mode 100644 index 000000000..7ca49d672 --- /dev/null +++ b/frontend/ARchive/app/src/main/res/drawable/spinner_dropdown_item_background.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/drawable/spinner_dropdown_item_last.xml b/frontend/ARchive/app/src/main/res/drawable/spinner_dropdown_item_background_last.xml similarity index 100% rename from frontend/ARchive/app/src/main/res/drawable/spinner_dropdown_item_last.xml rename to frontend/ARchive/app/src/main/res/drawable/spinner_dropdown_item_background_last.xml diff --git a/frontend/ARchive/app/src/main/res/layout/item_spinner_dropdown.xml b/frontend/ARchive/app/src/main/res/layout/item_spinner_dropdown.xml index 3a574c005..f758ab93a 100644 --- a/frontend/ARchive/app/src/main/res/layout/item_spinner_dropdown.xml +++ b/frontend/ARchive/app/src/main/res/layout/item_spinner_dropdown.xml @@ -4,10 +4,9 @@ - Date: Thu, 25 Apr 2024 20:39:42 +0900 Subject: [PATCH 202/301] =?UTF-8?q?refact:=20=EB=84=A4=EC=9D=B4=EB=B2=84?= =?UTF-8?q?=20=EC=A7=80=EB=8F=84=EC=9D=98=20=EB=84=A4=EC=9D=B4=ED=8B=B0?= =?UTF-8?q?=EB=B8=8C=20=ED=81=B4=EB=9F=AC=EC=8A=A4=ED=84=B0=EB=A7=81?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=9D=B4=EC=A0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/ARchive/app/build.gradle | 6 +- .../ui/home/CapsuleClusteringKey.kt | 18 ++ .../presentation/ui/home/HomeFragment.kt | 174 +++++++++--------- .../presentation/ui/home/MapCapsuleMarker.kt | 14 -- .../main/res/drawable/ic_cluster_marker.xml | 15 ++ .../main/res/layout/item_marker_cluster.xml | 48 ----- frontend/ARchive/settings.gradle | 2 +- 7 files changed, 121 insertions(+), 156 deletions(-) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/CapsuleClusteringKey.kt delete mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/MapCapsuleMarker.kt create mode 100644 frontend/ARchive/app/src/main/res/drawable/ic_cluster_marker.xml delete mode 100644 frontend/ARchive/app/src/main/res/layout/item_marker_cluster.xml diff --git a/frontend/ARchive/app/build.gradle b/frontend/ARchive/app/build.gradle index b35078fd1..9242d5fdb 100644 --- a/frontend/ARchive/app/build.gradle +++ b/frontend/ARchive/app/build.gradle @@ -142,7 +142,7 @@ dependencies { implementation 'com.google.android.gms:play-services-auth:20.7.0' //네이버 지도 : https://navermaps.github.io/android-map-sdk/guide-ko/1.html - implementation 'com.naver.maps:map-sdk:3.17.0' + implementation 'com.naver.maps:map-sdk:3.18.0' // Jetpack Security implementation 'androidx.security:security-crypto-ktx:1.1.0-alpha06' @@ -183,10 +183,6 @@ dependencies { // oss-licenses : https://github.com/google/play-services-plugins/tree/master/oss-licenses-plugin implementation 'com.google.android.gms:play-services-oss-licenses:17.0.1' - // TedNaverMapClustering: https://github.com/ParkSangGwon/TedNaverMapClustering - //implementation 'io.github.ParkSangGwon:tedclustering-naver:x.y.z' - implementation 'io.github.ParkSangGwon:tedclustering-naver:1.0.2' - // Swiperefreshlayout: https://developer.android.com/jetpack/androidx/releases/swiperefreshlayout?hl=ko#kts implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0") } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/CapsuleClusteringKey.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/CapsuleClusteringKey.kt new file mode 100644 index 000000000..eeebefbad --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/CapsuleClusteringKey.kt @@ -0,0 +1,18 @@ +package com.droidblossom.archive.presentation.ui.home + +import com.naver.maps.geometry.LatLng +import com.naver.maps.map.clustering.ClusteringKey + +class CapsuleClusteringKey(val id: Long, val capsuleType: HomeFragment.CapsuleType, private val position : LatLng) : ClusteringKey { + + override fun getPosition() = position + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || javaClass != other.javaClass) return false + val itemKey = other as CapsuleClusteringKey + return id == itemKey.id + } + + override fun hashCode() = id.hashCode() +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt index 5ba133ed4..00f70e307 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt @@ -1,10 +1,8 @@ package com.droidblossom.archive.presentation.ui.home import android.os.Bundle -import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.TextView import androidx.core.content.ContextCompat import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle @@ -25,12 +23,17 @@ import com.naver.maps.map.LocationTrackingMode import com.naver.maps.map.MapFragment import com.naver.maps.map.NaverMap import com.naver.maps.map.OnMapReadyCallback +import com.naver.maps.map.clustering.ClusterMarkerInfo +import com.naver.maps.map.clustering.Clusterer +import com.naver.maps.map.clustering.DefaultClusterMarkerUpdater +import com.naver.maps.map.clustering.DefaultLeafMarkerUpdater +import com.naver.maps.map.clustering.LeafMarkerInfo import com.naver.maps.map.overlay.Marker +import com.naver.maps.map.overlay.Overlay import com.naver.maps.map.overlay.OverlayImage import com.naver.maps.map.util.FusedLocationSource import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch -import ted.gun0912.clustering.naver.TedNaverClustering import kotlin.math.pow @AndroidEntryPoint @@ -39,10 +42,46 @@ class HomeFragment : BaseFragment(R.layo override val viewModel: HomeViewModelImpl by viewModels() - private lateinit var naverMap: NaverMap + //private lateinit var naverMap: NaverMap private lateinit var locationUtil: LocationUtil private lateinit var locationSource: FusedLocationSource - private lateinit var naverCluster: TedNaverClustering + + // https://navermaps.github.io/android-map-sdk/guide-ko/5-8.html + private val clusterer: Clusterer = Clusterer.Builder() + .clusterMarkerUpdater(object : DefaultClusterMarkerUpdater(){ + override fun updateClusterMarker(info: ClusterMarkerInfo, marker: Marker) { + super.updateClusterMarker(info, marker) + //marker.icon = OverlayImage.fromResource(R.drawable.ic_cluster_marker) + //marker.captionColor = Color.BLACK + marker.onClickListener = Overlay.OnClickListener{ + // 클러스터된 거 클릭이벤트 - 나중에 클러스터에 행당된 캡슐들 사이드에 보여주거나 하면 좋을듯? + true + } + } + }).leafMarkerUpdater(object : DefaultLeafMarkerUpdater(){ + override fun updateLeafMarker(info: LeafMarkerInfo, marker: Marker) { + super.updateLeafMarker(info, marker) + val key = info.key as CapsuleClusteringKey + marker.icon = when(key.capsuleType){ + CapsuleType.SECRET -> OverlayImage.fromResource(R.drawable.ic_marker_pin_secret) + CapsuleType.GROUP -> OverlayImage.fromResource(R.drawable.ic_marker_pin_group) + CapsuleType.PUBLIC -> OverlayImage.fromResource(R.drawable.ic_marker_pin_public) + } + marker.onClickListener = Overlay.OnClickListener { + viewModel.homeEvent( + HomeViewModel.HomeEvent.ShowCapsulePreviewDialog( + key.id.toString(), + key.capsuleType.toString() + ) + ) + true + } + } + }) + .minZoom(MINZOOM.toInt()+2) + .maxZoom(MAXZOOM.toInt()-2) + .build() + private val zoomToRadiusMap: Map by lazy { val map = mutableMapOf() @@ -123,36 +162,36 @@ class HomeFragment : BaseFragment(R.layo } viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { - viewModel.followLocation.collect { - if (::naverMap.isInitialized) { - if (it) { - naverMap.maxZoom = FIXZOOM - naverMap.minZoom = FIXZOOM - naverMap.locationOverlay.circleRadius = 100 - naverMap.locationOverlay.circleOutlineWidth = 1 - naverMap.locationOverlay.circleColor = + viewModel.followLocation.collect { followLocation -> + clusterer.map?.let { map -> + if (followLocation) { + map.maxZoom = FIXZOOM + map.minZoom = FIXZOOM + map.locationOverlay.circleRadius = 100 + map.locationOverlay.circleOutlineWidth = 1 + map.locationOverlay.circleColor = ContextCompat.getColor(requireContext(), R.color.main_1_alpha20) - naverMap.locationOverlay.circleOutlineColor = + map.locationOverlay.circleOutlineColor = ContextCompat.getColor(requireContext(), R.color.main_1) - naverMap.locationTrackingMode = LocationTrackingMode.Follow + map.locationTrackingMode = LocationTrackingMode.Follow } else { - naverMap.locationTrackingMode = LocationTrackingMode.NoFollow - naverMap.minZoom = MINZOOM - naverMap.maxZoom = MAXZOOM - naverMap.locationOverlay.circleRadius = 0 - + map.locationTrackingMode = LocationTrackingMode.NoFollow + map.minZoom = MINZOOM + map.maxZoom = MAXZOOM + map.locationOverlay.circleRadius = 0 } } - } } } viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.capsuleList.collect { - if (::naverMap.isInitialized) { - naverCluster.clearItems() - naverCluster.addItems(getItems(it)) + clusterer.map?.let{ map -> + // 마커 지우는 로직 + clusterer.clear() + // 마커 찍는 로직 + addMarker(it) } } } @@ -172,7 +211,7 @@ class HomeFragment : BaseFragment(R.layo override fun onMapReady(naverMap: NaverMap) { - this.naverMap = naverMap + this.clusterer.map = naverMap naverMap.uiSettings.isRotateGesturesEnabled = false naverMap.locationSource = locationSource naverMap.locationTrackingMode = LocationTrackingMode.Follow @@ -192,75 +231,32 @@ class HomeFragment : BaseFragment(R.layo isVisible = true icon = OverlayImage.fromResource(R.drawable.ic_my_location_24) } - - naverCluster = TedNaverClustering.with(requireContext(), naverMap) - .markerClickListener { capsuleMarker -> - viewModel.homeEvent( - HomeViewModel.HomeEvent.ShowCapsulePreviewDialog( - capsuleMarker.capsuleAnchor.id.toString(), - capsuleMarker.capsuleAnchor.capsuleType.toString() - ) - ) - - }.customMarker { capsuleMarker -> - Marker().apply { - icon = when (capsuleMarker.capsuleAnchor.capsuleType) { - CapsuleType.SECRET -> OverlayImage.fromResource(R.drawable.ic_marker_pin_secret) - CapsuleType.GROUP -> OverlayImage.fromResource(R.drawable.ic_marker_pin_group) - CapsuleType.PUBLIC -> OverlayImage.fromResource(R.drawable.ic_marker_pin_public) - } - } - }.customCluster { cluster -> - val clusterView = LayoutInflater.from(requireContext()).inflate(R.layout.item_marker_cluster, null) - val clusterSize = clusterView.findViewById(R.id.capsuleNumTextView) - - val displayText = when { - cluster.items.size < 10 -> cluster.items.size.toString() - cluster.items.size % 10 == 0 -> cluster.items.size.toString() - else -> "${(cluster.items.size / 10) * 10}+" - } - - clusterSize.text = displayText - clusterView - } - .make() } + private fun addMarker(capsuleList: List) { - private fun getItems(capsuleList: List): List { - val markers = ArrayList() - capsuleList.map { capsuleMarker -> - val temp = MapCapsuleMarker(capsuleMarker) - markers.add(temp) + val keyTagMap: Map = capsuleList.associate { + CapsuleClusteringKey(id = it.id, capsuleType = it.capsuleType, position = LatLng(it.latitude, it.longitude)) to null } - return markers + + clusterer.addAll(keyTagMap) } - private fun fetchCapsulesNearUser(){ - if (::naverMap.isInitialized){ - locationUtil.getCurrentLocation { latitude, longitude -> - viewModel.getNearbyCapsules( - latitude, - longitude, - getRadiusForCurrentZoom(), - viewModel.filterCapsuleSelect.value.toString() - ) - } - }else{ - locationUtil.getCurrentLocation { latitude, longitude -> - viewModel.getNearbyCapsules( - latitude, - longitude, - 4.0, - viewModel.filterCapsuleSelect.value.toString() - ) - } + private fun fetchCapsulesNearUser() { + locationUtil.getCurrentLocation { latitude, longitude -> + val radius = if (clusterer.map != null) getRadiusForCurrentZoom() else 4.0 + viewModel.getNearbyCapsules( + latitude, + longitude, + radius, + viewModel.filterCapsuleSelect.value.toString() + ) } } - private fun fetchCapsulesInCameraFocus(){ - if (::naverMap.isInitialized){ - val cameraTarget = naverMap.cameraPosition.target + private fun fetchCapsulesInCameraFocus() { + clusterer.map?.let { map -> + val cameraTarget = map.cameraPosition.target viewModel.getNearbyCapsules( cameraTarget.latitude, cameraTarget.longitude, @@ -272,8 +268,10 @@ class HomeFragment : BaseFragment(R.layo private fun getRadiusForCurrentZoom(): Double { - val currentZoom = naverMap.cameraPosition.zoom - val closestZoomLevel = zoomToRadiusMap.keys.minByOrNull { Math.abs(it - currentZoom) } + val currentZoom = clusterer.map?.cameraPosition?.zoom ?: return FIXZOOM + + val closestZoomLevel = zoomToRadiusMap.keys.minByOrNull { kotlin.math.abs(it - currentZoom) } + return zoomToRadiusMap[closestZoomLevel] ?: throw IllegalArgumentException("Invalid zoom level: $currentZoom") } @@ -288,7 +286,7 @@ class HomeFragment : BaseFragment(R.layo ) ) { if (!locationSource.isActivated) { // 권한 거부됨 - naverMap.locationTrackingMode = LocationTrackingMode.None + clusterer.map?.locationTrackingMode = LocationTrackingMode.None } return } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/MapCapsuleMarker.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/MapCapsuleMarker.kt deleted file mode 100644 index 5baf87ab7..000000000 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/MapCapsuleMarker.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.droidblossom.archive.presentation.ui.home - -import com.droidblossom.archive.domain.model.capsule.CapsuleMarker -import ted.gun0912.clustering.clustering.TedClusterItem -import ted.gun0912.clustering.geometry.TedLatLng - -data class MapCapsuleMarker( - val capsuleAnchor: CapsuleMarker -) : TedClusterItem { - - override fun getTedLatLng(): TedLatLng = TedLatLng(capsuleAnchor.latitude,capsuleAnchor.longitude) - - -} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/drawable/ic_cluster_marker.xml b/frontend/ARchive/app/src/main/res/drawable/ic_cluster_marker.xml new file mode 100644 index 000000000..5aa4a692e --- /dev/null +++ b/frontend/ARchive/app/src/main/res/drawable/ic_cluster_marker.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/frontend/ARchive/app/src/main/res/layout/item_marker_cluster.xml b/frontend/ARchive/app/src/main/res/layout/item_marker_cluster.xml deleted file mode 100644 index 8655abc19..000000000 --- a/frontend/ARchive/app/src/main/res/layout/item_marker_cluster.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/frontend/ARchive/settings.gradle b/frontend/ARchive/settings.gradle index 82fe46e22..231b9bf9b 100644 --- a/frontend/ARchive/settings.gradle +++ b/frontend/ARchive/settings.gradle @@ -13,7 +13,7 @@ dependencyResolutionManagement { maven { url "https://jitpack.io" } maven { url 'https://devrepo.kakao.com/nexus/content/groups/public/' } // 카카오 로그인 maven { - url 'https://naver.jfrog.io/artifactory/maven/' + url 'https://repository.map.naver.com/archive/maven' } } } From 036f489a053cf7635f72efaa4619d71f376bd6c9 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Fri, 26 Apr 2024 02:30:52 +0900 Subject: [PATCH 203/301] =?UTF-8?q?fix=20:=20=EC=A3=BC=EC=86=8C=EB=A1=9D?= =?UTF-8?q?=20=20=EC=9D=B4=EB=A6=84=20&=20=EB=B2=88=ED=98=B8=20=EB=8F=99?= =?UTF-8?q?=EC=8B=9C=20=EC=A0=84=EC=86=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../request/FriendsSearchPhoneRequestDto.kt | 2 +- .../data/dto/friend/request/PhoneBooks.kt | 6 +++++ .../model/friend/FriendsSearchPhoneRequest.kt | 5 ++-- .../presentation/ui/home/HomeFragment.kt | 23 +++++++++++-------- .../addfriend/AddFriendViewModelImpl.kt | 8 ++++--- .../archive/util/ContactsUtils.kt | 7 +++--- .../res/drawable/ic_cluster_marker_46.xml | 12 ++++++++++ 7 files changed, 45 insertions(+), 18 deletions(-) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/request/PhoneBooks.kt create mode 100644 frontend/ARchive/app/src/main/res/drawable/ic_cluster_marker_46.xml diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/request/FriendsSearchPhoneRequestDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/request/FriendsSearchPhoneRequestDto.kt index 8d1ec0556..4164058a6 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/request/FriendsSearchPhoneRequestDto.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/request/FriendsSearchPhoneRequestDto.kt @@ -1,5 +1,5 @@ package com.droidblossom.archive.data.dto.friend.request data class FriendsSearchPhoneRequestDto( - val phones : List + val phoneBooks : List ) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/request/PhoneBooks.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/request/PhoneBooks.kt new file mode 100644 index 000000000..ce9ed29c3 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/request/PhoneBooks.kt @@ -0,0 +1,6 @@ +package com.droidblossom.archive.data.dto.friend.request + +data class PhoneBooks ( + val originPhone: String, + val originName : String, +) \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendsSearchPhoneRequest.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendsSearchPhoneRequest.kt index 9cce2c986..b13222d9c 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendsSearchPhoneRequest.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendsSearchPhoneRequest.kt @@ -1,11 +1,12 @@ package com.droidblossom.archive.domain.model.friend import com.droidblossom.archive.data.dto.friend.request.FriendsSearchPhoneRequestDto +import com.droidblossom.archive.data.dto.friend.request.PhoneBooks data class FriendsSearchPhoneRequest ( - val phones : List + val phoneBooks : List ){ fun toDto() = FriendsSearchPhoneRequestDto( - phones = this.phones, + phoneBooks = this.phoneBooks, ) } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt index 00f70e307..9932f4e79 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt @@ -1,5 +1,6 @@ package com.droidblossom.archive.presentation.ui.home +import android.graphics.Color import android.os.Bundle import android.view.View import android.view.ViewGroup @@ -32,6 +33,7 @@ import com.naver.maps.map.overlay.Marker import com.naver.maps.map.overlay.Overlay import com.naver.maps.map.overlay.OverlayImage import com.naver.maps.map.util.FusedLocationSource +import com.naver.maps.map.util.MarkerIcons import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch import kotlin.math.pow @@ -47,12 +49,14 @@ class HomeFragment : BaseFragment(R.layo private lateinit var locationSource: FusedLocationSource // https://navermaps.github.io/android-map-sdk/guide-ko/5-8.html - private val clusterer: Clusterer = Clusterer.Builder() - .clusterMarkerUpdater(object : DefaultClusterMarkerUpdater(){ + private val clusterer: Clusterer = + Clusterer.Builder().clusterMarkerUpdater(object : DefaultClusterMarkerUpdater(){ override fun updateClusterMarker(info: ClusterMarkerInfo, marker: Marker) { super.updateClusterMarker(info, marker) - //marker.icon = OverlayImage.fromResource(R.drawable.ic_cluster_marker) - //marker.captionColor = Color.BLACK + marker.icon = OverlayImage.fromResource(R.drawable.ic_cluster_marker_46) + marker.captionColor = Color.BLACK + marker.captionHaloColor = ContextCompat.getColor(requireContext(), R.color.main_bg_1) + marker.captionTextSize = 20f marker.onClickListener = Overlay.OnClickListener{ // 클러스터된 거 클릭이벤트 - 나중에 클러스터에 행당된 캡슐들 사이드에 보여주거나 하면 좋을듯? true @@ -85,12 +89,10 @@ class HomeFragment : BaseFragment(R.layo private val zoomToRadiusMap: Map by lazy { val map = mutableMapOf() - val maxRadius = 1100.0 - val minRadius = 2.0 for (zoomLevel in MINZOOM.toInt()..MAXZOOM.toInt()) { val normalizedZoom = 1 - ((zoomLevel - MINZOOM) / (MAXZOOM - MINZOOM)) - val radius = minRadius + (maxRadius - minRadius) * normalizedZoom.pow(3) + val radius = MINRADIUS + (MAXRADIUS - MINRADIUS) * normalizedZoom.pow(3) map[zoomLevel.toDouble()] = radius } map.toMap() @@ -187,7 +189,7 @@ class HomeFragment : BaseFragment(R.layo viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.capsuleList.collect { - clusterer.map?.let{ map -> + clusterer.map?.let{ _ -> // 마커 지우는 로직 clusterer.clear() // 마커 찍는 로직 @@ -244,7 +246,7 @@ class HomeFragment : BaseFragment(R.layo private fun fetchCapsulesNearUser() { locationUtil.getCurrentLocation { latitude, longitude -> - val radius = if (clusterer.map != null) getRadiusForCurrentZoom() else 4.0 + val radius = if (clusterer.map != null) getRadiusForCurrentZoom() else DEFATULTRADIUS viewModel.getNearbyCapsules( latitude, longitude, @@ -324,6 +326,9 @@ class HomeFragment : BaseFragment(R.layo const val MAXLAT = 43.0 const val MINLNG = 124.0 const val MAXLNG = 132.0 + const val DEFATULTRADIUS = 4.0 + const val MAXRADIUS = 1100.0 + const val MINRADIUS = 2.0 } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt index 337da4ad1..18a95d09b 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt @@ -1,7 +1,9 @@ package com.droidblossom.archive.presentation.ui.mypage.friend.addfriend +import android.text.TextUtils.substring import androidx.lifecycle.viewModelScope import com.droidblossom.archive.R +import com.droidblossom.archive.data.dto.friend.request.PhoneBooks import com.droidblossom.archive.domain.model.friend.FriendReqRequest import com.droidblossom.archive.domain.model.friend.FriendsSearchPhoneRequest import com.droidblossom.archive.domain.model.friend.FriendsSearchRequest @@ -133,11 +135,11 @@ class AddFriendViewModelImpl @Inject constructor( } } - fun contactsSearch(phones: List) { + fun contactsSearch(phoneBooks: List) { viewModelScope.launch { _addEvent.emit(AddFriendViewModel.AddEvent.OpenLoading) - friendsSearchPhoneUseCase(FriendsSearchPhoneRequest(phones.filter { - it.length == 11 && it.substring(0, 3) == "010" + friendsSearchPhoneUseCase(FriendsSearchPhoneRequest(phoneBooks.filter { + it.originPhone.length == 11 && it.originPhone.substring(0, 3) == "010" })).collect { result -> result.onSuccess { response -> _addFriendList.emit(response.friends) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/ContactsUtils.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/ContactsUtils.kt index 15edfd4a9..24ba0c25e 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/ContactsUtils.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/ContactsUtils.kt @@ -4,11 +4,12 @@ import android.content.ContentResolver import android.content.Context import android.provider.ContactsContract import android.util.Log +import com.droidblossom.archive.data.dto.friend.request.PhoneBooks object ContactsUtils { - fun getContacts(context : Context) : List { + fun getContacts(context : Context) : List { val resolver: ContentResolver = context.contentResolver val phoneUri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI val projection = arrayOf( @@ -16,7 +17,7 @@ object ContactsUtils { ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone.NUMBER ) - val numberList = mutableListOf() + val numberList = mutableListOf() val cursor = resolver.query(phoneUri, projection, null, null, null) if (cursor != null) { while (cursor.moveToNext()) { @@ -25,7 +26,7 @@ object ContactsUtils { val name = cursor.getString(nameIndex) var number = cursor.getString(numberIndex) number = number.replace("-", "") - numberList.add(number) + numberList.add(PhoneBooks(number,name)) Log.d("GetContact", "이름 : $name 번호 : $number") } } diff --git a/frontend/ARchive/app/src/main/res/drawable/ic_cluster_marker_46.xml b/frontend/ARchive/app/src/main/res/drawable/ic_cluster_marker_46.xml new file mode 100644 index 000000000..e0a91117b --- /dev/null +++ b/frontend/ARchive/app/src/main/res/drawable/ic_cluster_marker_46.xml @@ -0,0 +1,12 @@ + + + + From c30cd6e18c37e05441f147a06e25ba860715a43b Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Fri, 26 Apr 2024 02:52:29 +0900 Subject: [PATCH 204/301] =?UTF-8?q?fix=20:=20=ED=83=9C=EA=B7=B8=20?= =?UTF-8?q?=EA=B2=80=EC=83=89=EC=97=90=EC=84=9C=20=EB=B2=84=EA=B7=B8=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/dto/friend/response/FriendsSearchResponseDto.kt | 3 ++- .../main/java/com/droidblossom/archive/util/BindingAdapter.kt | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendsSearchResponseDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendsSearchResponseDto.kt index 4b735dcb8..c5cb64518 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendsSearchResponseDto.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendsSearchResponseDto.kt @@ -6,6 +6,7 @@ import java.io.Serializable data class FriendsSearchResponseDto( val id: Long, val profileUrl: String, + val originName : String?, val nickname: String, val isFriend: Boolean, val isFriendRequest :Boolean, @@ -17,7 +18,7 @@ data class FriendsSearchResponseDto( nickname = this.nickname, isFriend = this.isFriend, isFriendRequest = this.isFriendRequest, - name = "", + name = this.originName ?: "", isChecked = false ) } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/BindingAdapter.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/BindingAdapter.kt index 4b4490831..757f1cf2b 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/BindingAdapter.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/BindingAdapter.kt @@ -214,7 +214,7 @@ fun TextView.addFriendText(isFriend: Boolean, isRequest : Boolean, name : String if (isFriend){ this.text = "이미 친구입니다." } else { - this.text = "요천을 보냈습니다." + this.text = "요청을 보냈습니다." } }else { this.text = name From f74f1af4b3491f944ead030076a17fed98506679 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Fri, 26 Apr 2024 03:47:46 +0900 Subject: [PATCH 205/301] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC=20=EB=A6=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=EC=9A=94=EC=B2=AD=20usecase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../friend/request/FriendsReqRequestDto.kt | 5 +++ .../data/repository/FriendRepositoryImpl.kt | 5 +++ .../data/source/remote/api/FriendService.kt | 6 ++++ .../domain/repository/FriendRepository.kt | 2 ++ .../friend/FriendListRequestUseCase.kt | 32 +++++++++++++++++++ .../addfriend/AddFriendViewModelImpl.kt | 5 ++- 6 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/request/FriendsReqRequestDto.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendListRequestUseCase.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/request/FriendsReqRequestDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/request/FriendsReqRequestDto.kt new file mode 100644 index 000000000..a73861f8e --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/request/FriendsReqRequestDto.kt @@ -0,0 +1,5 @@ +package com.droidblossom.archive.data.dto.friend.request + +data class FriendsReqRequestDto( + val friendIds : List +) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/FriendRepositoryImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/FriendRepositoryImpl.kt index a619f9526..b38a10931 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/FriendRepositoryImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/FriendRepositoryImpl.kt @@ -5,6 +5,7 @@ import com.droidblossom.archive.data.dto.capsule_skin.response.CapsuleSkinsPageR import com.droidblossom.archive.data.dto.common.toModel import com.droidblossom.archive.data.dto.friend.request.FriendAcceptRequestDto import com.droidblossom.archive.data.dto.friend.request.FriendReqRequestDto +import com.droidblossom.archive.data.dto.friend.request.FriendsReqRequestDto import com.droidblossom.archive.data.dto.friend.request.FriendsSearchPhoneRequestDto import com.droidblossom.archive.data.dto.friend.request.FriendsSearchRequestDto import com.droidblossom.archive.data.dto.friend.response.FriendReqStatusResponseDto @@ -29,6 +30,10 @@ class FriendRepositoryImpl @Inject constructor( return apiHandler({ api.postFriendsRequestApi(request.friendId) }) { response: ResponseBody -> response.result.toModel() } } + override suspend fun postFriendsListRequest(request: FriendsReqRequestDto): RetrofitResult { + return apiHandler({api.postFriendListRequestsPageApi(request)}) { response : ResponseBody ->response.result.toModel()} + } + override suspend fun postFriendsAcceptRequest(request: FriendAcceptRequestDto): RetrofitResult { return apiHandler({ api.postFriendsAcceptRequestApi(request.friendId) }) {response: ResponseBody -> response.result.toModel()} } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt index e66b68080..dd35f98f9 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/FriendService.kt @@ -5,6 +5,7 @@ import retrofit2.http.Body import retrofit2.http.POST import retrofit2.Response import com.droidblossom.archive.data.dto.ResponseBody +import com.droidblossom.archive.data.dto.friend.request.FriendsReqRequestDto import com.droidblossom.archive.data.dto.friend.request.FriendsSearchPhoneRequestDto import com.droidblossom.archive.data.dto.friend.response.FriendReqStatusResponseDto import com.droidblossom.archive.data.dto.friend.response.FriendsPageResponseDto @@ -49,6 +50,11 @@ interface FriendService { @Query("created_at") createdAt : String, ) : Response> + @POST("friends/requests") + suspend fun postFriendListRequestsPageApi( + @Body request : FriendsReqRequestDto + ) : Response> + @DELETE("friends/{friend_id}") suspend fun deleteFriendApi( @Path("friend_id") friendId : Long, diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/FriendRepository.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/FriendRepository.kt index 3c04d094b..cc5b883dc 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/FriendRepository.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/FriendRepository.kt @@ -2,6 +2,7 @@ package com.droidblossom.archive.domain.repository import com.droidblossom.archive.data.dto.friend.request.FriendAcceptRequestDto import com.droidblossom.archive.data.dto.friend.request.FriendReqRequestDto +import com.droidblossom.archive.data.dto.friend.request.FriendsReqRequestDto import com.droidblossom.archive.data.dto.friend.request.FriendsSearchPhoneRequestDto import com.droidblossom.archive.data.dto.friend.request.FriendsSearchRequestDto import com.droidblossom.archive.domain.model.friend.FriendReqStatusResponse @@ -13,6 +14,7 @@ import com.droidblossom.archive.util.RetrofitResult interface FriendRepository { suspend fun postFriendsRequest(request: FriendReqRequestDto) : RetrofitResult + suspend fun postFriendsListRequest(request: FriendsReqRequestDto) : RetrofitResult suspend fun postFriendsAcceptRequest(request: FriendAcceptRequestDto) : RetrofitResult suspend fun postFriendsSearch(request: FriendsSearchRequestDto) : RetrofitResult suspend fun postFriendsSearchPhone(request : FriendsSearchPhoneRequestDto) : RetrofitResult diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendListRequestUseCase.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendListRequestUseCase.kt new file mode 100644 index 000000000..664fcf167 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendListRequestUseCase.kt @@ -0,0 +1,32 @@ +package com.droidblossom.archive.domain.usecase.friend + +import android.util.Log +import com.droidblossom.archive.data.dto.friend.request.FriendsReqRequestDto +import com.droidblossom.archive.domain.model.friend.FriendReqRequest +import com.droidblossom.archive.domain.repository.FriendRepository +import com.droidblossom.archive.util.onException +import com.droidblossom.archive.util.onFail +import com.droidblossom.archive.util.onSuccess +import kotlinx.coroutines.flow.flow +import javax.inject.Inject + +class FriendListRequestUseCase @Inject constructor( + private val repository: FriendRepository +) { + + suspend operator fun invoke(request : FriendsReqRequestDto) = + flow { + try { + emit(repository.postFriendsListRequest(request).onSuccess { + + }.onFail { + + }.onException { + throw Exception(it) + }) + }catch (e : Exception){ + Log.d("예외확인", "$e") + e.printStackTrace() + } + } +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt index 18a95d09b..de9dba2b2 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt @@ -1,13 +1,12 @@ package com.droidblossom.archive.presentation.ui.mypage.friend.addfriend -import android.text.TextUtils.substring import androidx.lifecycle.viewModelScope -import com.droidblossom.archive.R import com.droidblossom.archive.data.dto.friend.request.PhoneBooks import com.droidblossom.archive.domain.model.friend.FriendReqRequest import com.droidblossom.archive.domain.model.friend.FriendsSearchPhoneRequest import com.droidblossom.archive.domain.model.friend.FriendsSearchRequest import com.droidblossom.archive.domain.model.friend.FriendsSearchResponse +import com.droidblossom.archive.domain.usecase.friend.FriendListRequestUseCase import com.droidblossom.archive.domain.usecase.friend.FriendsRequestUseCase import com.droidblossom.archive.domain.usecase.friend.FriendsSearchPhoneUseCase import com.droidblossom.archive.domain.usecase.friend.FriendsSearchUseCase @@ -20,7 +19,6 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import javax.inject.Inject @@ -28,6 +26,7 @@ import javax.inject.Inject class AddFriendViewModelImpl @Inject constructor( private val friendsSearchUseCase: FriendsSearchUseCase, private val friendsRequestUseCase: FriendsRequestUseCase, + private val friendListRequestUseCase: FriendListRequestUseCase, private val friendsSearchPhoneUseCase: FriendsSearchPhoneUseCase ) : BaseViewModel(), AddFriendViewModel { From 80cf2778569958403f2e85151af647c4db4fbe6b Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Fri, 26 Apr 2024 19:24:43 +0900 Subject: [PATCH 206/301] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC=EC=97=91?= =?UTF-8?q?=ED=8B=B0=20=EC=B2=98=EC=9D=8C=EC=97=90=20=EA=B7=B8=EB=A3=B9/?= =?UTF-8?q?=20=EC=B9=9C=EA=B5=AC=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../home/notification/NotificationActivity.kt | 9 +++++-- .../ui/mypage/friend/FriendActivity.kt | 27 ++++++++++++++++--- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationActivity.kt index f9d8e6d2d..20f23b48d 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationActivity.kt @@ -15,6 +15,8 @@ import com.droidblossom.archive.databinding.ActivityNotificationBinding import com.droidblossom.archive.domain.model.member.NotiCategoryName import com.droidblossom.archive.presentation.base.BaseActivity import com.droidblossom.archive.presentation.ui.home.notification.adapter.NotificationRVA +import com.droidblossom.archive.presentation.ui.mypage.friend.FriendActivity +import com.droidblossom.archive.presentation.ui.mypage.friend.FriendActivity.Companion.FRIEND import com.droidblossom.archive.presentation.ui.mypage.friendaccept.FriendAcceptActivity import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch @@ -37,9 +39,12 @@ class NotificationActivity : startActivity(FriendAcceptActivity.newIntent(this)) } - NotiCategoryName.FRIEND_ACCEPT, - NotiCategoryName.GROUP_ACCEPT -> { + NotiCategoryName.FRIEND_ACCEPT -> { + startActivity(FriendActivity.newIntent(this, FriendActivity.FRIEND)) + } + NotiCategoryName.GROUP_ACCEPT -> { + startActivity(FriendActivity.newIntent(this,FriendActivity.GROUP)) } else ->{} diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendActivity.kt index c4be4f59f..4f3ac54b2 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendActivity.kt @@ -13,6 +13,8 @@ import com.droidblossom.archive.presentation.ui.mypage.friend.addfriend.AddFrien import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayout.OnTabSelectedListener import com.google.android.material.tabs.TabLayoutMediator +import com.kakao.sdk.common.KakaoSdk +import com.kakao.sdk.common.KakaoSdk.type import dagger.hilt.android.AndroidEntryPoint @@ -32,7 +34,7 @@ class FriendActivity : viewModel.getFriendList() } - private fun initView(){ + private fun initView() { val layoutParams = binding.closeBtn.layoutParams as ViewGroup.MarginLayoutParams layoutParams.topMargin += getStatusBarHeight() binding.closeBtn.layoutParams = layoutParams @@ -53,23 +55,40 @@ class FriendActivity : binding.tab.addOnTabSelectedListener(object : OnTabSelectedListener { override fun onTabSelected(tab: TabLayout.Tab) { - if (tab.position == 0){ + if (tab.position == 0) { binding.addT.text = "그룹 추가" binding.addCV.setOnClickListener { } } else { binding.addT.text = "친구 추가" - binding.addCV.setOnClickListener{ + binding.addCV.setOnClickListener { startActivity(AddFriendActivity.newIntent(this@FriendActivity)) } } } + override fun onTabUnselected(tab: TabLayout.Tab) {} override fun onTabReselected(tab: TabLayout.Tab) {} }) - } + intent.getStringExtra(TYPE_KEY)?.let { type -> + when (type) { + GROUP -> { + binding.tab.getTabAt(0)?.select() + } + + FRIEND -> { + binding.tab.getTabAt(1)?.select() + } + + else -> { + binding.tab.getTabAt(0)?.select() + } + + } + } + } override fun observeData() { } From 3679823a867058e0813ef08579cbee28de5f0ff5 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Fri, 26 Apr 2024 21:37:55 +0900 Subject: [PATCH 207/301] =?UTF-8?q?feat:=20=EC=9A=94=EC=B2=AD=20vpa=20?= =?UTF-8?q?=EC=9E=91=EC=97=85=20+=20=EB=AA=87=EC=9D=BC=EC=A0=84=20?= =?UTF-8?q?=EB=82=A0=EC=A7=9C=20=EA=B3=84=EC=82=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../home/notification/NotificationActivity.kt | 7 ++- .../friendaccept/FriendAcceptActivity.kt | 32 +++++++------ .../friendaccept/adapter/FriendAcceptVPA.kt | 21 +++++++++ .../friendaccept/page/FriendAcceptFragment.kt | 47 +++++++++++++++++++ .../friendaccept/page/GroupAcceptFragment.kt | 47 +++++++++++++++++++ .../droidblossom/archive/util/DateUtils.kt | 12 ++++- 6 files changed, 149 insertions(+), 17 deletions(-) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/adapter/FriendAcceptVPA.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/page/FriendAcceptFragment.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/page/GroupAcceptFragment.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationActivity.kt index 20f23b48d..364cabc6d 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationActivity.kt @@ -34,9 +34,12 @@ class NotificationActivity : } - NotiCategoryName.FRIEND_REQUEST, + NotiCategoryName.FRIEND_REQUEST -> { + startActivity(FriendAcceptActivity.newIntent(this, FriendAcceptActivity.FRIEND)) + } + NotiCategoryName.GROUP_REQUEST -> { - startActivity(FriendAcceptActivity.newIntent(this)) + startActivity(FriendAcceptActivity.newIntent(this, FriendAcceptActivity.GROUP)) } NotiCategoryName.FRIEND_ACCEPT -> { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptActivity.kt index 02833ae5d..4d3e722ea 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptActivity.kt @@ -8,7 +8,7 @@ import androidx.activity.viewModels import com.droidblossom.archive.R import com.droidblossom.archive.databinding.ActivityFriendAcceptBinding import com.droidblossom.archive.presentation.base.BaseActivity -import com.droidblossom.archive.presentation.ui.mypage.friend.adapter.FriendVPA +import com.droidblossom.archive.presentation.ui.mypage.friendaccept.adapter.FriendAcceptVPA import com.google.android.material.tabs.TabLayoutMediator import dagger.hilt.android.AndroidEntryPoint @@ -18,9 +18,9 @@ class FriendAcceptActivity : BaseActivity(R.layout.activity_friend_accept) { override val viewModel: FriendAcceptViewModelImpl by viewModels() -// private val friendVPA by lazy { -// FriendVPA(this) -// } + private val friendAcceptVPA by lazy { + FriendAcceptVPA(this) + } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -33,19 +33,19 @@ class FriendAcceptActivity : layoutParams.topMargin += getStatusBarHeight() binding.closeBtn.layoutParams = layoutParams -// binding.vp.adapter = friendVPA + binding.vp.adapter = friendAcceptVPA binding.closeBtn.setOnClickListener { finish() } -// TabLayoutMediator(binding.tab, binding.vp) { tab, position -> -// tab.text = when (position) { -// 0 -> getString(R.string.groupAccept) -// 1 -> getString(R.string.friendAccept) -// else -> null -// } -// }.attach() + TabLayoutMediator(binding.tab, binding.vp) { tab, position -> + tab.text = when (position) { + 0 -> getString(R.string.groupAccept) + 1 -> getString(R.string.friendAccept) + else -> null + } + }.attach() } override fun observeData() { @@ -53,9 +53,13 @@ class FriendAcceptActivity : } companion object { + const val FRIEND = "friend" + const val GROUP = "group" const val FRIENDACCEPT = "friend_accept" - fun newIntent(context: Context) = - Intent(context, FriendAcceptActivity::class.java) + fun newIntent(context: Context, type : String) = + Intent(context, FriendAcceptActivity::class.java).apply { + putExtra(FRIENDACCEPT, type) + } } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/adapter/FriendAcceptVPA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/adapter/FriendAcceptVPA.kt new file mode 100644 index 000000000..54153d4c6 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/adapter/FriendAcceptVPA.kt @@ -0,0 +1,21 @@ +package com.droidblossom.archive.presentation.ui.mypage.friendaccept.adapter + +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentActivity +import androidx.viewpager2.adapter.FragmentStateAdapter +import com.droidblossom.archive.presentation.ui.mypage.friend.page.FriendListFragment +import com.droidblossom.archive.presentation.ui.mypage.friendaccept.page.GroupAcceptFragment + +class FriendAcceptVPA(activity: FragmentActivity) : FragmentStateAdapter(activity){ + + private val fragmentList = listOf( + GroupAcceptFragment(), + FriendListFragment() + ) + + override fun getItemCount(): Int = fragmentList.size + + override fun createFragment(position: Int): Fragment { + return fragmentList[position] + } +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/page/FriendAcceptFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/page/FriendAcceptFragment.kt new file mode 100644 index 000000000..048f06835 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/page/FriendAcceptFragment.kt @@ -0,0 +1,47 @@ +package com.droidblossom.archive.presentation.ui.mypage.friendaccept.page + +import android.os.Bundle +import android.view.View +import androidx.fragment.app.activityViewModels +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import com.droidblossom.archive.R +import com.droidblossom.archive.databinding.FragmentAcceptBinding +import com.droidblossom.archive.presentation.base.BaseFragment +import com.droidblossom.archive.presentation.ui.mypage.friend.FriendViewModel +import com.droidblossom.archive.presentation.ui.mypage.friendaccept.FriendAcceptViewModelImpl +import kotlinx.coroutines.launch + +class FriendAcceptFragment : + BaseFragment(R.layout.fragment_accept) { + + override val viewModel: FriendAcceptViewModelImpl by activityViewModels() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.vm = viewModel + initView() + } + + private fun initView() { + + } + + override fun observeData() { + + viewLifecycleOwner.lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.friendAcceptEvent.collect{ event -> + when(event){ + is FriendViewModel.FriendEvent.ShowToastMessage -> { + showToastMessage(event.message) + } + + else -> {} + } + } + } + } + } +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/page/GroupAcceptFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/page/GroupAcceptFragment.kt new file mode 100644 index 000000000..dc2649d41 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/page/GroupAcceptFragment.kt @@ -0,0 +1,47 @@ +package com.droidblossom.archive.presentation.ui.mypage.friendaccept.page + +import android.os.Bundle +import android.view.View +import androidx.fragment.app.activityViewModels +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import com.droidblossom.archive.R +import com.droidblossom.archive.databinding.FragmentAcceptBinding +import com.droidblossom.archive.presentation.base.BaseFragment +import com.droidblossom.archive.presentation.ui.mypage.friend.FriendViewModel +import com.droidblossom.archive.presentation.ui.mypage.friendaccept.FriendAcceptViewModelImpl +import kotlinx.coroutines.launch + +class GroupAcceptFragment : + BaseFragment(R.layout.fragment_accept) { + + override val viewModel: FriendAcceptViewModelImpl by activityViewModels() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.vm = viewModel + initView() + } + + private fun initView() { + + } + + override fun observeData() { + + viewLifecycleOwner.lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.friendAcceptEvent.collect{ event -> + when(event){ + is FriendViewModel.FriendEvent.ShowToastMessage -> { + showToastMessage(event.message) + } + + else -> {} + } + } + } + } + } +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/DateUtils.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/DateUtils.kt index e014994b3..052e1291b 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/DateUtils.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/DateUtils.kt @@ -1,5 +1,6 @@ package com.droidblossom.archive.util +import android.util.Log import java.text.ParseException import java.text.SimpleDateFormat import java.util.Calendar @@ -148,7 +149,16 @@ object DateUtils { @JvmStatic fun main(args: Array) { - println(getAfterYears(1, "yyyyMMdd")) + println( calcLastDate("2024-01-22T11:36:57.593Z")) + } + + fun calcLastDate(date: String): String { + val currentDate = Date(System.currentTimeMillis()) + val date = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX").parse(date) + + val difference = currentDate.time - date.time + val daysDifference = difference / (24 * 60 * 60 * 1000) + return if (daysDifference == 0.toLong()) "오늘" else "${daysDifference}일전" } fun getAfterSeconds(date1: Date, date2: Date): Int { From bd8a1f65a90baefc012bfd249d2a6bd125120473 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Fri, 26 Apr 2024 22:15:13 +0900 Subject: [PATCH 208/301] =?UTF-8?q?design=20:=20=EC=9A=94=EC=B2=AD=20?= =?UTF-8?q?=EC=95=84=EC=9D=B4=ED=85=9C=20&=20=EC=9A=94=EC=B2=AD=20?= =?UTF-8?q?=ED=94=84=EB=A0=88=EA=B7=B8=EB=A8=BC=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/util/BindingAdapter.kt | 5 + .../src/main/res/layout/fragment_accept.xml | 30 +++++ .../app/src/main/res/layout/item_accept.xml | 121 ++++++++++++++++++ 3 files changed, 156 insertions(+) create mode 100644 frontend/ARchive/app/src/main/res/layout/fragment_accept.xml create mode 100644 frontend/ARchive/app/src/main/res/layout/item_accept.xml diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/BindingAdapter.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/BindingAdapter.kt index 757f1cf2b..4d8f32ffd 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/BindingAdapter.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/BindingAdapter.kt @@ -98,6 +98,11 @@ fun TextView.displayRemainingTime(totalSeconds: Int) { this.text = String.format("%02d분 %02d초", minutes, seconds) } +@BindingAdapter("bind:culcLastDay") +fun TextView.culcLastDays(date : String){ + this.text = DateUtils.calcLastDate(date) +} + @BindingAdapter("bind:animateFAB") fun CardView.animateFAB(y : Float){ ObjectAnimator.ofFloat(this, "translationY", y).apply { start() } diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_accept.xml b/frontend/ARchive/app/src/main/res/layout/fragment_accept.xml new file mode 100644 index 000000000..d8f445394 --- /dev/null +++ b/frontend/ARchive/app/src/main/res/layout/fragment_accept.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/item_accept.xml b/frontend/ARchive/app/src/main/res/layout/item_accept.xml new file mode 100644 index 000000000..32e6b30b0 --- /dev/null +++ b/frontend/ARchive/app/src/main/res/layout/item_accept.xml @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From d07ef32adae931eda388759a4c592935dc02672f Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Fri, 26 Apr 2024 23:19:22 +0900 Subject: [PATCH 209/301] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC=20=EC=9A=94?= =?UTF-8?q?=EC=B2=AD=20RVA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../friendaccept/FriendAcceptActivity.kt | 20 ++++++ .../friendaccept/adapter/FriendAcceptRVA.kt | 65 +++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/adapter/FriendAcceptRVA.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptActivity.kt index 4d3e722ea..b329790c4 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptActivity.kt @@ -8,6 +8,7 @@ import androidx.activity.viewModels import com.droidblossom.archive.R import com.droidblossom.archive.databinding.ActivityFriendAcceptBinding import com.droidblossom.archive.presentation.base.BaseActivity +import com.droidblossom.archive.presentation.ui.mypage.friend.FriendActivity import com.droidblossom.archive.presentation.ui.mypage.friendaccept.adapter.FriendAcceptVPA import com.google.android.material.tabs.TabLayoutMediator import dagger.hilt.android.AndroidEntryPoint @@ -26,6 +27,7 @@ class FriendAcceptActivity : super.onCreate(savedInstanceState) initView() + viewModel.getFriendAcceptList() } private fun initView(){ @@ -46,6 +48,24 @@ class FriendAcceptActivity : else -> null } }.attach() + + + intent.getStringExtra(FriendActivity.TYPE_KEY)?.let { type -> + when (type) { + FriendActivity.GROUP -> { + binding.tab.getTabAt(0)?.select() + } + + FriendActivity.FRIEND -> { + binding.tab.getTabAt(1)?.select() + } + + else -> { + binding.tab.getTabAt(0)?.select() + } + + } + } } override fun observeData() { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/adapter/FriendAcceptRVA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/adapter/FriendAcceptRVA.kt new file mode 100644 index 000000000..586adba28 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/adapter/FriendAcceptRVA.kt @@ -0,0 +1,65 @@ +package com.droidblossom.archive.presentation.ui.mypage.friendaccept.adapter + +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.droidblossom.archive.databinding.ItemAcceptBinding +import com.droidblossom.archive.domain.model.friend.Friend + + +class FriendAcceptRVA( + private val onDeny : (Friend) -> Unit, + private val onAccept : (Friend) -> Unit +) : + ListAdapter(differ) { + + + inner class ItemViewHolder( + private val binding: ItemAcceptBinding + ) : RecyclerView.ViewHolder(binding.root) { + + fun bind(data: Friend) { + binding.item = data + binding.acceptBtn.setOnClickListener { + onAccept(data) + } + binding.denyBtn.setOnClickListener { + onDeny(data) + } + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder { + return ItemViewHolder( + ItemAcceptBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) + } + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + holder.bind(getItem(position)) + } + + companion object { + val differ = object : DiffUtil.ItemCallback() { + override fun areItemsTheSame( + oldItem: Friend, + newItem: Friend + ): Boolean { + return oldItem.id == newItem.id + } + + override fun areContentsTheSame( + oldItem: Friend, + newItem: Friend + ): Boolean { + return oldItem == newItem + } + } + } +} \ No newline at end of file From 155617765507cc43308564fce3d54e4140b55295 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Fri, 26 Apr 2024 23:58:24 +0900 Subject: [PATCH 210/301] =?UTF-8?q?feat:=20=EC=9A=94=EC=B2=AD=20=EB=A6=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EB=B6=88=EB=9F=AC=EC=98=A4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../friendaccept/FriendAcceptViewModel.kt | 10 ++- .../friendaccept/FriendAcceptViewModelImpl.kt | 85 +++++++++++++++++++ .../friendaccept/page/FriendAcceptFragment.kt | 46 +++++++++- .../app/src/main/res/layout/item_accept.xml | 2 +- 4 files changed, 136 insertions(+), 7 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptViewModel.kt index 68efca7b9..02cba3fb1 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptViewModel.kt @@ -1,14 +1,20 @@ package com.droidblossom.archive.presentation.ui.mypage.friendaccept +import com.droidblossom.archive.domain.model.friend.Friend import com.droidblossom.archive.presentation.ui.mypage.friend.FriendViewModel import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.StateFlow interface FriendAcceptViewModel { - val friendAcceptEvent : SharedFlow + val friendAcceptEvent: SharedFlow + val friendAcceptList: StateFlow> + fun getFriendAcceptList() + fun denyRequest(friend: Friend) + fun acceptRequest(friend: Friend) sealed class FriendAcceptEvent { - data class ShowToastMessage(val message : String) : FriendAcceptEvent() + data class ShowToastMessage(val message: String) : FriendAcceptEvent() } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptViewModelImpl.kt index e62b3c11f..6e3cbf913 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptViewModelImpl.kt @@ -1,20 +1,105 @@ package com.droidblossom.archive.presentation.ui.mypage.friendaccept +import androidx.lifecycle.viewModelScope +import com.droidblossom.archive.domain.model.friend.Friend +import com.droidblossom.archive.domain.model.friend.FriendAcceptRequest +import com.droidblossom.archive.domain.usecase.friend.FriendDenyRequestUseCase +import com.droidblossom.archive.domain.usecase.friend.FriendsAcceptRequestUseCase +import com.droidblossom.archive.domain.usecase.friend.FriendsRequestsPageUseCase import com.droidblossom.archive.presentation.base.BaseViewModel import com.droidblossom.archive.presentation.ui.mypage.friend.FriendViewModel +import com.droidblossom.archive.util.DateUtils +import com.droidblossom.archive.util.onFail +import com.droidblossom.archive.util.onSuccess import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel class FriendAcceptViewModelImpl @Inject constructor( + private val friendsRequestsPageUseCase: FriendsRequestsPageUseCase, + private val denyRequestUseCase: FriendDenyRequestUseCase, + private val acceptRequestUseCase: FriendsAcceptRequestUseCase ) : BaseViewModel(), FriendAcceptViewModel { private val _friendAcceptEvent = MutableSharedFlow() override val friendAcceptEvent: SharedFlow get() = _friendAcceptEvent.asSharedFlow() + private val _friendAcceptList = MutableStateFlow>(listOf()) + override val friendAcceptList: StateFlow> + get() = _friendAcceptList + + private val friendHasNextPage = MutableStateFlow(true) + private val friendLastCreatedTime = MutableStateFlow(DateUtils.dataServerString) + + + override fun getFriendAcceptList() { + viewModelScope.launch { + if (friendHasNextPage.value) { + friendsRequestsPageUseCase(15, friendLastCreatedTime.value).collect { result -> + result.onSuccess { + friendHasNextPage.value = it.hasNext + _friendAcceptList.emit(friendAcceptList.value + it.friends) + friendLastCreatedTime.value = it.friends.last().createdAt + }.onFail { + _friendAcceptEvent.emit( + FriendViewModel.FriendEvent.ShowToastMessage( + "친구 리스트 불러오기 실패. 잠시후 시도해 주세요" + ) + ) + } + } + } + } + } + + override fun denyRequest(friend: Friend) { + viewModelScope.launch { + denyRequestUseCase(friend.id).collect { result -> + result.onSuccess { + removeItem(friend) + }.onFail { + _friendAcceptEvent.emit( + FriendViewModel.FriendEvent.ShowToastMessage( + "요청 실패. 잠시후 시도해 주세요" + ) + ) + } + + } + } + } + + override fun acceptRequest(friend: Friend) { + viewModelScope.launch { + acceptRequestUseCase(FriendAcceptRequest(friend.id)).collect { result -> + result.onSuccess { + removeItem(friend) + }.onFail { + _friendAcceptEvent.emit( + FriendViewModel.FriendEvent.ShowToastMessage( + "요청 실패. 잠시후 시도해 주세요" + ) + ) + } + + } + } + } + + fun removeItem(friend: Friend) { + viewModelScope.launch { + val newList = _friendAcceptList.value.toMutableList() + newList.remove(friend) + _friendAcceptList.emit(newList) + } + } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/page/FriendAcceptFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/page/FriendAcceptFragment.kt index 048f06835..8784e22de 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/page/FriendAcceptFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/page/FriendAcceptFragment.kt @@ -6,11 +6,14 @@ import androidx.fragment.app.activityViewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView import com.droidblossom.archive.R import com.droidblossom.archive.databinding.FragmentAcceptBinding import com.droidblossom.archive.presentation.base.BaseFragment import com.droidblossom.archive.presentation.ui.mypage.friend.FriendViewModel import com.droidblossom.archive.presentation.ui.mypage.friendaccept.FriendAcceptViewModelImpl +import com.droidblossom.archive.presentation.ui.mypage.friendaccept.adapter.FriendAcceptRVA import kotlinx.coroutines.launch class FriendAcceptFragment : @@ -18,22 +21,49 @@ class FriendAcceptFragment : override val viewModel: FriendAcceptViewModelImpl by activityViewModels() + private val friendAcceptRVA by lazy { + FriendAcceptRVA( + { denyFriend -> + viewModel.denyRequest(denyFriend) + }, + { acceptFriend -> + viewModel.acceptRequest(acceptFriend) + } + ) + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding.vm = viewModel - initView() + initRV() } - private fun initView() { + private fun initRV() { + binding.rv.adapter = friendAcceptRVA + + binding.rv.addOnScrollListener(object : RecyclerView.OnScrollListener() { + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + super.onScrollStateChanged(recyclerView, newState) + if (newState == RecyclerView.SCROLL_STATE_IDLE || newState == RecyclerView.SCROLL_STATE_DRAGGING) { + val layoutManager = recyclerView.layoutManager as LinearLayoutManager + val totalItemCount = layoutManager.itemCount + val lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition() + + if (totalItemCount - lastVisibleItemPosition <= 1) { + viewModel.getFriendAcceptList() + } + } + } + }) } override fun observeData() { viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { - viewModel.friendAcceptEvent.collect{ event -> - when(event){ + viewModel.friendAcceptEvent.collect { event -> + when (event) { is FriendViewModel.FriendEvent.ShowToastMessage -> { showToastMessage(event.message) } @@ -43,5 +73,13 @@ class FriendAcceptFragment : } } } + + viewLifecycleOwner.lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.friendAcceptList.collect { friendAccepts -> + friendAcceptRVA.submitList(friendAccepts) + } + } + } } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/item_accept.xml b/frontend/ARchive/app/src/main/res/layout/item_accept.xml index 32e6b30b0..32e9abf9c 100644 --- a/frontend/ARchive/app/src/main/res/layout/item_accept.xml +++ b/frontend/ARchive/app/src/main/res/layout/item_accept.xml @@ -69,7 +69,7 @@ android:layout_marginEnd="16dp" android:gravity="start" tools:text="1일전" - bind:culcLastDay="@{item.createdAt}" + bind:culcLastDays="@{item.createdAt}" android:textAppearance="@style/TextAppearance.App.caption2" android:textColor="@color/gray_600" app:layout_constraintBottom_toBottomOf="@id/nameT" From 0f6a17770a7ace322bef8bf4501871e57c3edc19 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Fri, 26 Apr 2024 23:58:43 +0900 Subject: [PATCH 211/301] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC=20=EC=88=98?= =?UTF-8?q?=EB=9D=BD=20&=20=EA=B1=B0=EC=A0=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/presentation/ui/mypage/MyPageFragment.kt | 3 ++- .../ui/mypage/friendaccept/adapter/FriendAcceptRVA.kt | 4 ++-- .../ui/mypage/friendaccept/adapter/FriendAcceptVPA.kt | 4 ++-- .../ui/mypage/friendaccept/page/GroupAcceptFragment.kt | 4 ++-- .../java/com/droidblossom/archive/util/BindingAdapter.kt | 2 +- .../main/java/com/droidblossom/archive/util/DateUtils.kt | 9 ++++++--- .../ARchive/app/src/main/res/layout/fragment_accept.xml | 9 +++++---- 7 files changed, 20 insertions(+), 15 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt index b1b36ce0b..8d40192da 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt @@ -55,7 +55,8 @@ class MyPageFragment : } private val spinnerAdapter by lazy { - val capsuleTypeList = arrayOf(SpinnerCapsuleType.SECRET, SpinnerCapsuleType.PUBLIC, SpinnerCapsuleType.GROUP) + val capsuleTypeList = + arrayOf(SpinnerCapsuleType.SECRET, SpinnerCapsuleType.PUBLIC, SpinnerCapsuleType.GROUP) CapsuleTypeSpinner(requireContext(), capsuleTypeList) } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/adapter/FriendAcceptRVA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/adapter/FriendAcceptRVA.kt index 586adba28..c22247a74 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/adapter/FriendAcceptRVA.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/adapter/FriendAcceptRVA.kt @@ -10,8 +10,8 @@ import com.droidblossom.archive.domain.model.friend.Friend class FriendAcceptRVA( - private val onDeny : (Friend) -> Unit, - private val onAccept : (Friend) -> Unit + private val onDeny: (Friend) -> Unit, + private val onAccept: (Friend) -> Unit ) : ListAdapter(differ) { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/adapter/FriendAcceptVPA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/adapter/FriendAcceptVPA.kt index 54153d4c6..eeedf1028 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/adapter/FriendAcceptVPA.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/adapter/FriendAcceptVPA.kt @@ -3,14 +3,14 @@ package com.droidblossom.archive.presentation.ui.mypage.friendaccept.adapter import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity import androidx.viewpager2.adapter.FragmentStateAdapter -import com.droidblossom.archive.presentation.ui.mypage.friend.page.FriendListFragment +import com.droidblossom.archive.presentation.ui.mypage.friendaccept.page.FriendAcceptFragment import com.droidblossom.archive.presentation.ui.mypage.friendaccept.page.GroupAcceptFragment class FriendAcceptVPA(activity: FragmentActivity) : FragmentStateAdapter(activity){ private val fragmentList = listOf( GroupAcceptFragment(), - FriendListFragment() + FriendAcceptFragment() ) override fun getItemCount(): Int = fragmentList.size diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/page/GroupAcceptFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/page/GroupAcceptFragment.kt index dc2649d41..4c0e5c00d 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/page/GroupAcceptFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/page/GroupAcceptFragment.kt @@ -32,8 +32,8 @@ class GroupAcceptFragment : viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { - viewModel.friendAcceptEvent.collect{ event -> - when(event){ + viewModel.friendAcceptEvent.collect { event -> + when (event) { is FriendViewModel.FriendEvent.ShowToastMessage -> { showToastMessage(event.message) } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/BindingAdapter.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/BindingAdapter.kt index 4d8f32ffd..966ee760a 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/BindingAdapter.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/BindingAdapter.kt @@ -98,7 +98,7 @@ fun TextView.displayRemainingTime(totalSeconds: Int) { this.text = String.format("%02d분 %02d초", minutes, seconds) } -@BindingAdapter("bind:culcLastDay") +@BindingAdapter("bind:culcLastDays") fun TextView.culcLastDays(date : String){ this.text = DateUtils.calcLastDate(date) } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/DateUtils.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/DateUtils.kt index 052e1291b..675c4630e 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/DateUtils.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/DateUtils.kt @@ -1,5 +1,6 @@ package com.droidblossom.archive.util +import android.annotation.SuppressLint import android.util.Log import java.text.ParseException import java.text.SimpleDateFormat @@ -149,12 +150,14 @@ object DateUtils { @JvmStatic fun main(args: Array) { - println( calcLastDate("2024-01-22T11:36:57.593Z")) + } + @SuppressLint("SimpleDateFormat") fun calcLastDate(date: String): String { - val currentDate = Date(System.currentTimeMillis()) - val date = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX").parse(date) + val format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss") + val date = format.parse(date) + val currentDate = format.parse(dataServerString) val difference = currentDate.time - date.time val daysDifference = difference / (24 * 60 * 60 * 1000) diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_accept.xml b/frontend/ARchive/app/src/main/res/layout/fragment_accept.xml index d8f445394..165621392 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_accept.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_accept.xml @@ -12,8 +12,8 @@ + android:layout_height="match_parent" + android:background="@color/main_bg_1"> + app:layout_constraintTop_toTopOf="parent" + tools:listitem="@layout/item_accept" /> \ No newline at end of file From 190cc263f95eedcce96251392ed153151a587939 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Sat, 27 Apr 2024 00:04:52 +0900 Subject: [PATCH 212/301] =?UTF-8?q?chore:=20=EC=9E=90=EC=9E=98=ED=95=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/presentation/ui/mypage/MyPageFragment.kt | 12 ++++++++++++ .../ui/mypage/friendaccept/FriendAcceptActivity.kt | 4 ++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt index 8d40192da..c7e9f1dc3 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt @@ -21,6 +21,7 @@ import com.droidblossom.archive.presentation.ui.home.dialog.CapsulePreviewDialog import com.droidblossom.archive.presentation.ui.mypage.adapter.CapsuleTypeSpinner import com.droidblossom.archive.presentation.ui.mypage.adapter.MyCapsuleRVA import com.droidblossom.archive.presentation.ui.mypage.friend.FriendActivity +import com.droidblossom.archive.presentation.ui.mypage.friendaccept.FriendAcceptActivity import com.droidblossom.archive.presentation.ui.mypage.setting.SettingActivity import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch @@ -105,6 +106,17 @@ class MyPageFragment : true } } + + binding.groupLayout.setOnClickListener { + startActivity(FriendActivity.newIntent(requireContext(), FriendActivity.GROUP)) + } + binding.friendLayout.setOnClickListener { + startActivity(FriendActivity.newIntent(requireContext(), FriendActivity.FRIEND)) + } + + binding.requestLayout.setOnClickListener { + startActivity(FriendAcceptActivity.newIntent(requireContext(), FriendAcceptActivity.FRIEND)) + } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptActivity.kt index b329790c4..3adf7012f 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptActivity.kt @@ -30,7 +30,7 @@ class FriendAcceptActivity : viewModel.getFriendAcceptList() } - private fun initView(){ + private fun initView() { val layoutParams = binding.closeBtn.layoutParams as ViewGroup.MarginLayoutParams layoutParams.topMargin += getStatusBarHeight() binding.closeBtn.layoutParams = layoutParams @@ -77,7 +77,7 @@ class FriendAcceptActivity : const val GROUP = "group" const val FRIENDACCEPT = "friend_accept" - fun newIntent(context: Context, type : String) = + fun newIntent(context: Context, type: String) = Intent(context, FriendAcceptActivity::class.java).apply { putExtra(FRIENDACCEPT, type) } From 4cfaa7f5068a1a37ba578a3be592acccc2c814ec Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Sat, 27 Apr 2024 00:12:08 +0900 Subject: [PATCH 213/301] chore : 2 --- .../ui/home/notification/NotificationActivity.kt | 4 ++-- .../presentation/ui/mypage/friend/FriendActivity.kt | 1 + .../presentation/ui/mypage/friend/FriendViewModel.kt | 10 +++++----- .../ui/mypage/friend/page/FriendListFragment.kt | 4 ++-- .../ui/mypage/friend/page/GroupListFragment.kt | 4 ++-- .../ui/mypage/friendaccept/FriendAcceptActivity.kt | 6 +++--- .../ui/mypage/friendaccept/adapter/FriendAcceptVPA.kt | 2 +- .../ARchive/app/src/main/res/layout/item_accept.xml | 8 ++++---- 8 files changed, 20 insertions(+), 19 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationActivity.kt index 364cabc6d..085f5e400 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationActivity.kt @@ -47,10 +47,10 @@ class NotificationActivity : } NotiCategoryName.GROUP_ACCEPT -> { - startActivity(FriendActivity.newIntent(this,FriendActivity.GROUP)) + startActivity(FriendActivity.newIntent(this, FriendActivity.GROUP)) } - else ->{} + else -> {} } } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendActivity.kt index 4f3ac54b2..55192e4e5 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendActivity.kt @@ -89,6 +89,7 @@ class FriendActivity : } } } + override fun observeData() { } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModel.kt index 3e427ea6c..f5ad0d9eb 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModel.kt @@ -8,15 +8,15 @@ import kotlinx.coroutines.flow.StateFlow interface FriendViewModel { - val friendEvent : SharedFlow + val friendEvent: SharedFlow //friend - val isFriendSearchOpen : StateFlow + val isFriendSearchOpen: StateFlow val friendList: StateFlow> val friendListUI: StateFlow> //group - val isGroupSearchOpen : StateFlow + val isGroupSearchOpen: StateFlow //friend fun openSearchFriend() @@ -24,7 +24,7 @@ interface FriendViewModel { fun searchFriend() fun getFriendList() fun changeDeleteOpen(previousPosition: Int?, currentPosition: Int) - fun deleteFriend(friend : Friend) + fun deleteFriend(friend: Friend) //group fun openSearchGroup() @@ -32,6 +32,6 @@ interface FriendViewModel { fun searchGroup() sealed class FriendEvent { - data class ShowToastMessage(val message : String) : FriendViewModel.FriendEvent() + data class ShowToastMessage(val message: String) : FriendViewModel.FriendEvent() } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/FriendListFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/FriendListFragment.kt index 79ff35857..9cc8810b0 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/FriendListFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/FriendListFragment.kt @@ -68,8 +68,8 @@ class FriendListFragment : viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { - viewModel.friendEvent.collect{ event -> - when(event){ + viewModel.friendEvent.collect { event -> + when (event) { is FriendViewModel.FriendEvent.ShowToastMessage -> { showToastMessage(event.message) } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/GroupListFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/GroupListFragment.kt index 78517e7a6..b710b62bb 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/GroupListFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/GroupListFragment.kt @@ -39,8 +39,8 @@ class GroupListFragment : viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { - viewModel.friendEvent.collect{ event -> - when(event){ + viewModel.friendEvent.collect { event -> + when (event) { is FriendViewModel.FriendEvent.ShowToastMessage -> { showToastMessage(event.message) } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptActivity.kt index 3adf7012f..dca576317 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptActivity.kt @@ -50,13 +50,13 @@ class FriendAcceptActivity : }.attach() - intent.getStringExtra(FriendActivity.TYPE_KEY)?.let { type -> + intent.getStringExtra(FRIENDACCEPT)?.let { type -> when (type) { - FriendActivity.GROUP -> { + GROUP -> { binding.tab.getTabAt(0)?.select() } - FriendActivity.FRIEND -> { + FRIEND -> { binding.tab.getTabAt(1)?.select() } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/adapter/FriendAcceptVPA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/adapter/FriendAcceptVPA.kt index eeedf1028..7d2eb305c 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/adapter/FriendAcceptVPA.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/adapter/FriendAcceptVPA.kt @@ -6,7 +6,7 @@ import androidx.viewpager2.adapter.FragmentStateAdapter import com.droidblossom.archive.presentation.ui.mypage.friendaccept.page.FriendAcceptFragment import com.droidblossom.archive.presentation.ui.mypage.friendaccept.page.GroupAcceptFragment -class FriendAcceptVPA(activity: FragmentActivity) : FragmentStateAdapter(activity){ +class FriendAcceptVPA(activity: FragmentActivity) : FragmentStateAdapter(activity) { private val fragmentList = listOf( GroupAcceptFragment(), diff --git a/frontend/ARchive/app/src/main/res/layout/item_accept.xml b/frontend/ARchive/app/src/main/res/layout/item_accept.xml index 32e9abf9c..3340d40cd 100644 --- a/frontend/ARchive/app/src/main/res/layout/item_accept.xml +++ b/frontend/ARchive/app/src/main/res/layout/item_accept.xml @@ -18,7 +18,7 @@ + app:layout_constraintTop_toTopOf="@id/profileImg" + bind:culcLastDays="@{item.createdAt}" + tools:text="1일전" /> Date: Sat, 27 Apr 2024 23:15:57 +0900 Subject: [PATCH 214/301] =?UTF-8?q?feat:=20cluster=20text=20=EA=B0=92?= =?UTF-8?q?=EC=97=90=20=EB=94=B0=EB=9D=BC=20captionText=20=EC=A1=B0?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../droidblossom/archive/presentation/ui/home/HomeFragment.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt index 9932f4e79..f8339d4b5 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt @@ -56,7 +56,7 @@ class HomeFragment : BaseFragment(R.layo marker.icon = OverlayImage.fromResource(R.drawable.ic_cluster_marker_46) marker.captionColor = Color.BLACK marker.captionHaloColor = ContextCompat.getColor(requireContext(), R.color.main_bg_1) - marker.captionTextSize = 20f + marker.captionTextSize = if (info.size >= 100) 15f else 18f marker.onClickListener = Overlay.OnClickListener{ // 클러스터된 거 클릭이벤트 - 나중에 클러스터에 행당된 캡슐들 사이드에 보여주거나 하면 좋을듯? true From 41a82f6a55d77267def77e4b88b8d4f2cf197039 Mon Sep 17 00:00:00 2001 From: comst19 Date: Sun, 28 Apr 2024 00:00:56 +0900 Subject: [PATCH 215/301] =?UTF-8?q?feat:=20AddFriendActivity=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EB=8F=8C=EC=95=84=EC=98=A4=EB=A9=B4=20=EB=82=B4=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=EA=B0=B1=EC=8B=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/presentation/ui/mypage/MyPageFragment.kt | 8 ++++++++ .../archive/presentation/ui/mypage/MyPageViewModel.kt | 1 + .../archive/presentation/ui/mypage/MyPageViewModelImpl.kt | 2 ++ 3 files changed, 11 insertions(+) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt index c7e9f1dc3..0bf9ccb42 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt @@ -115,6 +115,7 @@ class MyPageFragment : } binding.requestLayout.setOnClickListener { + viewModel.reloadMyInfo = true startActivity(FriendAcceptActivity.newIntent(requireContext(), FriendAcceptActivity.FRIEND)) } } @@ -208,6 +209,13 @@ class MyPageFragment : } + override fun onResume() { + super.onResume() + if (viewModel.reloadMyInfo){ + viewModel.getMe() + } + } + override fun onHiddenChanged(hidden: Boolean) { super.onHiddenChanged(hidden) if (!hidden) { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModel.kt index db83a5f66..e95d7d387 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModel.kt @@ -13,6 +13,7 @@ interface MyPageViewModel { val myCapsulesUI : StateFlow> val hasNextPage : StateFlow val lastCreatedTime : StateFlow + var reloadMyInfo:Boolean fun getMe() fun getSecretCapsulePage() diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt index 38bc5fd46..89f471ca2 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt @@ -54,6 +54,7 @@ class MyPageViewModelImpl @Inject constructor( override val lastCreatedTime: StateFlow get() = _lastCreatedTime + override var reloadMyInfo = false init { load() @@ -70,6 +71,7 @@ class MyPageViewModelImpl @Inject constructor( memberUseCase().collect { result -> result.onSuccess { _myInfo.emit(it) + reloadMyInfo = false }.onFail { _myPageEvents.emit(MyPageViewModel.MyPageEvent.ShowToastMessage("정보 불러오기 실패")) } From ad4621518797415f2124056e7d90b48915687fdf Mon Sep 17 00:00:00 2001 From: comst19 Date: Mon, 29 Apr 2024 02:07:35 +0900 Subject: [PATCH 216/301] design: kakao login btn color --- frontend/ARchive/app/src/main/res/layout/fragment_sign_in.xml | 4 ++-- frontend/ARchive/app/src/main/res/values/colors.xml | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_sign_in.xml b/frontend/ARchive/app/src/main/res/layout/fragment_sign_in.xml index 1d587fb9b..bba54f643 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_sign_in.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_sign_in.xml @@ -76,9 +76,9 @@ app:layout_constraintBottom_toTopOf="@id/googleLoginBtn" android:text="@string/kakaoLogin" android:textAppearance="@style/TextAppearance.App.button" - android:textColor="@color/white" + android:textColor="@color/black" android:background="@drawable/rectangle_solid_corner_8dp" - android:backgroundTint="@color/main_1" + android:backgroundTint="@color/kakao" android:layout_marginBottom="@dimen/margin_small" android:layout_marginHorizontal="@dimen/margin"/> diff --git a/frontend/ARchive/app/src/main/res/values/colors.xml b/frontend/ARchive/app/src/main/res/values/colors.xml index 9cd32d4b7..6f4940ea0 100644 --- a/frontend/ARchive/app/src/main/res/values/colors.xml +++ b/frontend/ARchive/app/src/main/res/values/colors.xml @@ -20,6 +20,7 @@ #FF424242 #FF2E2E2E #FF1C1C1C + #FEE500 #324263F7 From f9e73f5fcecce08f75fb2fd74ffd5a4fca8af321 Mon Sep 17 00:00:00 2001 From: comst19 Date: Mon, 29 Apr 2024 21:21:08 +0900 Subject: [PATCH 217/301] =?UTF-8?q?perf:=20RecyclerView=20=EC=84=B1?= =?UTF-8?q?=EB=8A=A5=20=EA=B0=9C=EC=84=A0=EC=9D=84=20=EC=9C=84=ED=95=9C=20?= =?UTF-8?q?=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/ARchive/app/build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frontend/ARchive/app/build.gradle b/frontend/ARchive/app/build.gradle index 9242d5fdb..fb654f877 100644 --- a/frontend/ARchive/app/build.gradle +++ b/frontend/ARchive/app/build.gradle @@ -185,6 +185,10 @@ dependencies { // Swiperefreshlayout: https://developer.android.com/jetpack/androidx/releases/swiperefreshlayout?hl=ko#kts implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0") + + // ConcatAdapter + implementation "androidx.recyclerview:recyclerview:1.3.2" + } kapt { correctErrorTypes true From 9ca4d767c1020a560574048a7249de4e402fb84e Mon Sep 17 00:00:00 2001 From: comst19 Date: Mon, 29 Apr 2024 21:28:16 +0900 Subject: [PATCH 218/301] =?UTF-8?q?perf:=20ProfileAdapter=20=EC=85=8B?= =?UTF-8?q?=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/model/mypage/ProfileData.kt | 10 + .../ui/mypage/adapter/ProfileRVA.kt | 69 +++++++ .../main/res/layout/item_my_page_profile.xml | 190 ++++++++++++++++++ 3 files changed, 269 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/model/mypage/ProfileData.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/ProfileRVA.kt create mode 100644 frontend/ARchive/app/src/main/res/layout/item_my_page_profile.xml diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/model/mypage/ProfileData.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/model/mypage/ProfileData.kt new file mode 100644 index 000000000..90e148544 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/model/mypage/ProfileData.kt @@ -0,0 +1,10 @@ +package com.droidblossom.archive.presentation.model.mypage + +data class ProfileData ( + val profileId:Long, + val nickname : String, + val profileUrl : String, + val tag : String, + val friendCount: Int, + val groupCount: Int +) \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/ProfileRVA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/ProfileRVA.kt new file mode 100644 index 000000000..0810430f3 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/ProfileRVA.kt @@ -0,0 +1,69 @@ +package com.droidblossom.archive.presentation.ui.mypage.adapter + +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.droidblossom.archive.databinding.ItemMyPageProfileBinding +import com.droidblossom.archive.presentation.ui.mypage.MyPageFragment.Companion.PROFILE_TYPE +import com.droidblossom.archive.presentation.model.mypage.ProfileData + +class ProfileRVA( + +) : ListAdapter(differ) { + + inner class ItemViewHolder( + private val binding: ItemMyPageProfileBinding + ) : RecyclerView.ViewHolder(binding.root) { + fun bind(data: ProfileData) { + binding.data = data + binding.root.setOnClickListener { + + + } + } + } + + init { + setHasStableIds(true) + } + + override fun getItemId(position: Int): Long { + return getItem(position).profileId + } + + override fun getItemViewType(position: Int): Int { + return PROFILE_TYPE + } + + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): ItemViewHolder { + return ItemViewHolder( + ItemMyPageProfileBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) + } + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + holder.bind(getItem(position)) + } + + + companion object { + val differ = object : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: ProfileData, newItem: ProfileData): Boolean { + return oldItem == newItem + } + + override fun areContentsTheSame(oldItem: ProfileData, newItem: ProfileData): Boolean { + return oldItem == newItem + } + } + } +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/item_my_page_profile.xml b/frontend/ARchive/app/src/main/res/layout/item_my_page_profile.xml new file mode 100644 index 000000000..0224f6169 --- /dev/null +++ b/frontend/ARchive/app/src/main/res/layout/item_my_page_profile.xml @@ -0,0 +1,190 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From f3455a8404ac8fb632e52b129d54f03b01e0d7b8 Mon Sep 17 00:00:00 2001 From: comst19 Date: Mon, 29 Apr 2024 21:29:14 +0900 Subject: [PATCH 219/301] =?UTF-8?q?perf:=20CapsuleAdapter=20=EC=85=8B?= =?UTF-8?q?=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/model/mypage/CapsuleData.kt | 11 +++ .../ui/mypage/adapter/CapsuleRVA.kt | 70 +++++++++++++++++++ .../main/res/layout/item_my_page_capsule.xml | 66 +++++++++++++++++ 3 files changed, 147 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/model/mypage/CapsuleData.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/CapsuleRVA.kt create mode 100644 frontend/ARchive/app/src/main/res/layout/item_my_page_capsule.xml diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/model/mypage/CapsuleData.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/model/mypage/CapsuleData.kt new file mode 100644 index 000000000..3bc471bf5 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/model/mypage/CapsuleData.kt @@ -0,0 +1,11 @@ +package com.droidblossom.archive.presentation.model.mypage + +data class CapsuleData ( + val capsuleId: Long, + val capsuleSkinUrl: String, + val createdDate: String, + val dueDate: String?, + var isOpened: Boolean, + val title: String, + val capsuleType: String +) \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/CapsuleRVA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/CapsuleRVA.kt new file mode 100644 index 000000000..303868771 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/CapsuleRVA.kt @@ -0,0 +1,70 @@ +package com.droidblossom.archive.presentation.ui.mypage.adapter + +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.droidblossom.archive.databinding.ItemMyPageCapsuleBinding +import com.droidblossom.archive.presentation.ui.mypage.MyPageFragment.Companion.CAPSULE_TYPE +import com.droidblossom.archive.presentation.model.mypage.CapsuleData + +class CapsuleRVA( + +) : + ListAdapter(differ) { + + inner class ItemViewHolder( + private val binding: ItemMyPageCapsuleBinding + ) : RecyclerView.ViewHolder(binding.root) { + fun bind(data: CapsuleData) { + binding.data = data + binding.root.setOnClickListener { + + + } + } + } + + init { + setHasStableIds(true) + } + + override fun getItemId(position: Int): Long { + return getItem(position).capsuleId + } + + + override fun getItemViewType(position: Int): Int { + return CAPSULE_TYPE + } + + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): ItemViewHolder { + return ItemViewHolder( + ItemMyPageCapsuleBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) + } + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + holder.bind(getItem(position)) + } + + companion object { + val differ = object : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: CapsuleData, newItem: CapsuleData): Boolean { + return oldItem.capsuleId == newItem.capsuleId + } + + override fun areContentsTheSame(oldItem: CapsuleData, newItem: CapsuleData): Boolean { + return oldItem == newItem + } + } + } +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/item_my_page_capsule.xml b/frontend/ARchive/app/src/main/res/layout/item_my_page_capsule.xml new file mode 100644 index 000000000..a2b3e7ccf --- /dev/null +++ b/frontend/ARchive/app/src/main/res/layout/item_my_page_capsule.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file From e1d3d17b23be4ad199e8a513f663d8b50f7cb3bf Mon Sep 17 00:00:00 2001 From: comst19 Date: Mon, 29 Apr 2024 21:30:43 +0900 Subject: [PATCH 220/301] =?UTF-8?q?perf:=20SpinnerAdapter=20=EC=85=8B?= =?UTF-8?q?=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/model/mypage/SpinnerData.kt | 7 ++ .../ui/mypage/adapter/SpinnerAdapter.kt | 74 +++++++++++++++++++ .../main/res/layout/item_my_page_spinner.xml | 30 ++++++++ 3 files changed, 111 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/model/mypage/SpinnerData.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/SpinnerAdapter.kt create mode 100644 frontend/ARchive/app/src/main/res/layout/item_my_page_spinner.xml diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/model/mypage/SpinnerData.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/model/mypage/SpinnerData.kt new file mode 100644 index 000000000..3745cc72a --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/model/mypage/SpinnerData.kt @@ -0,0 +1,7 @@ +package com.droidblossom.archive.presentation.model.mypage + +import com.droidblossom.archive.presentation.ui.mypage.adapter.CapsuleTypeSpinner + +data class SpinnerData( + val capsuleType:CapsuleTypeSpinner +) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/SpinnerAdapter.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/SpinnerAdapter.kt new file mode 100644 index 000000000..fb60c189e --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/SpinnerAdapter.kt @@ -0,0 +1,74 @@ +package com.droidblossom.archive.presentation.ui.mypage.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.AdapterView +import androidx.recyclerview.widget.RecyclerView +import com.droidblossom.archive.databinding.ItemMyPageSpinnerBinding +import com.droidblossom.archive.presentation.ui.mypage.MyPageFragment + +class SpinnerAdapter( + private val context: Context, + private val spinnerItems: Array +) : RecyclerView.Adapter() { + + inner class ItemViewHolder( + private val binding: ItemMyPageSpinnerBinding + ) : RecyclerView.ViewHolder(binding.root) { + + fun bind(spinnerItems: Array) { + val spinnerAdapter = CapsuleTypeSpinner(context, spinnerItems) + binding.capsuleTypeSpinner.adapter = spinnerAdapter + binding.capsuleTypeSpinner.onItemSelectedListener = + object : AdapterView.OnItemSelectedListener { + override fun onItemSelected( + parent: AdapterView<*>, + view: View, + position: Int, + id: Long + ) { + + } + + override fun onNothingSelected(parent: AdapterView<*>?) { + + } + } + binding.capsuleTypeSpinner.viewTreeObserver.addOnWindowFocusChangeListener { hasFocus -> + spinnerAdapter.spinnerIsOpened = hasFocus + spinnerAdapter.notifyDataSetChanged() + } + } + } + + init { + setHasStableIds(true) + } + + override fun getItemId(position: Int): Long { + return 1L + } + + + override fun getItemViewType(position: Int): Int { + return MyPageFragment.SPINNER_TYPE + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder { + return ItemViewHolder( + ItemMyPageSpinnerBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) + } + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + holder.bind(spinnerItems) + } + + override fun getItemCount(): Int = 1 +} diff --git a/frontend/ARchive/app/src/main/res/layout/item_my_page_spinner.xml b/frontend/ARchive/app/src/main/res/layout/item_my_page_spinner.xml new file mode 100644 index 000000000..318b359aa --- /dev/null +++ b/frontend/ARchive/app/src/main/res/layout/item_my_page_spinner.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file From 14dad7a5386afbff76f7854069708012f2f8e9b9 Mon Sep 17 00:00:00 2001 From: comst19 Date: Tue, 30 Apr 2024 16:55:14 +0900 Subject: [PATCH 221/301] =?UTF-8?q?perf:=20StoryAdapter=20=EC=85=8B?= =?UTF-8?q?=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/model/mypage/StoryData.kt | 6 ++ .../ui/mypage/adapter/StoryRVA.kt | 56 +++++++++++++++++++ .../main/res/layout/item_my_page_story.xml | 42 ++++++++++++++ .../app/src/main/res/layout/item_story.xml | 3 +- 4 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/model/mypage/StoryData.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/StoryRVA.kt create mode 100644 frontend/ARchive/app/src/main/res/layout/item_my_page_story.xml diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/model/mypage/StoryData.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/model/mypage/StoryData.kt new file mode 100644 index 000000000..d4dcc86a1 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/model/mypage/StoryData.kt @@ -0,0 +1,6 @@ +package com.droidblossom.archive.presentation.model.mypage + +data class StoryData( + val id:Long, + val imageUrl:String +) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/StoryRVA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/StoryRVA.kt new file mode 100644 index 000000000..425ab9eb1 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/StoryRVA.kt @@ -0,0 +1,56 @@ +package com.droidblossom.archive.presentation.ui.mypage.adapter + +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.droidblossom.archive.databinding.ItemMyPageStoryBinding +import com.droidblossom.archive.presentation.model.mypage.StoryData + +class StoryRVA( + +) : ListAdapter(differ) { + + inner class ItemViewHolder( + private val binding: ItemMyPageStoryBinding + ) : RecyclerView.ViewHolder(binding.root) { + fun bind(data: StoryData) { + binding.data = data + binding.root.setOnClickListener { + + + } + } + } + + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): ItemViewHolder { + return ItemViewHolder( + ItemMyPageStoryBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) + } + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + holder.bind(getItem(position)) + } + + + companion object { + val differ = object : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: StoryData, newItem: StoryData): Boolean { + return oldItem.id == newItem.id + } + + override fun areContentsTheSame(oldItem: StoryData, newItem: StoryData): Boolean { + return oldItem == newItem + } + } + } +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/item_my_page_story.xml b/frontend/ARchive/app/src/main/res/layout/item_my_page_story.xml new file mode 100644 index 000000000..20829247c --- /dev/null +++ b/frontend/ARchive/app/src/main/res/layout/item_my_page_story.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/item_story.xml b/frontend/ARchive/app/src/main/res/layout/item_story.xml index 995181b62..2a0ecab25 100644 --- a/frontend/ARchive/app/src/main/res/layout/item_story.xml +++ b/frontend/ARchive/app/src/main/res/layout/item_story.xml @@ -1,5 +1,6 @@ - From 5e6a3967bc0cf7fbfdd75d95d4a07ebb259fafc0 Mon Sep 17 00:00:00 2001 From: comst19 Date: Tue, 30 Apr 2024 16:57:54 +0900 Subject: [PATCH 222/301] =?UTF-8?q?perf:=20CapsuleAdapter=20Click,=20Pagin?= =?UTF-8?q?g=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../response/SecretCapsulePageResponseDto.kt | 2 +- .../response/SecretCapsuleResponseDto.kt | 17 ++++- .../archive/domain/model/common/MyCapsule.kt | 14 ++++- .../domain/model/member/MemberDetail.kt | 13 +++- .../domain/model/secret/SecretCapsulePage.kt | 4 +- .../presentation/ui/mypage/MyPageViewModel.kt | 7 +-- .../ui/mypage/MyPageViewModelImpl.kt | 9 +-- .../ui/mypage/adapter/CapsuleRVA.kt | 14 ++++- .../ui/mypage/adapter/MyCapsuleRVA.kt | 63 ------------------- 9 files changed, 61 insertions(+), 82 deletions(-) delete mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/MyCapsuleRVA.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/secret/response/SecretCapsulePageResponseDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/secret/response/SecretCapsulePageResponseDto.kt index 2979aca4c..9e311eb77 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/secret/response/SecretCapsulePageResponseDto.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/secret/response/SecretCapsulePageResponseDto.kt @@ -8,7 +8,7 @@ data class SecretCapsulePageResponseDto( val hasPrevious: Boolean ){ fun toModel() = SecretCapsulePage( - capsules = this.capsules.map { it.toModel() }, + capsules = this.capsules.map { it.toUIModel() }, hasNext = this.hasNext, hasPrevious = this.hasPrevious, ) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/secret/response/SecretCapsuleResponseDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/secret/response/SecretCapsuleResponseDto.kt index f190fdbdf..a7236901e 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/secret/response/SecretCapsuleResponseDto.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/secret/response/SecretCapsuleResponseDto.kt @@ -1,6 +1,7 @@ package com.droidblossom.archive.data.dto.secret.response import com.droidblossom.archive.domain.model.common.MyCapsule +import com.droidblossom.archive.presentation.model.mypage.CapsuleData data class SecretCapsuleResponseDto( val capsuleId: Long, @@ -10,14 +11,24 @@ data class SecretCapsuleResponseDto( val isOpened: Boolean, val title: String, val type: String, -){ - fun toModel()=MyCapsule( +) { + fun toModel() = MyCapsule( capsuleId = this.capsuleId, capsuleSkinUrl = this.SkinUrl, createdDate = this.createdAt, dueDate = this.dueDate, isOpened = this.isOpened, - title =this.title, + title = this.title, + capsuleType = this.type + ) + + fun toUIModel() = CapsuleData( + capsuleId = this.capsuleId, + capsuleSkinUrl = this.SkinUrl, + createdDate = this.createdAt, + dueDate = this.dueDate, + isOpened = this.isOpened, + title = this.title, capsuleType = this.type ) } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/common/MyCapsule.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/common/MyCapsule.kt index 9cc7e0679..1b537f91e 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/common/MyCapsule.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/common/MyCapsule.kt @@ -1,5 +1,7 @@ package com.droidblossom.archive.domain.model.common +import com.droidblossom.archive.presentation.model.mypage.CapsuleData + data class MyCapsule ( val capsuleId: Long, val capsuleSkinUrl: String, @@ -8,4 +10,14 @@ data class MyCapsule ( var isOpened: Boolean, val title: String, val capsuleType: String -) \ No newline at end of file +){ + fun toUIModel() = CapsuleData( + capsuleId = this.capsuleId, + capsuleSkinUrl = this.capsuleSkinUrl, + createdDate = this.createdDate, + dueDate = this.dueDate, + isOpened = this.isOpened, + title = this.title, + capsuleType = this.capsuleType + ) +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/member/MemberDetail.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/member/MemberDetail.kt index 496a9fca5..b23ee375e 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/member/MemberDetail.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/member/MemberDetail.kt @@ -1,9 +1,20 @@ package com.droidblossom.archive.domain.model.member +import com.droidblossom.archive.presentation.model.mypage.ProfileData + data class MemberDetail( val nickname : String, val profileUrl : String, val tag : String, val friendCount: Int, val groupCount: Int -) \ No newline at end of file +){ + fun toUIModel() = ProfileData( + profileId = 0, + nickname = this.nickname, + profileUrl = this.profileUrl, + tag = this.tag, + friendCount = this.friendCount, + groupCount = this.groupCount + ) +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/secret/SecretCapsulePage.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/secret/SecretCapsulePage.kt index 136a23561..4de497c10 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/secret/SecretCapsulePage.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/secret/SecretCapsulePage.kt @@ -1,9 +1,9 @@ package com.droidblossom.archive.domain.model.secret -import com.droidblossom.archive.domain.model.common.MyCapsule +import com.droidblossom.archive.presentation.model.mypage.CapsuleData data class SecretCapsulePage( - val capsules: List, + val capsules: List, val hasNext: Boolean, val hasPrevious: Boolean ) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModel.kt index e95d7d387..d19039315 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModel.kt @@ -2,6 +2,7 @@ package com.droidblossom.archive.presentation.ui.mypage import com.droidblossom.archive.domain.model.common.MyCapsule import com.droidblossom.archive.domain.model.member.MemberDetail +import com.droidblossom.archive.presentation.model.mypage.CapsuleData import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow @@ -9,8 +10,8 @@ interface MyPageViewModel { val myPageEvents : SharedFlow val myInfo : StateFlow - val myCapsules : StateFlow> - val myCapsulesUI : StateFlow> + val myCapsules : StateFlow> + val myCapsulesUI : StateFlow> val hasNextPage : StateFlow val lastCreatedTime : StateFlow var reloadMyInfo:Boolean @@ -26,8 +27,6 @@ interface MyPageViewModel { sealed class MyPageEvent { data class ShowToastMessage(val message : String) : MyPageEvent() - data class CapsuleStateUpdate(val capsuleIndex: Int) : MyPageEvent() - object ClickSetting : MyPageEvent() } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt index 89f471ca2..4a333bcf2 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt @@ -8,6 +8,7 @@ import com.droidblossom.archive.domain.model.secret.SecretCapsulePageRequest import com.droidblossom.archive.domain.usecase.member.MemberUseCase import com.droidblossom.archive.domain.usecase.secret.SecretCapsulePageUseCase import com.droidblossom.archive.presentation.base.BaseViewModel +import com.droidblossom.archive.presentation.model.mypage.CapsuleData import com.droidblossom.archive.presentation.ui.auth.AuthViewModel import com.droidblossom.archive.util.DateUtils import com.droidblossom.archive.util.onFail @@ -39,12 +40,12 @@ class MyPageViewModelImpl @Inject constructor( override val myInfo: StateFlow get() = _myInfo - private val _myCapsules = MutableStateFlow(listOf()) - override val myCapsules: StateFlow> + private val _myCapsules = MutableStateFlow(listOf()) + override val myCapsules: StateFlow> get() = _myCapsules - private val _myCapsulesUI = MutableStateFlow(listOf()) - override val myCapsulesUI: StateFlow> + private val _myCapsulesUI = MutableStateFlow(listOf()) + override val myCapsulesUI: StateFlow> get() = _myCapsulesUI private val _hasNextPage = MutableStateFlow(true) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/CapsuleRVA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/CapsuleRVA.kt index 303868771..99c76f4ff 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/CapsuleRVA.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/CapsuleRVA.kt @@ -8,9 +8,12 @@ import androidx.recyclerview.widget.RecyclerView import com.droidblossom.archive.databinding.ItemMyPageCapsuleBinding import com.droidblossom.archive.presentation.ui.mypage.MyPageFragment.Companion.CAPSULE_TYPE import com.droidblossom.archive.presentation.model.mypage.CapsuleData +import com.droidblossom.archive.presentation.ui.home.HomeFragment +import com.droidblossom.archive.util.CapsuleTypeUtils class CapsuleRVA( - + private val goDetail: (Long, HomeFragment.CapsuleType) -> Unit, + private val goSummary: (Int, Long, HomeFragment.CapsuleType) -> Unit ) : ListAdapter(differ) { @@ -20,8 +23,13 @@ class CapsuleRVA( fun bind(data: CapsuleData) { binding.data = data binding.root.setOnClickListener { - - + if (data.isOpened) { + goDetail(data.capsuleId, CapsuleTypeUtils.stringToEnum(data.capsuleType)) + } else { + goSummary(bindingAdapterPosition, data.capsuleId, + CapsuleTypeUtils.stringToEnum(data.capsuleType) + ) + } } } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/MyCapsuleRVA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/MyCapsuleRVA.kt deleted file mode 100644 index a750ee954..000000000 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/MyCapsuleRVA.kt +++ /dev/null @@ -1,63 +0,0 @@ -package com.droidblossom.archive.presentation.ui.mypage.adapter - -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.droidblossom.archive.databinding.ItemMyCapsuleBinding -import com.droidblossom.archive.domain.model.common.MyCapsule -import com.droidblossom.archive.presentation.ui.home.HomeFragment -import com.droidblossom.archive.util.CapsuleTypeUtils.stringToEnum - -class MyCapsuleRVA( - private val goDetail: (Long, HomeFragment.CapsuleType) -> Unit, - private val goSummary: (Int, Long, HomeFragment.CapsuleType) -> Unit -) : - ListAdapter(differ) { - - inner class ItemViewHolder( - private val binding: ItemMyCapsuleBinding - ) : RecyclerView.ViewHolder(binding.root) { - fun bind(data: MyCapsule) { - binding.item = data - binding.root.setOnClickListener { - if (data.isOpened) { - goDetail(data.capsuleId, stringToEnum(data.capsuleType)) - } else { - goSummary(bindingAdapterPosition, data.capsuleId, stringToEnum(data.capsuleType)) - } - - } - } - } - - override fun onCreateViewHolder( - parent: ViewGroup, - viewType: Int - ): ItemViewHolder { - return ItemViewHolder( - ItemMyCapsuleBinding.inflate( - LayoutInflater.from(parent.context), - parent, - false - ) - ) - } - - override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { - holder.bind(getItem(position)) - } - - companion object { - val differ = object : DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: MyCapsule, newItem: MyCapsule): Boolean { - return oldItem.capsuleId == newItem.capsuleId - } - - override fun areContentsTheSame(oldItem: MyCapsule, newItem: MyCapsule): Boolean { - return oldItem == newItem - } - } - } -} \ No newline at end of file From 2fc68a4cd3cc40c73123ac893a45dd3e29b268d2 Mon Sep 17 00:00:00 2001 From: comst19 Date: Tue, 30 Apr 2024 16:58:47 +0900 Subject: [PATCH 223/301] =?UTF-8?q?perf:=20ProfileAdapter=20Click=20Events?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ui/mypage/adapter/ProfileRVA.kt | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/ProfileRVA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/ProfileRVA.kt index 0810430f3..0dd889135 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/ProfileRVA.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/ProfileRVA.kt @@ -1,5 +1,6 @@ package com.droidblossom.archive.presentation.ui.mypage.adapter +import android.util.Log import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.DiffUtil @@ -10,7 +11,10 @@ import com.droidblossom.archive.presentation.ui.mypage.MyPageFragment.Companion. import com.droidblossom.archive.presentation.model.mypage.ProfileData class ProfileRVA( - + private val goGroupList: () -> Unit, + private val goFriendList: () -> Unit, + private val goRequestList: () -> Unit, + private val goSetting: () -> Unit, ) : ListAdapter(differ) { inner class ItemViewHolder( @@ -18,10 +22,10 @@ class ProfileRVA( ) : RecyclerView.ViewHolder(binding.root) { fun bind(data: ProfileData) { binding.data = data - binding.root.setOnClickListener { - - - } + binding.groupLayout.setOnClickListener { goGroupList() } + binding.friendLayout.setOnClickListener { goFriendList() } + binding.requestLayout.setOnClickListener { goRequestList() } + binding.settingBtn.setOnClickListener { goSetting() } } } From 15241a1d4a0fcfb743ade6d8e11cdd5ad02f19e3 Mon Sep 17 00:00:00 2001 From: comst19 Date: Tue, 30 Apr 2024 16:59:39 +0900 Subject: [PATCH 224/301] perf: NestedScrollView (RecyclerView) -> RecyclerView --- .../presentation/ui/mypage/MyPageFragment.kt | 221 +++++++------- .../src/main/res/layout/fragment_my_page.xml | 270 +----------------- 2 files changed, 119 insertions(+), 372 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt index 0bf9ccb42..3d61c912e 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt @@ -1,16 +1,14 @@ package com.droidblossom.archive.presentation.ui.mypage import android.os.Bundle -import android.util.Log -import android.view.MotionEvent import android.view.View import android.view.ViewGroup -import android.widget.AdapterView -import androidx.core.content.ContextCompat import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle +import androidx.recyclerview.widget.ConcatAdapter +import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.droidblossom.archive.R @@ -18,8 +16,9 @@ import com.droidblossom.archive.databinding.FragmentMyPageBinding import com.droidblossom.archive.presentation.base.BaseFragment import com.droidblossom.archive.presentation.ui.capsule.CapsuleDetailActivity import com.droidblossom.archive.presentation.ui.home.dialog.CapsulePreviewDialogFragment -import com.droidblossom.archive.presentation.ui.mypage.adapter.CapsuleTypeSpinner -import com.droidblossom.archive.presentation.ui.mypage.adapter.MyCapsuleRVA +import com.droidblossom.archive.presentation.ui.mypage.adapter.CapsuleRVA +import com.droidblossom.archive.presentation.ui.mypage.adapter.ProfileRVA +import com.droidblossom.archive.presentation.ui.mypage.adapter.SpinnerAdapter import com.droidblossom.archive.presentation.ui.mypage.friend.FriendActivity import com.droidblossom.archive.presentation.ui.mypage.friendaccept.FriendAcceptActivity import com.droidblossom.archive.presentation.ui.mypage.setting.SettingActivity @@ -32,38 +31,15 @@ class MyPageFragment : BaseFragment(R.layout.fragment_my_page) { override val viewModel: MyPageViewModelImpl by viewModels() - private val myCapsuleRVA by lazy { - MyCapsuleRVA( - { id, type -> - startActivity( - CapsuleDetailActivity.newIntent( - requireContext(), - id, - type - ) - ) - }, - { capsuleIndex, id, type -> - val sheet = CapsulePreviewDialogFragment.newInstance( - capsuleIndex.toString(), - id.toString(), - type.toString(), - false - ) - sheet.show(parentFragmentManager, "CapsulePreviewDialog") - }, - ) - } + private lateinit var profileRVA: ProfileRVA + private lateinit var capsuleRVA: CapsuleRVA + private lateinit var spinnerA: SpinnerAdapter + + private lateinit var concatAdapter: ConcatAdapter - private val spinnerAdapter by lazy { - val capsuleTypeList = - arrayOf(SpinnerCapsuleType.SECRET, SpinnerCapsuleType.PUBLIC, SpinnerCapsuleType.GROUP) - CapsuleTypeSpinner(requireContext(), capsuleTypeList) - } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - binding.vm = viewModel parentFragmentManager.setFragmentResultListener( "capsuleState", @@ -74,97 +50,111 @@ class MyPageFragment : val capsuleOpenState = bundle.getBoolean("isOpened") if (capsuleIndex != -1 && capsuleOpenState) { viewModel.updateCapsuleOpenState(capsuleIndex, capsuleId) - myCapsuleRVA.notifyItemChanged(capsuleIndex) + capsuleRVA.notifyItemChanged(capsuleIndex) } } - initRVA() - initView() - initSpinner() - -// binding.settingBtn.setOnClickListener { -// throw RuntimeException("Test Crash") -// } - - val layoutParams = binding.profileImg.layoutParams as ViewGroup.MarginLayoutParams + initAdapters() + initMyPageRVA() + val layoutParams = binding.myPageRV.layoutParams as ViewGroup.MarginLayoutParams layoutParams.topMargin += getStatusBarHeight() - binding.profileImg.layoutParams = layoutParams - - binding.groupLayout.setOnClickListener { - startActivity(FriendActivity.newIntent(requireContext(), FriendActivity.GROUP)) - } - binding.friendLayout.setOnClickListener { - startActivity(FriendActivity.newIntent(requireContext(), FriendActivity.FRIEND)) - } + binding.myPageRV.layoutParams = layoutParams } - private fun initView() { + private fun initAdapters() { + profileRVA = ProfileRVA( + { + startActivity(FriendActivity.newIntent(requireContext(), FriendActivity.GROUP)) + }, + { + startActivity(FriendActivity.newIntent(requireContext(), FriendActivity.FRIEND)) + }, + { + viewModel.reloadMyInfo = true + startActivity( + FriendAcceptActivity.newIntent( + requireContext(), + FriendAcceptActivity.FRIEND + ) + ) + }, + { + startActivity(SettingActivity.newIntent(requireContext())) + } - with(binding) { - profileTagT.setOnLongClickListener { - copyText("userTag", viewModel.myInfo.value.tag) - true + ) + capsuleRVA = CapsuleRVA( + { id, type -> + startActivity( + CapsuleDetailActivity.newIntent( + requireContext(), + id, + type + ) + ) + }, + { capsuleIndex, id, type -> + val sheet = CapsulePreviewDialogFragment.newInstance( + capsuleIndex.toString(), + id.toString(), + type.toString(), + false + ) + sheet.show(parentFragmentManager, "CapsulePreviewDialog") } - } + ) - binding.groupLayout.setOnClickListener { - startActivity(FriendActivity.newIntent(requireContext(), FriendActivity.GROUP)) - } - binding.friendLayout.setOnClickListener { - startActivity(FriendActivity.newIntent(requireContext(), FriendActivity.FRIEND)) - } + val capsuleTypes = arrayOf( + SpinnerCapsuleType.SECRET, SpinnerCapsuleType.PUBLIC, SpinnerCapsuleType.GROUP + ) + spinnerA = SpinnerAdapter(requireContext(), capsuleTypes) - binding.requestLayout.setOnClickListener { - viewModel.reloadMyInfo = true - startActivity(FriendAcceptActivity.newIntent(requireContext(), FriendAcceptActivity.FRIEND)) - } + val config = ConcatAdapter.Config.Builder() + .setIsolateViewTypes(true) + .setStableIdMode(ConcatAdapter.Config.StableIdMode.ISOLATED_STABLE_IDS) + .build() + + concatAdapter = ConcatAdapter(config, profileRVA, spinnerA, capsuleRVA) } + private fun initMyPageRVA() { + val layoutManager = GridLayoutManager(requireContext(), 3) + layoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() { + override fun getSpanSize(position: Int): Int { + var totalItemsCounted = 0 + concatAdapter.adapters.forEach { adapter -> + val itemCount = adapter.itemCount + if (position < totalItemsCounted + itemCount) { + val localPosition = position - totalItemsCounted + val viewType = adapter.getItemViewType(localPosition) + return when (viewType) { + PROFILE_TYPE -> 3 + CAPSULE_TYPE -> 1 + else -> 3 + } + } + totalItemsCounted += itemCount + } + return 3 + } + } + binding.myPageRV.layoutManager = layoutManager + binding.myPageRV.adapter = concatAdapter - private fun initRVA() { - binding.capsuleRecycleView.adapter = myCapsuleRVA - binding.capsuleRecycleView.animation = null - //무한 스크롤 - binding.capsuleRecycleView.addOnScrollListener(object : RecyclerView.OnScrollListener() { + binding.myPageRV.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { super.onScrollStateChanged(recyclerView, newState) - val lastVisibleItemPosition = - (recyclerView.layoutManager as LinearLayoutManager?)!!.findLastCompletelyVisibleItemPosition() - val totalItemViewCount = recyclerView.adapter!!.itemCount - 1 - if (newState == 2 && !recyclerView.canScrollVertically(1) - && lastVisibleItemPosition == totalItemViewCount - ) { + val layoutManager = recyclerView.layoutManager as LinearLayoutManager + val lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition() + val totalItemCount = recyclerView.adapter!!.itemCount + val threshold = 6 + + if (lastVisibleItemPosition >= totalItemCount - threshold) { viewModel.getSecretCapsulePage() } } - }) - } - - private fun initSpinner() { - with(binding) { - capsuleTypeSpinner.adapter = spinnerAdapter - capsuleTypeSpinner.onItemSelectedListener = - object : AdapterView.OnItemSelectedListener { - override fun onItemSelected( - parent: AdapterView<*>, - view: View, - position: Int, - id: Long - ) { - - } - - override fun onNothingSelected(parent: AdapterView<*>?) { - - } - } - capsuleTypeSpinner.viewTreeObserver.addOnWindowFocusChangeListener { hasFocus -> - spinnerAdapter.spinnerIsOpened = hasFocus - spinnerAdapter.notifyDataSetChanged() - } - } } @@ -181,10 +171,11 @@ class MyPageFragment : viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.myCapsulesUI.collect { capsule -> - myCapsuleRVA.submitList(capsule) + capsuleRVA.submitList(capsule) } } } + viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.myPageEvents.collect { event -> @@ -197,21 +188,25 @@ class MyPageFragment : startActivity(SettingActivity.newIntent(requireContext())) } - is MyPageViewModel.MyPageEvent.CapsuleStateUpdate -> { - //myCapsuleRVA.notifyItemChanged(event.capsuleIndex) - } - else -> {} } } } } + viewLifecycleOwner.lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.myInfo.collect { memberDetail -> + profileRVA.submitList(listOf(memberDetail.toUIModel())) + } + } + } + } override fun onResume() { super.onResume() - if (viewModel.reloadMyInfo){ + if (viewModel.reloadMyInfo) { viewModel.getMe() } } @@ -226,6 +221,10 @@ class MyPageFragment : companion object { const val TAG = "MY" + const val PROFILE_TYPE = 1 + const val STORY_TYPE = 2 + const val SPINNER_TYPE = 3 + const val CAPSULE_TYPE = 4 fun newIntent() = MyPageFragment() } diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_my_page.xml b/frontend/ARchive/app/src/main/res/layout/fragment_my_page.xml index 9228b9a20..af5f0c0cc 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_my_page.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_my_page.xml @@ -1,280 +1,28 @@ + xmlns:app="http://schemas.android.com/apk/res-auto"> - - + android:background="@color/main_bg_1"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + app:layout_constraintTop_toTopOf="parent" /> \ No newline at end of file From dfcdc9ba9977d377ed3f3255779404f3403d6430 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Tue, 30 Apr 2024 19:07:32 +0900 Subject: [PATCH 225/301] =?UTF-8?q?refact:=20by=20lazy=20=EB=B3=80?= =?UTF-8?q?=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ui/mypage/MyPageFragment.kt | 68 +++++++++---------- .../archive/util/SocialLoginUtil.kt | 1 + 2 files changed, 35 insertions(+), 34 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt index 3d61c912e..054adeba8 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt @@ -31,38 +31,8 @@ class MyPageFragment : BaseFragment(R.layout.fragment_my_page) { override val viewModel: MyPageViewModelImpl by viewModels() - private lateinit var profileRVA: ProfileRVA - private lateinit var capsuleRVA: CapsuleRVA - private lateinit var spinnerA: SpinnerAdapter - - private lateinit var concatAdapter: ConcatAdapter - - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - parentFragmentManager.setFragmentResultListener( - "capsuleState", - viewLifecycleOwner - ) { key, bundle -> - val capsuleIndex = bundle.getInt("capsuleIndex") - val capsuleId = bundle.getLong("capsuleId") - val capsuleOpenState = bundle.getBoolean("isOpened") - if (capsuleIndex != -1 && capsuleOpenState) { - viewModel.updateCapsuleOpenState(capsuleIndex, capsuleId) - capsuleRVA.notifyItemChanged(capsuleIndex) - } - } - - initAdapters() - initMyPageRVA() - val layoutParams = binding.myPageRV.layoutParams as ViewGroup.MarginLayoutParams - layoutParams.topMargin += getStatusBarHeight() - binding.myPageRV.layoutParams = layoutParams - } - - private fun initAdapters() { - profileRVA = ProfileRVA( + private val profileRVA: ProfileRVA by lazy { + ProfileRVA( { startActivity(FriendActivity.newIntent(requireContext(), FriendActivity.GROUP)) }, @@ -81,9 +51,10 @@ class MyPageFragment : { startActivity(SettingActivity.newIntent(requireContext())) } - ) - capsuleRVA = CapsuleRVA( + } + private val capsuleRVA: CapsuleRVA by lazy { + CapsuleRVA( { id, type -> startActivity( CapsuleDetailActivity.newIntent( @@ -103,7 +74,36 @@ class MyPageFragment : sheet.show(parentFragmentManager, "CapsulePreviewDialog") } ) + } + private lateinit var spinnerA: SpinnerAdapter + + private lateinit var concatAdapter: ConcatAdapter + + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + parentFragmentManager.setFragmentResultListener( + "capsuleState", + viewLifecycleOwner + ) { key, bundle -> + val capsuleIndex = bundle.getInt("capsuleIndex") + val capsuleId = bundle.getLong("capsuleId") + val capsuleOpenState = bundle.getBoolean("isOpened") + if (capsuleIndex != -1 && capsuleOpenState) { + viewModel.updateCapsuleOpenState(capsuleIndex, capsuleId) + capsuleRVA.notifyItemChanged(capsuleIndex) + } + } + initAdapters() + initMyPageRVA() + val layoutParams = binding.myPageRV.layoutParams as ViewGroup.MarginLayoutParams + layoutParams.topMargin += getStatusBarHeight() + binding.myPageRV.layoutParams = layoutParams + } + + private fun initAdapters() { val capsuleTypes = arrayOf( SpinnerCapsuleType.SECRET, SpinnerCapsuleType.PUBLIC, SpinnerCapsuleType.GROUP ) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/SocialLoginUtil.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/SocialLoginUtil.kt index 0c0d491c6..6b7133f40 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/SocialLoginUtil.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/SocialLoginUtil.kt @@ -91,6 +91,7 @@ class SocialLoginUtil(private val context: Context, private val callback: LoginC val authId = account.id.toString() val email = account.email ?: "" val profileUrl = account.photoUrl.toString() ?: "" + val token = account.idToken ?: "" callback.onLoginSuccess(CheckStatus(authId,AuthViewModel.Social.GOOGLE),SignUp(authId, email, profileUrl, AuthViewModel.Social.GOOGLE)) googleSignOut() } catch (e: ApiException) { From 8c5bea1f4e7baece362deb86d251be5279d14d58 Mon Sep 17 00:00:00 2001 From: comst19 Date: Tue, 30 Apr 2024 19:20:48 +0900 Subject: [PATCH 226/301] =?UTF-8?q?refact:=20by=20lazy=20=EB=B3=80?= =?UTF-8?q?=ED=99=98=20-=20SpinnerAdapter,=20ConcatAdapter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ui/mypage/MyPageFragment.kt | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt index 054adeba8..6342acad0 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt @@ -75,9 +75,24 @@ class MyPageFragment : } ) } - private lateinit var spinnerA: SpinnerAdapter - private lateinit var concatAdapter: ConcatAdapter + private val capsuleTypes = arrayOf( + SpinnerCapsuleType.SECRET, SpinnerCapsuleType.PUBLIC, SpinnerCapsuleType.GROUP + ) + + private val spinnerA: SpinnerAdapter by lazy { + SpinnerAdapter(requireContext(), capsuleTypes) + } + + private val concatAdapter: ConcatAdapter by lazy { + val config = ConcatAdapter.Config.Builder() + .setIsolateViewTypes(true) + .setStableIdMode(ConcatAdapter.Config.StableIdMode.ISOLATED_STABLE_IDS) + .build() + ConcatAdapter(config, profileRVA, spinnerA, capsuleRVA) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -96,27 +111,12 @@ class MyPageFragment : } } - initAdapters() initMyPageRVA() val layoutParams = binding.myPageRV.layoutParams as ViewGroup.MarginLayoutParams layoutParams.topMargin += getStatusBarHeight() binding.myPageRV.layoutParams = layoutParams } - private fun initAdapters() { - val capsuleTypes = arrayOf( - SpinnerCapsuleType.SECRET, SpinnerCapsuleType.PUBLIC, SpinnerCapsuleType.GROUP - ) - spinnerA = SpinnerAdapter(requireContext(), capsuleTypes) - - val config = ConcatAdapter.Config.Builder() - .setIsolateViewTypes(true) - .setStableIdMode(ConcatAdapter.Config.StableIdMode.ISOLATED_STABLE_IDS) - .build() - - concatAdapter = ConcatAdapter(config, profileRVA, spinnerA, capsuleRVA) - } - private fun initMyPageRVA() { val layoutManager = GridLayoutManager(requireContext(), 3) layoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() { From 8e4232532eb85049ab6c7dd16d4e4b460d3e0b47 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Tue, 30 Apr 2024 23:05:52 +0900 Subject: [PATCH 227/301] =?UTF-8?q?feat:=20FCM=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=A0=91=EC=86=8D=EC=8B=9C=20=EC=B9=9C=EA=B5=AC=20=EC=9A=94?= =?UTF-8?q?=EC=B2=AD/=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=ED=99=94=EB=A9=B4?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/presentation/ui/MainActivity.kt | 11 +++++++---- .../archive/util/MyFirebaseMessagingService.kt | 8 ++++---- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt index 4534c0752..68155b6e2 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt @@ -13,6 +13,8 @@ import com.droidblossom.archive.presentation.base.BaseActivity import com.droidblossom.archive.presentation.ui.camera.CameraFragment import com.droidblossom.archive.presentation.ui.home.HomeFragment import com.droidblossom.archive.presentation.ui.mypage.MyPageFragment +import com.droidblossom.archive.presentation.ui.mypage.friend.FriendActivity +import com.droidblossom.archive.presentation.ui.mypage.friendaccept.FriendAcceptActivity import com.droidblossom.archive.presentation.ui.skin.SkinFragment import com.droidblossom.archive.presentation.ui.social.SocialFragment import com.droidblossom.archive.util.DataStoreUtils @@ -126,18 +128,19 @@ class MainActivity : BaseActivity(R.layout.activi // 현재 포그라운드에서만 이동 됨 - 서버와 얘기해야함 private fun handleIntent(intent: Intent) { val destination = intent.getStringExtra("fragmentDestination") + Log.d("알림", destination.toString()) when (destination) { MyFirebaseMessagingService.FragmentDestination.SKIN_FRAGMENT.name -> { showFragment(SkinFragment.newIntent(), SkinFragment.TAG) } - MyFirebaseMessagingService.FragmentDestination.FRIEND_REQUEST_DIALOG.name -> { - + MyFirebaseMessagingService.FragmentDestination.FRIEND_REQUEST_ACTIVITY.name -> { + startActivity(FriendAcceptActivity.newIntent(this, FriendAcceptActivity.FRIEND)) } - MyFirebaseMessagingService.FragmentDestination.FRIEND_ACCEPT_DIALOG.name -> { - + MyFirebaseMessagingService.FragmentDestination.FRIEND_ACCEPT_ACTIVITY.name -> { + startActivity(FriendActivity.newIntent(this, FriendActivity.FRIEND)) } else -> { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/MyFirebaseMessagingService.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/MyFirebaseMessagingService.kt index d99ddbdc3..ccbe088cd 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/MyFirebaseMessagingService.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/MyFirebaseMessagingService.kt @@ -95,11 +95,11 @@ class MyFirebaseMessagingService : FirebaseMessagingService() { } FcmTopic.FRIEND_REQUEST.name -> { - intent.putExtra("fragmentDestination", FragmentDestination.FRIEND_REQUEST_DIALOG.name) + intent.putExtra("fragmentDestination", FragmentDestination.FRIEND_REQUEST_ACTIVITY.name) } FcmTopic.FRIEND_ACCEPT.name -> { - intent.putExtra("fragmentDestination", FragmentDestination.FRIEND_ACCEPT_DIALOG.name) + intent.putExtra("fragmentDestination", FragmentDestination.FRIEND_ACCEPT_ACTIVITY.name) } else -> { @@ -178,8 +178,8 @@ class MyFirebaseMessagingService : FirebaseMessagingService() { enum class FragmentDestination { SKIN_FRAGMENT, - FRIEND_REQUEST_DIALOG, - FRIEND_ACCEPT_DIALOG, + FRIEND_REQUEST_ACTIVITY, + FRIEND_ACCEPT_ACTIVITY, } enum class FcmTopic{ CAPSULE_SKIN, From 9ed8c0076a05fb4bed64780d709e05fa110cfd19 Mon Sep 17 00:00:00 2001 From: comst19 Date: Thu, 2 May 2024 16:54:02 +0900 Subject: [PATCH 228/301] =?UTF-8?q?feat:=20Spinner=EC=97=90=20=EC=84=A0?= =?UTF-8?q?=ED=83=9D=EB=90=9C=20=EA=B0=92=EC=97=90=20=EB=94=B0=EB=9D=BC=20?= =?UTF-8?q?=EB=B6=84=EA=B8=B0=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ui/mypage/adapter/SpinnerAdapter.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/SpinnerAdapter.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/SpinnerAdapter.kt index fb60c189e..875fa1c76 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/SpinnerAdapter.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/SpinnerAdapter.kt @@ -1,6 +1,7 @@ package com.droidblossom.archive.presentation.ui.mypage.adapter import android.content.Context +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -11,7 +12,8 @@ import com.droidblossom.archive.presentation.ui.mypage.MyPageFragment class SpinnerAdapter( private val context: Context, - private val spinnerItems: Array + private val spinnerItems: Array, + private val selectedCapsuleType: (MyPageFragment.SpinnerCapsuleType) -> Unit ) : RecyclerView.Adapter() { inner class ItemViewHolder( @@ -29,7 +31,7 @@ class SpinnerAdapter( position: Int, id: Long ) { - + selectedCapsuleType(spinnerItems[position]) } override fun onNothingSelected(parent: AdapterView<*>?) { From d1cce1ed07d6eb2c88cd04c774c31c07ec57f4e1 Mon Sep 17 00:00:00 2001 From: comst19 Date: Thu, 2 May 2024 16:55:30 +0900 Subject: [PATCH 229/301] =?UTF-8?q?feat:=20=EC=9C=84=EB=A1=9C=20=EC=8A=A4?= =?UTF-8?q?=ED=81=AC=EB=A1=A4=20=EC=8B=9C=20=EA=B0=B1=EC=8B=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ui/mypage/MyPageFragment.kt | 22 +++++++-- .../presentation/ui/mypage/MyPageViewModel.kt | 5 +- .../ui/mypage/MyPageViewModelImpl.kt | 49 ++++++++++++++++--- .../src/main/res/layout/fragment_my_page.xml | 22 ++++++--- 4 files changed, 77 insertions(+), 21 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt index 6342acad0..b61dc76a1 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt @@ -81,7 +81,13 @@ class MyPageFragment : ) private val spinnerA: SpinnerAdapter by lazy { - SpinnerAdapter(requireContext(), capsuleTypes) + SpinnerAdapter( + requireContext(), + capsuleTypes + ) { capsuleTypes -> + viewModel.selectSpinnerItem(capsuleTypes) + + } } private val concatAdapter: ConcatAdapter by lazy { @@ -112,9 +118,9 @@ class MyPageFragment : } initMyPageRVA() - val layoutParams = binding.myPageRV.layoutParams as ViewGroup.MarginLayoutParams + val layoutParams = binding.myPageSwipeRefreshLayout.layoutParams as ViewGroup.MarginLayoutParams layoutParams.topMargin += getStatusBarHeight() - binding.myPageRV.layoutParams = layoutParams + binding.myPageSwipeRefreshLayout.layoutParams = layoutParams } private fun initMyPageRVA() { @@ -140,6 +146,9 @@ class MyPageFragment : } binding.myPageRV.layoutManager = layoutManager binding.myPageRV.adapter = concatAdapter + binding.myPageSwipeRefreshLayout.setOnRefreshListener { + viewModel.load() + } binding.myPageRV.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { @@ -151,7 +160,7 @@ class MyPageFragment : val threshold = 6 if (lastVisibleItemPosition >= totalItemCount - threshold) { - viewModel.getSecretCapsulePage() + viewModel.getCapsulePage() } } }) @@ -172,6 +181,10 @@ class MyPageFragment : repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.myCapsulesUI.collect { capsule -> capsuleRVA.submitList(capsule) + if (binding.myPageSwipeRefreshLayout.isRefreshing){ + binding.myPageSwipeRefreshLayout.isRefreshing = false + binding.myPageRV.scrollToPosition(0) + } } } } @@ -201,7 +214,6 @@ class MyPageFragment : } } } - } override fun onResume() { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModel.kt index d19039315..c8424b1bf 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModel.kt @@ -14,16 +14,19 @@ interface MyPageViewModel { val myCapsulesUI : StateFlow> val hasNextPage : StateFlow val lastCreatedTime : StateFlow + val capsuleType: StateFlow var reloadMyInfo:Boolean fun getMe() - fun getSecretCapsulePage() fun clearCapsules() fun updateMyCapsulesUI() fun updateCapsuleOpenState(capsuleIndex: Int, capsuleId: Long) fun clickSetting() + fun getCapsulePage() fun load() + fun selectSpinnerItem(item:MyPageFragment.SpinnerCapsuleType) + sealed class MyPageEvent { data class ShowToastMessage(val message : String) : MyPageEvent() diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt index 4a333bcf2..d887e9432 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt @@ -1,28 +1,22 @@ package com.droidblossom.archive.presentation.ui.mypage -import android.util.Log import androidx.lifecycle.viewModelScope -import com.droidblossom.archive.domain.model.common.MyCapsule import com.droidblossom.archive.domain.model.member.MemberDetail import com.droidblossom.archive.domain.model.secret.SecretCapsulePageRequest import com.droidblossom.archive.domain.usecase.member.MemberUseCase import com.droidblossom.archive.domain.usecase.secret.SecretCapsulePageUseCase import com.droidblossom.archive.presentation.base.BaseViewModel import com.droidblossom.archive.presentation.model.mypage.CapsuleData -import com.droidblossom.archive.presentation.ui.auth.AuthViewModel import com.droidblossom.archive.util.DateUtils import com.droidblossom.archive.util.onFail import com.droidblossom.archive.util.onSuccess import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import javax.inject.Inject @HiltViewModel @@ -55,6 +49,10 @@ class MyPageViewModelImpl @Inject constructor( override val lastCreatedTime: StateFlow get() = _lastCreatedTime + private val _capsuleType = MutableStateFlow(MyPageFragment.SpinnerCapsuleType.SECRET) + override val capsuleType: StateFlow + get() = _capsuleType + override var reloadMyInfo = false init { @@ -80,7 +78,21 @@ class MyPageViewModelImpl @Inject constructor( } } - override fun getSecretCapsulePage() { + override fun getCapsulePage(){ + when(capsuleType.value){ + MyPageFragment.SpinnerCapsuleType.SECRET -> { + getSecretCapsulePage() + } + MyPageFragment.SpinnerCapsuleType.PUBLIC -> { + getPublicCapsulePage() + } + MyPageFragment.SpinnerCapsuleType.GROUP -> { + getGroupCapsulePage() + } + } + } + + private fun getSecretCapsulePage() { viewModelScope.launch { if (hasNextPage.value) { secretCapsulePageUseCase( @@ -101,6 +113,22 @@ class MyPageViewModelImpl @Inject constructor( } } + private fun getPublicCapsulePage() { + viewModelScope.launch { + if (hasNextPage.value){ + + } + } + } + + private fun getGroupCapsulePage() { + viewModelScope.launch { + if (hasNextPage.value) { + + } + } + } + override fun updateMyCapsulesUI() { viewModelScope.launch { @@ -113,7 +141,7 @@ class MyPageViewModelImpl @Inject constructor( _myCapsules.value = listOf() _lastCreatedTime.value = DateUtils.dataServerString _hasNextPage.value = true - getSecretCapsulePage() + getCapsulePage() } } @@ -132,4 +160,9 @@ class MyPageViewModelImpl @Inject constructor( } } + override fun selectSpinnerItem(item:MyPageFragment.SpinnerCapsuleType) { + _capsuleType.value = item + clearCapsules() + } + } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_my_page.xml b/frontend/ARchive/app/src/main/res/layout/fragment_my_page.xml index af5f0c0cc..23beb1931 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_my_page.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_my_page.xml @@ -11,18 +11,26 @@ android:layout_height="match_parent" android:background="@color/main_bg_1"> - + app:layout_constraintTop_toTopOf="parent"> + + + + + \ No newline at end of file From f322164120e834ccf71aed057bbf7aaaa11bcab3 Mon Sep 17 00:00:00 2001 From: comst19 Date: Fri, 3 May 2024 11:30:02 +0900 Subject: [PATCH 230/301] =?UTF-8?q?fix:=20UI=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=20=EB=A1=9C=EC=A7=81=20-=20=EB=B9=88=20=EB=A6=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=EC=9D=BC=20=EB=95=8C=20=EC=98=A4=EB=A5=98=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ui/mypage/MyPageFragment.kt | 10 ++++- .../presentation/ui/mypage/MyPageViewModel.kt | 5 ++- .../ui/mypage/MyPageViewModelImpl.kt | 40 +++++++++++++------ 3 files changed, 40 insertions(+), 15 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt index b61dc76a1..d44c5bf3c 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt @@ -1,6 +1,7 @@ package com.droidblossom.archive.presentation.ui.mypage import android.os.Bundle +import android.util.Log import android.view.View import android.view.ViewGroup import androidx.fragment.app.viewModels @@ -171,7 +172,10 @@ class MyPageFragment : viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.myCapsules.collect { capsule -> - if (capsule.isNotEmpty()) { + if (viewModel.clearCapsule){ + viewModel.clearCapsule = false + }else{ + Log.d("캡슐","{$capsule}") viewModel.updateMyCapsulesUI() } } @@ -201,6 +205,10 @@ class MyPageFragment : startActivity(SettingActivity.newIntent(requireContext())) } + is MyPageViewModel.MyPageEvent.HideLoading -> { + binding.myPageSwipeRefreshLayout.isRefreshing = false + } + else -> {} } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModel.kt index c8424b1bf..cda6a986a 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModel.kt @@ -1,6 +1,5 @@ package com.droidblossom.archive.presentation.ui.mypage -import com.droidblossom.archive.domain.model.common.MyCapsule import com.droidblossom.archive.domain.model.member.MemberDetail import com.droidblossom.archive.presentation.model.mypage.CapsuleData import kotlinx.coroutines.flow.SharedFlow @@ -16,6 +15,7 @@ interface MyPageViewModel { val lastCreatedTime : StateFlow val capsuleType: StateFlow var reloadMyInfo:Boolean + var clearCapsule:Boolean fun getMe() fun clearCapsules() @@ -26,10 +26,13 @@ interface MyPageViewModel { fun getCapsulePage() fun load() fun selectSpinnerItem(item:MyPageFragment.SpinnerCapsuleType) + fun myPageEvent(event: MyPageEvent) sealed class MyPageEvent { data class ShowToastMessage(val message : String) : MyPageEvent() + object HideLoading : MyPageEvent() + object ClickSetting : MyPageEvent() } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt index d887e9432..e8d94c127 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt @@ -1,5 +1,6 @@ package com.droidblossom.archive.presentation.ui.mypage +import android.util.Log import androidx.lifecycle.viewModelScope import com.droidblossom.archive.domain.model.member.MemberDetail import com.droidblossom.archive.domain.model.secret.SecretCapsulePageRequest @@ -27,10 +28,10 @@ class MyPageViewModelImpl @Inject constructor( private val _myPageEvents = MutableSharedFlow() override val myPageEvents: SharedFlow - get() =_myPageEvents.asSharedFlow() + get() = _myPageEvents.asSharedFlow() - private val _myInfo = MutableStateFlow(MemberDetail("USER", "", "",0,0)) + private val _myInfo = MutableStateFlow(MemberDetail("USER", "", "", 0, 0)) override val myInfo: StateFlow get() = _myInfo @@ -49,21 +50,28 @@ class MyPageViewModelImpl @Inject constructor( override val lastCreatedTime: StateFlow get() = _lastCreatedTime - private val _capsuleType = MutableStateFlow(MyPageFragment.SpinnerCapsuleType.SECRET) + private val _capsuleType = MutableStateFlow(MyPageFragment.SpinnerCapsuleType.SECRET) override val capsuleType: StateFlow get() = _capsuleType override var reloadMyInfo = false + override var clearCapsule = false init { load() } - override fun load(){ + override fun load() { getMe() clearCapsules() } + override fun myPageEvent(event: MyPageViewModel.MyPageEvent) { + viewModelScope.launch { + _myPageEvents.emit(event) + } + } + override fun getMe() { viewModelScope.launch { @@ -72,20 +80,22 @@ class MyPageViewModelImpl @Inject constructor( _myInfo.emit(it) reloadMyInfo = false }.onFail { - _myPageEvents.emit(MyPageViewModel.MyPageEvent.ShowToastMessage("정보 불러오기 실패")) + myPageEvent(MyPageViewModel.MyPageEvent.ShowToastMessage("정보 불러오기 실패")) } } } } - override fun getCapsulePage(){ - when(capsuleType.value){ + override fun getCapsulePage() { + when (capsuleType.value) { MyPageFragment.SpinnerCapsuleType.SECRET -> { getSecretCapsulePage() } + MyPageFragment.SpinnerCapsuleType.PUBLIC -> { getPublicCapsulePage() } + MyPageFragment.SpinnerCapsuleType.GROUP -> { getGroupCapsulePage() } @@ -106,17 +116,19 @@ class MyPageViewModelImpl @Inject constructor( _myCapsules.emit(myCapsules.value + it.capsules) _lastCreatedTime.value = myCapsules.value.last().createdDate }.onFail { - _myPageEvents.emit(MyPageViewModel.MyPageEvent.ShowToastMessage("정보 불러오기 실패")) + myPageEvent(MyPageViewModel.MyPageEvent.ShowToastMessage("정보 불러오기 실패")) } } + myPageEvent(MyPageViewModel.MyPageEvent.HideLoading) } } } private fun getPublicCapsulePage() { viewModelScope.launch { - if (hasNextPage.value){ - + if (hasNextPage.value) { + _myCapsules.emit(listOf()) + myPageEvent(MyPageViewModel.MyPageEvent.HideLoading) } } } @@ -124,7 +136,8 @@ class MyPageViewModelImpl @Inject constructor( private fun getGroupCapsulePage() { viewModelScope.launch { if (hasNextPage.value) { - + _myCapsules.emit(listOf()) + myPageEvent(MyPageViewModel.MyPageEvent.HideLoading) } } } @@ -137,6 +150,7 @@ class MyPageViewModelImpl @Inject constructor( } override fun clearCapsules() { + clearCapsule = true viewModelScope.launch { _myCapsules.value = listOf() _lastCreatedTime.value = DateUtils.dataServerString @@ -156,11 +170,11 @@ class MyPageViewModelImpl @Inject constructor( override fun clickSetting() { viewModelScope.launch { - _myPageEvents.emit(MyPageViewModel.MyPageEvent.ClickSetting) + myPageEvent(MyPageViewModel.MyPageEvent.ClickSetting) } } - override fun selectSpinnerItem(item:MyPageFragment.SpinnerCapsuleType) { + override fun selectSpinnerItem(item: MyPageFragment.SpinnerCapsuleType) { _capsuleType.value = item clearCapsules() } From d03daa516906312b0fa2a4a2c173744d589d883e Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Fri, 3 May 2024 12:37:12 +0900 Subject: [PATCH 231/301] =?UTF-8?q?fix=20:=EC=83=88=EB=A1=9C=EA=B3=A0?= =?UTF-8?q?=EC=B9=A8=EC=8B=9C=20rv=20=EB=B9=84=EC=9A=B0=EC=84=9C=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=EC=9E=90=EA=B0=80=20=EC=A7=81=EA=B4=80?= =?UTF-8?q?=EC=A0=81=EC=9D=B4=EA=B2=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/social/page/friend/SocialFriendViewModelImpl.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModelImpl.kt index f016107aa..4c1868326 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModelImpl.kt @@ -92,6 +92,7 @@ class SocialFriendViewModelImpl @Inject constructor( override fun getLatestPublicCapsule(){ viewModelScope.launch { + _publicCapsules.emit(listOf()) publicCapsulePageUseCase( PublicCapsuleSliceRequestDto( 15, From f4d03d78af15ed0ac3e64986db959adf2d3cdb31 Mon Sep 17 00:00:00 2001 From: comst19 Date: Fri, 3 May 2024 16:08:49 +0900 Subject: [PATCH 232/301] =?UTF-8?q?refact:=20=EC=B5=9C=EC=8B=A0=20?= =?UTF-8?q?=EC=B9=9C=EA=B5=AC,=20=EA=B7=B8=EB=A3=B9=20=EC=BA=A1=EC=8A=90?= =?UTF-8?q?=20=EA=B0=80=EC=A0=B8=EC=98=A4=EB=8A=94=20=EB=A1=9C=EC=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ui/mypage/MyPageFragment.kt | 1 - .../page/friend/SocialFriendFragment.kt | 32 +++++++++- .../page/friend/SocialFriendViewModel.kt | 5 ++ .../page/friend/SocialFriendViewModelImpl.kt | 41 ++++++------- .../social/page/group/SocialGroupFragment.kt | 30 +++++++++- .../social/page/group/SocialGroupViewModel.kt | 12 ++++ .../page/group/SocialGroupViewModelImpl.kt | 58 ++++++------------- 7 files changed, 109 insertions(+), 70 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt index d44c5bf3c..f656526db 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt @@ -175,7 +175,6 @@ class MyPageFragment : if (viewModel.clearCapsule){ viewModel.clearCapsule = false }else{ - Log.d("캡슐","{$capsule}") viewModel.updateMyCapsulesUI() } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendFragment.kt index 9476367c2..fecf0fd75 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendFragment.kt @@ -76,6 +76,9 @@ class SocialFriendFragment : BaseFragment { showToastMessage(event.message) } + is SocialFriendViewModel.SocialFriendEvent.HideLoading -> { + binding.socialFriendSwipeRefreshLayout.isRefreshing = false + } else -> {} } @@ -86,10 +89,33 @@ class SocialFriendFragment : BaseFragment - socialFriendCapsuleRVA.submitList(publicCapsules){ - if (binding.socialFriendSwipeRefreshLayout.isRefreshing){ + if (viewModel.clearCapsule){ + viewModel.clearCapsule = false + }else{ + socialFriendCapsuleRVA.submitList(publicCapsules){ + if (binding.socialFriendSwipeRefreshLayout.isRefreshing){ + binding.socialFriendSwipeRefreshLayout.isRefreshing = false + binding.socialFriendRV.scrollToPosition(0) + } + } + } + } + } + } + + viewLifecycleOwner.lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED){ + viewModel.socialFriendEvents.collect{ event-> + when (event){ + is SocialFriendViewModel.SocialFriendEvent.ShowToastMessage -> { + showToastMessage(event.message) + } + + is SocialFriendViewModel.SocialFriendEvent.HideLoading ->{ binding.socialFriendSwipeRefreshLayout.isRefreshing = false - binding.socialFriendRV.scrollToPosition(0) + } + else -> { + } } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModel.kt index 789029165..26befd030 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModel.kt @@ -1,6 +1,7 @@ package com.droidblossom.archive.presentation.ui.social.page.friend import com.droidblossom.archive.domain.model.common.SocialCapsules +import com.droidblossom.archive.presentation.ui.mypage.MyPageViewModel import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow @@ -10,6 +11,8 @@ interface SocialFriendViewModel { val isSearchOpen : StateFlow val hasNextPage : StateFlow val lastCreatedTime : StateFlow + var clearCapsule:Boolean + fun socialFriendEvent(event: SocialFriendEvent) fun openSearchFriendCapsule() @@ -21,5 +24,7 @@ interface SocialFriendViewModel { fun getLatestPublicCapsule() sealed class SocialFriendEvent{ data class ShowToastMessage(val message : String) : SocialFriendEvent() + object HideLoading : SocialFriendEvent() + } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModelImpl.kt index 4c1868326..95f749d88 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModelImpl.kt @@ -25,11 +25,11 @@ import javax.inject.Inject @HiltViewModel class SocialFriendViewModelImpl @Inject constructor( private val publicCapsulePageUseCase: PublicCapsulePageUseCase -): BaseViewModel(), SocialFriendViewModel { +) : BaseViewModel(), SocialFriendViewModel { private val _socialFriendEvents = MutableSharedFlow() override val socialFriendEvents: SharedFlow - get() =_socialFriendEvents.asSharedFlow() + get() = _socialFriendEvents.asSharedFlow() private val _publicCapsules = MutableStateFlow(listOf()) override val publicCapsules: StateFlow> @@ -45,6 +45,8 @@ class SocialFriendViewModelImpl @Inject constructor( private val _lastCreatedTime = MutableStateFlow(DateUtils.dataServerString) override val lastCreatedTime: StateFlow get() = _lastCreatedTime + override var clearCapsule = false + init { getPublicCapsulePage() @@ -69,7 +71,7 @@ class SocialFriendViewModelImpl @Inject constructor( } - override fun getPublicCapsulePage(){ + override fun getPublicCapsulePage() { viewModelScope.launch { if (hasNextPage.value) { publicCapsulePageUseCase( @@ -79,38 +81,29 @@ class SocialFriendViewModelImpl @Inject constructor( ) ).collect { result -> result.onSuccess { + val currentIds = publicCapsules.value.map { capsule -> capsule.capsuleId }.toSet() + val newCapsules = it.publicCapsules.filter { capsule -> capsule.capsuleId !in currentIds } + _publicCapsules.emit(publicCapsules.value + newCapsules) _hasNextPage.value = it.hasNext - _publicCapsules.emit(publicCapsules.value + it.publicCapsules) - _lastCreatedTime.value = publicCapsules.value.last().createdDate + _lastCreatedTime.value = newCapsules.last().createdDate }.onFail { socialFriendEvent(SocialFriendViewModel.SocialFriendEvent.ShowToastMessage("공개캡슐 불러오기 실패")) } } + socialFriendEvent(SocialFriendViewModel.SocialFriendEvent.HideLoading) } } } - override fun getLatestPublicCapsule(){ - viewModelScope.launch { - _publicCapsules.emit(listOf()) - publicCapsulePageUseCase( - PublicCapsuleSliceRequestDto( - 15, - DateUtils.dataServerString - ) - ).collect { result -> - result.onSuccess { - _hasNextPage.value = it.hasNext - _publicCapsules.emit(it.publicCapsules) - _lastCreatedTime.value = publicCapsules.value.last().createdDate - }.onFail { - socialFriendEvent(SocialFriendViewModel.SocialFriendEvent.ShowToastMessage("공개캡슐 불러오기 실패")) - } - } - } + override fun getLatestPublicCapsule() { + clearCapsule = true + _publicCapsules.value = listOf() + _hasNextPage.value = true + _lastCreatedTime.value = DateUtils.dataServerString + getPublicCapsulePage() } - override fun searchFriendCapsule(){ + override fun searchFriendCapsule() { } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupFragment.kt index db84dc6a7..33d860bb2 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupFragment.kt @@ -24,6 +24,7 @@ import com.droidblossom.archive.presentation.ui.capsule.CapsuleDetailActivity import com.droidblossom.archive.presentation.ui.home.HomeFragment import com.droidblossom.archive.presentation.ui.social.adapter.SocialFriendCapsuleRVA import com.droidblossom.archive.presentation.ui.social.adapter.TestSocialFriendModel +import com.droidblossom.archive.presentation.ui.social.page.friend.SocialFriendViewModel import com.droidblossom.archive.util.SpaceItemDecoration import com.droidblossom.archive.util.updateTopConstraintsForSearch import dagger.hilt.android.AndroidEntryPoint @@ -75,10 +76,33 @@ class SocialGroupFragment : BaseFragment - socialFriendCapsuleRVA.submitList(groupCapsules){ - if (binding.socialFriendSwipeRefreshLayout.isRefreshing){ + if (viewModel.clearCapsule){ + viewModel.clearCapsule = false + }else{ + socialFriendCapsuleRVA.submitList(groupCapsules){ + if (binding.socialFriendSwipeRefreshLayout.isRefreshing){ + binding.socialFriendSwipeRefreshLayout.isRefreshing = false + binding.socialGroupRV.scrollToPosition(0) + } + } + } + } + } + } + + viewLifecycleOwner.lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED){ + viewModel.socialGroupEvents.collect{ event-> + when (event){ + is SocialGroupViewModel.SocialGroupEvent.ShowToastMessage -> { + showToastMessage(event.message) + } + + is SocialGroupViewModel.SocialGroupEvent.HideLoading ->{ binding.socialFriendSwipeRefreshLayout.isRefreshing = false - binding.socialGroupRV.scrollToPosition(0) + } + else -> { + } } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupViewModel.kt index 2b1f61180..f3555d83f 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupViewModel.kt @@ -1,14 +1,20 @@ package com.droidblossom.archive.presentation.ui.social.page.group import com.droidblossom.archive.domain.model.common.SocialCapsules +import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow interface SocialGroupViewModel { + val socialGroupEvents : SharedFlow val groupCapsules : StateFlow> val isSearchOpen : StateFlow val hasNextPage : StateFlow val lastCreatedTime : StateFlow + var clearCapsule:Boolean + + fun socialGroupEvent(event: SocialGroupEvent) + fun openSearchGroupCapsule() fun closeSearchGroupCapsule() @@ -18,4 +24,10 @@ interface SocialGroupViewModel { fun getGroupCapsulePage() fun getLatestGroupCapsule() + + sealed class SocialGroupEvent{ + data class ShowToastMessage(val message : String) : SocialGroupEvent() + object HideLoading : SocialGroupEvent() + + } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupViewModelImpl.kt index 4956b2d0a..fe8d78c22 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/group/SocialGroupViewModelImpl.kt @@ -1,16 +1,16 @@ package com.droidblossom.archive.presentation.ui.social.page.group import androidx.lifecycle.viewModelScope -import com.droidblossom.archive.data.dto.open.request.PublicCapsuleSliceRequestDto import com.droidblossom.archive.domain.model.common.SocialCapsules import com.droidblossom.archive.presentation.base.BaseViewModel import com.droidblossom.archive.presentation.ui.social.page.friend.SocialFriendViewModel import com.droidblossom.archive.util.DateUtils -import com.droidblossom.archive.util.onFail -import com.droidblossom.archive.util.onSuccess import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.launch import javax.inject.Inject @@ -19,6 +19,9 @@ class SocialGroupViewModelImpl @Inject constructor( ): BaseViewModel(), SocialGroupViewModel { + private val _socialGroupEvents = MutableSharedFlow() + override val socialGroupEvents: SharedFlow + get() =_socialGroupEvents.asSharedFlow() private val _isSearchOpen = MutableStateFlow(false) override val isSearchOpen: StateFlow @@ -34,11 +37,18 @@ class SocialGroupViewModelImpl @Inject constructor( private val _lastCreatedTime = MutableStateFlow(DateUtils.dataServerString) override val lastCreatedTime: StateFlow get() = _lastCreatedTime + override var clearCapsule = false init { //getGroupCapsulePage() } + override fun socialGroupEvent(event: SocialGroupViewModel.SocialGroupEvent) { + viewModelScope.launch { + _socialGroupEvents.emit(event) + } + } + override fun openSearchGroupCapsule() { viewModelScope.launch { _isSearchOpen.emit(true) @@ -58,47 +68,17 @@ class SocialGroupViewModelImpl @Inject constructor( override fun getGroupCapsulePage(){ viewModelScope.launch { if (hasNextPage.value) { - /* - publicCapsulePageUseCase( - PublicCapsuleSliceRequestDto( - 15, - lastCreatedTime.value - ) - ).collect { result -> - result.onSuccess { - _hasNextPage.value = it.hasNext - _publicCapsules.emit(publicCapsules.value + it.publicCapsules) - _lastCreatedTime.value = publicCapsules.value.last().createdDate - }.onFail { - socialFriendEvent(SocialFriendViewModel.SocialFriendEvent.ShowToastMessage("공개캡슐 불러오기 실패")) - } - } - - */ + } } } override fun getLatestGroupCapsule(){ - viewModelScope.launch { - /* - publicCapsulePageUseCase( - PublicCapsuleSliceRequestDto( - 15, - DateUtils.dataServerString - ) - ).collect { result -> - result.onSuccess { - _hasNextPage.value = it.hasNext - _publicCapsules.emit(it.publicCapsules) - _lastCreatedTime.value = publicCapsules.value.last().createdDate - }.onFail { - socialFriendEvent(SocialFriendViewModel.SocialFriendEvent.ShowToastMessage("공개캡슐 불러오기 실패")) - } - } - - */ - } + clearCapsule = true + _groupCapsules.value = listOf() + _hasNextPage.value = true + _lastCreatedTime.value = DateUtils.dataServerString + getGroupCapsulePage() } } \ No newline at end of file From cd80678382c888eb12b9b0e2fbb2f30736b548f2 Mon Sep 17 00:00:00 2001 From: comst19 Date: Fri, 3 May 2024 17:26:38 +0900 Subject: [PATCH 233/301] =?UTF-8?q?fix:=20PublicCapsuleSliceResponseDto?= =?UTF-8?q?=EC=9D=98=20toModel()=20=EC=98=A4=ED=83=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/dto/open/response/PublicCapsuleSliceResponseDto.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/open/response/PublicCapsuleSliceResponseDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/open/response/PublicCapsuleSliceResponseDto.kt index d07b6f9df..a34bca078 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/open/response/PublicCapsuleSliceResponseDto.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/open/response/PublicCapsuleSliceResponseDto.kt @@ -10,6 +10,6 @@ data class PublicCapsuleSliceResponseDto( ){ fun toModel()= PublicCapsuleSliceResponse( publicCapsules = this.publicCapsules.map { it.toModel() }, - hasNext = false + hasNext = this.hasNext ) } \ No newline at end of file From 08b306f215c69cf7a89c2a320d1165283f4cdbba Mon Sep 17 00:00:00 2001 From: comst19 Date: Fri, 3 May 2024 17:28:34 +0900 Subject: [PATCH 234/301] =?UTF-8?q?fix:=20FriendCapsules=20=EA=B0=92=20?= =?UTF-8?q?=EC=A4=91=EB=B3=B5=EC=9D=BC=20=EB=95=8C=20=EB=AC=B8=EC=A0=9C=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../page/friend/SocialFriendViewModelImpl.kt | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModelImpl.kt index 95f749d88..352d722d1 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModelImpl.kt @@ -1,25 +1,22 @@ package com.droidblossom.archive.presentation.ui.social.page.friend -import android.util.Log import androidx.lifecycle.viewModelScope import com.droidblossom.archive.data.dto.open.request.PublicCapsuleSliceRequestDto -import com.droidblossom.archive.domain.model.common.MyCapsule import com.droidblossom.archive.domain.model.common.SocialCapsules -import com.droidblossom.archive.domain.model.secret.SecretCapsulePageRequest import com.droidblossom.archive.domain.usecase.open.PublicCapsulePageUseCase import com.droidblossom.archive.presentation.base.BaseViewModel -import com.droidblossom.archive.presentation.ui.auth.AuthViewModel -import com.droidblossom.archive.presentation.ui.mypage.MyPageViewModel import com.droidblossom.archive.util.DateUtils import com.droidblossom.archive.util.onFail import com.droidblossom.archive.util.onSuccess import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import javax.inject.Inject @HiltViewModel @@ -81,11 +78,15 @@ class SocialFriendViewModelImpl @Inject constructor( ) ).collect { result -> result.onSuccess { - val currentIds = publicCapsules.value.map { capsule -> capsule.capsuleId }.toSet() - val newCapsules = it.publicCapsules.filter { capsule -> capsule.capsuleId !in currentIds } - _publicCapsules.emit(publicCapsules.value + newCapsules) - _hasNextPage.value = it.hasNext - _lastCreatedTime.value = newCapsules.last().createdDate + viewModelScope.launch(Dispatchers.IO) { + val currentIds = publicCapsules.value.map { capsule -> capsule.capsuleId }.toSet() + val newCapsules = it.publicCapsules.filter { capsule -> capsule.capsuleId !in currentIds } + withContext(Dispatchers.Main) { + _publicCapsules.emit(publicCapsules.value + newCapsules) + _hasNextPage.value = it.hasNext + _lastCreatedTime.value = publicCapsules.value.last().createdDate + } + } }.onFail { socialFriendEvent(SocialFriendViewModel.SocialFriendEvent.ShowToastMessage("공개캡슐 불러오기 실패")) } From 5ee01c2a028536beac8df7ff6b0f6d0f151d7d75 Mon Sep 17 00:00:00 2001 From: comst19 Date: Fri, 3 May 2024 18:13:23 +0900 Subject: [PATCH 235/301] =?UTF-8?q?fix:=20MyPage=20CapsuleData=20=EC=A4=91?= =?UTF-8?q?=EB=B3=B5=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ui/mypage/MyPageFragment.kt | 19 ++++++++++++----- .../ui/mypage/MyPageViewModelImpl.kt | 21 ++++++++++++++----- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt index f656526db..f5ed8a6a8 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt @@ -1,7 +1,6 @@ package com.droidblossom.archive.presentation.ui.mypage import android.os.Bundle -import android.util.Log import android.view.View import android.view.ViewGroup import androidx.fragment.app.viewModels @@ -183,10 +182,11 @@ class MyPageFragment : viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.myCapsulesUI.collect { capsule -> - capsuleRVA.submitList(capsule) - if (binding.myPageSwipeRefreshLayout.isRefreshing){ - binding.myPageSwipeRefreshLayout.isRefreshing = false - binding.myPageRV.scrollToPosition(0) + capsuleRVA.submitList(capsule){ + if (binding.myPageSwipeRefreshLayout.isRefreshing){ + binding.myPageSwipeRefreshLayout.isRefreshing = false + binding.myPageRV.scrollToPosition(0) + } } } } @@ -221,6 +221,15 @@ class MyPageFragment : } } } + + viewLifecycleOwner.lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.capsuleType.collect { + viewModel.clearCapsules() + } + } + } + } override fun onResume() { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt index e8d94c127..fc6ab8d0d 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt @@ -12,12 +12,14 @@ import com.droidblossom.archive.util.DateUtils import com.droidblossom.archive.util.onFail import com.droidblossom.archive.util.onSuccess import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import javax.inject.Inject @HiltViewModel @@ -58,7 +60,8 @@ class MyPageViewModelImpl @Inject constructor( override var clearCapsule = false init { - load() + getMe() + getCapsulePage() } override fun load() { @@ -103,6 +106,7 @@ class MyPageViewModelImpl @Inject constructor( } private fun getSecretCapsulePage() { + Log.d("흠","엥") viewModelScope.launch { if (hasNextPage.value) { secretCapsulePageUseCase( @@ -112,9 +116,16 @@ class MyPageViewModelImpl @Inject constructor( ).toDto() ).collect { result -> result.onSuccess { - _hasNextPage.value = it.hasNext - _myCapsules.emit(myCapsules.value + it.capsules) - _lastCreatedTime.value = myCapsules.value.last().createdDate + viewModelScope.launch(Dispatchers.IO) { + val currentIds = myCapsules.value.map { capsule -> capsule.capsuleId }.toSet() + val newCapsules = it.capsules.filter { capsule -> capsule.capsuleId !in currentIds } + Log.d("뭐냐", "${myCapsules.value + newCapsules}") + withContext(Dispatchers.Main) { + _myCapsules.emit(myCapsules.value + newCapsules) + _hasNextPage.value = it.hasNext + _lastCreatedTime.value = myCapsules.value.last().createdDate + } + } }.onFail { myPageEvent(MyPageViewModel.MyPageEvent.ShowToastMessage("정보 불러오기 실패")) } @@ -150,6 +161,7 @@ class MyPageViewModelImpl @Inject constructor( } override fun clearCapsules() { + Log.d("유아이","클리어") clearCapsule = true viewModelScope.launch { _myCapsules.value = listOf() @@ -176,7 +188,6 @@ class MyPageViewModelImpl @Inject constructor( override fun selectSpinnerItem(item: MyPageFragment.SpinnerCapsuleType) { _capsuleType.value = item - clearCapsules() } } \ No newline at end of file From d18d5aaced7cfcb7b1ca1988a96ecdff76a2c65f Mon Sep 17 00:00:00 2001 From: comst19 Date: Fri, 3 May 2024 18:46:28 +0900 Subject: [PATCH 236/301] =?UTF-8?q?fix:=20SkinFragment=EC=97=90=EC=84=9C?= =?UTF-8?q?=20SkinData=20=EC=A4=91=EB=B3=B5=20=EB=AC=B8=EC=A0=9C=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ui/skin/SkinViewModelImpl.kt | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinViewModelImpl.kt index c3713a3be..b5fbd7eb6 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinViewModelImpl.kt @@ -10,12 +10,14 @@ import com.droidblossom.archive.util.DateUtils import com.droidblossom.archive.util.onFail import com.droidblossom.archive.util.onSuccess import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import javax.inject.Inject @HiltViewModel @@ -60,9 +62,16 @@ class SkinViewModelImpl @Inject constructor( ) ).collect { result -> result.onSuccess { - _hasNextSkins.value = it.hasNext - _skins.emit(skins.value + it.skins) - _lastCreatedSkinTime.value = skins.value.last().createdAt + withContext(Dispatchers.Default) { + val currentIds = skins.value.map { skin -> skin.id }.toSet() + val newCapsules = it.skins.filter { skin -> skin.id !in currentIds } + withContext(Dispatchers.Main) { + _skins.emit(skins.value + newCapsules) + _hasNextSkins.value = it.hasNext + _lastCreatedSkinTime.value = skins.value.last().createdAt + } + } + }.onFail { _skinEvents.emit(SkinViewModel.SkinEvent.ShowToastMessage("스킨 불러오기 실패.")) } From 8fa56629c77c6c6a2b9219c84ae06f848191744a Mon Sep 17 00:00:00 2001 From: comst19 Date: Fri, 3 May 2024 19:28:35 +0900 Subject: [PATCH 237/301] =?UTF-8?q?fix:=20MyPage=20CapsuleData=20=EC=A4=91?= =?UTF-8?q?=EB=B3=B5=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ui/mypage/MyPageFragment.kt | 3 +-- .../presentation/ui/mypage/MyPageViewModel.kt | 2 +- .../presentation/ui/mypage/MyPageViewModelImpl.kt | 13 ++++--------- .../social/page/friend/SocialFriendViewModelImpl.kt | 2 +- 4 files changed, 7 insertions(+), 13 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt index f5ed8a6a8..7ab4a84ce 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt @@ -225,11 +225,10 @@ class MyPageFragment : viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.capsuleType.collect { - viewModel.clearCapsules() + viewModel.clearCapsules(false) } } } - } override fun onResume() { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModel.kt index cda6a986a..a622925d3 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModel.kt @@ -18,7 +18,7 @@ interface MyPageViewModel { var clearCapsule:Boolean fun getMe() - fun clearCapsules() + fun clearCapsules(setting:Boolean) fun updateMyCapsulesUI() fun updateCapsuleOpenState(capsuleIndex: Int, capsuleId: Long) fun clickSetting() diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt index fc6ab8d0d..5df52190e 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt @@ -61,12 +61,10 @@ class MyPageViewModelImpl @Inject constructor( init { getMe() - getCapsulePage() } - override fun load() { getMe() - clearCapsules() + clearCapsules(true) } override fun myPageEvent(event: MyPageViewModel.MyPageEvent) { @@ -106,7 +104,6 @@ class MyPageViewModelImpl @Inject constructor( } private fun getSecretCapsulePage() { - Log.d("흠","엥") viewModelScope.launch { if (hasNextPage.value) { secretCapsulePageUseCase( @@ -116,7 +113,7 @@ class MyPageViewModelImpl @Inject constructor( ).toDto() ).collect { result -> result.onSuccess { - viewModelScope.launch(Dispatchers.IO) { + withContext(Dispatchers.Default) { val currentIds = myCapsules.value.map { capsule -> capsule.capsuleId }.toSet() val newCapsules = it.capsules.filter { capsule -> capsule.capsuleId !in currentIds } Log.d("뭐냐", "${myCapsules.value + newCapsules}") @@ -160,9 +157,8 @@ class MyPageViewModelImpl @Inject constructor( } } - override fun clearCapsules() { - Log.d("유아이","클리어") - clearCapsule = true + override fun clearCapsules(setting:Boolean) { + clearCapsule = setting viewModelScope.launch { _myCapsules.value = listOf() _lastCreatedTime.value = DateUtils.dataServerString @@ -189,5 +185,4 @@ class MyPageViewModelImpl @Inject constructor( override fun selectSpinnerItem(item: MyPageFragment.SpinnerCapsuleType) { _capsuleType.value = item } - } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModelImpl.kt index 352d722d1..fd3f0f2a3 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModelImpl.kt @@ -78,7 +78,7 @@ class SocialFriendViewModelImpl @Inject constructor( ) ).collect { result -> result.onSuccess { - viewModelScope.launch(Dispatchers.IO) { + withContext(Dispatchers.Default) { val currentIds = publicCapsules.value.map { capsule -> capsule.capsuleId }.toSet() val newCapsules = it.publicCapsules.filter { capsule -> capsule.capsuleId !in currentIds } withContext(Dispatchers.Main) { From c8cd51f73e640c74abf758d51fdc9e0f5f5a6169 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Sat, 4 May 2024 21:24:54 +0900 Subject: [PATCH 238/301] =?UTF-8?q?fix=20:=20=EB=A6=AC=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20=EC=A4=91=EB=B3=B5=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/presentation/ui/skin/SkinViewModelImpl.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinViewModelImpl.kt index b5fbd7eb6..c848e9639 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinViewModelImpl.kt @@ -66,7 +66,11 @@ class SkinViewModelImpl @Inject constructor( val currentIds = skins.value.map { skin -> skin.id }.toSet() val newCapsules = it.skins.filter { skin -> skin.id !in currentIds } withContext(Dispatchers.Main) { - _skins.emit(skins.value + newCapsules) + if (skins.value.isEmpty()){ + _skins.emit(newCapsules) + }else{ + _skins.emit(skins.value + newCapsules) + } _hasNextSkins.value = it.hasNext _lastCreatedSkinTime.value = skins.value.last().createdAt } From 807ba1b623bc3224c0bd6b3050710846c45071e2 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Sat, 4 May 2024 21:39:34 +0900 Subject: [PATCH 239/301] =?UTF-8?q?fix=20:=20=EB=A6=AC=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20=EC=A4=91=EB=B3=B5=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/home/createcapsule/CreateCapsuleViewModelImpl.kt | 6 +++++- .../presentation/ui/mypage/MyPageViewModelImpl.kt | 6 +++++- .../presentation/ui/mypage/friend/FriendViewModelImpl.kt | 9 +++++++-- .../ui/mypage/friendaccept/FriendAcceptViewModelImpl.kt | 6 +++++- .../ui/social/page/friend/SocialFriendViewModelImpl.kt | 6 +++++- 5 files changed, 27 insertions(+), 6 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt index 4c1d1d158..72547f3a7 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt @@ -252,7 +252,11 @@ class CreateCapsuleViewModelImpl @Inject constructor( ).collect { result -> result.onSuccess { _hasNextSkins.emit(it.hasNext) - _skins.emit(skins.value + it.skins) + if (skins.value.isEmpty()) { + _skins.emit(it.skins) + } else { + _skins.emit(skins.value + it.skins) + } _lastCreatedSkinTime.value = _skins.value.last().createdAt }.onFail { _create2Events.emit(CreateCapsuleViewModel.Create2Event.ShowToastMessage("스킨 불러오기 실패.")) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt index 5df52190e..9711f7d0c 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt @@ -118,7 +118,11 @@ class MyPageViewModelImpl @Inject constructor( val newCapsules = it.capsules.filter { capsule -> capsule.capsuleId !in currentIds } Log.d("뭐냐", "${myCapsules.value + newCapsules}") withContext(Dispatchers.Main) { - _myCapsules.emit(myCapsules.value + newCapsules) + if (myCapsules.value.isEmpty()) { + _myCapsules.emit(newCapsules) + } else { + _myCapsules.emit(myCapsules.value + newCapsules) + } _hasNextPage.value = it.hasNext _lastCreatedTime.value = myCapsules.value.last().createdDate } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModelImpl.kt index 996a50c9a..935f27d9c 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModelImpl.kt @@ -78,8 +78,13 @@ class FriendViewModelImpl @Inject constructor( friendsPageUseCase(15, friendLastCreatedTime.value).collect { result -> result.onSuccess { friendHasNextPage.value = it.hasNext - _friendListUI.emit(_friendListUI.value + it.friends) - _friendList.emit(friendList.value + it.friends) + if (friendListUI.value.isEmpty()){ + _friendListUI.emit(it.friends) + _friendList.emit(it.friends) + } else { + _friendListUI.emit(_friendListUI.value + it.friends) + _friendList.emit(friendList.value + it.friends) + } friendLastCreatedTime.value = it.friends.last().createdAt }.onFail { _friendEvent.emit( diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptViewModelImpl.kt index 6e3cbf913..6a501a50e 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptViewModelImpl.kt @@ -46,7 +46,11 @@ class FriendAcceptViewModelImpl @Inject constructor( friendsRequestsPageUseCase(15, friendLastCreatedTime.value).collect { result -> result.onSuccess { friendHasNextPage.value = it.hasNext - _friendAcceptList.emit(friendAcceptList.value + it.friends) + if (friendAcceptList.value.isEmpty()) { + _friendAcceptList.emit(it.friends) + } else { + _friendAcceptList.emit(friendAcceptList.value + it.friends) + } friendLastCreatedTime.value = it.friends.last().createdAt }.onFail { _friendAcceptEvent.emit( diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModelImpl.kt index fd3f0f2a3..9aa362288 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModelImpl.kt @@ -82,7 +82,11 @@ class SocialFriendViewModelImpl @Inject constructor( val currentIds = publicCapsules.value.map { capsule -> capsule.capsuleId }.toSet() val newCapsules = it.publicCapsules.filter { capsule -> capsule.capsuleId !in currentIds } withContext(Dispatchers.Main) { - _publicCapsules.emit(publicCapsules.value + newCapsules) + if (publicCapsules.value.isEmpty()) { + _publicCapsules.emit(newCapsules) + } else { + _publicCapsules.emit(publicCapsules.value + newCapsules) + } _hasNextPage.value = it.hasNext _lastCreatedTime.value = publicCapsules.value.last().createdDate } From 6b88dbf3327c96a01df82bb7595a492634db458f Mon Sep 17 00:00:00 2001 From: comst19 Date: Sun, 5 May 2024 11:36:52 +0900 Subject: [PATCH 240/301] =?UTF-8?q?fix:=20=EC=9E=A6=EC=9D=80=20API=20?= =?UTF-8?q?=ED=98=B8=EC=B6=9C,=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EC=A4=91?= =?UTF-8?q?=EB=B3=B5=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0=20-=20?= =?UTF-8?q?=EB=94=94=EB=B0=94=EC=9A=B4=EC=8B=B1,=20=EC=BD=94=EB=A3=A8?= =?UTF-8?q?=ED=8B=B4=20=EB=A6=AC=EC=86=8C=EC=8A=A4=20=EA=B4=80=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/base/BaseViewModel.kt | 17 +++++++ .../home/notification/NotificationActivity.kt | 2 +- .../notification/NotificationViewModel.kt | 1 + .../notification/NotificationViewModelImpl.kt | 27 ++++++++++- .../presentation/ui/mypage/MyPageFragment.kt | 2 +- .../presentation/ui/mypage/MyPageViewModel.kt | 1 + .../ui/mypage/MyPageViewModelImpl.kt | 46 ++++++++++++------- .../friendaccept/FriendAcceptViewModel.kt | 2 + .../friendaccept/FriendAcceptViewModelImpl.kt | 34 +++++++++++--- .../friendaccept/page/FriendAcceptFragment.kt | 5 +- .../presentation/ui/skin/SkinFragment.kt | 2 +- .../presentation/ui/skin/SkinViewModel.kt | 1 + .../presentation/ui/skin/SkinViewModelImpl.kt | 44 ++++++++++++------ .../page/friend/SocialFriendFragment.kt | 2 +- .../page/friend/SocialFriendViewModel.kt | 2 +- .../page/friend/SocialFriendViewModelImpl.kt | 41 +++++++++++------ 16 files changed, 168 insertions(+), 61 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseViewModel.kt index 0af502b11..c5f156cdc 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseViewModel.kt @@ -3,9 +3,26 @@ package com.droidblossom.archive.presentation.base import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.channelFlow import kotlinx.coroutines.launch +import java.util.concurrent.TimeUnit abstract class BaseViewModel:ViewModel() { open fun fetchData(): Job = viewModelScope.launch { } + + companion object { + fun Flow.throttleFirst(windowDuration: Long, timeUnit: TimeUnit): Flow = channelFlow { + val windowMillis = timeUnit.toMillis(windowDuration) + var lastTime = 0L + collect { value -> + val currentTime = System.currentTimeMillis() + if (currentTime - lastTime >= windowMillis) { + lastTime = currentTime + send(value) + } + } + } + } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationActivity.kt index 085f5e400..03b2c68d2 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationActivity.kt @@ -81,7 +81,7 @@ class NotificationActivity : if (newState == 2 && !recyclerView.canScrollVertically(1) && lastVisibleItemPosition == totalItemViewCount ) { - viewModel.getNotificationPage() + viewModel.onScrollNearBottom() } } }) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationViewModel.kt index c712fe28f..1e6c8387a 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationViewModel.kt @@ -12,6 +12,7 @@ interface NotificationViewModel { val lastCreatedTime: StateFlow fun getNotificationPage() + fun onScrollNearBottom() sealed class NotificationEvent { data class ShowToastMessage(val message: String) : NotificationEvent() diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationViewModelImpl.kt index df3cdc1c8..81ff32040 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationViewModelImpl.kt @@ -4,16 +4,21 @@ import androidx.lifecycle.viewModelScope import com.droidblossom.archive.domain.model.member.NotificationModel import com.droidblossom.archive.domain.usecase.member.GetNotificationsUseCase import com.droidblossom.archive.presentation.base.BaseViewModel +import com.droidblossom.archive.presentation.base.BaseViewModel.Companion.throttleFirst import com.droidblossom.archive.util.DateUtils import com.droidblossom.archive.util.onFail import com.droidblossom.archive.util.onSuccess import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.Job +import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch +import java.util.concurrent.TimeUnit import javax.inject.Inject @HiltViewModel @@ -38,9 +43,26 @@ class NotificationViewModelImpl @Inject constructor( override val lastCreatedTime: StateFlow get() = _lastCreatedTime + private val scrollEventChannel = Channel(Channel.CONFLATED) + private val scrollEventFlow = scrollEventChannel.receiveAsFlow().throttleFirst(1000, TimeUnit.MILLISECONDS) + + private var getNotificationListJob: Job? = null + + init { + viewModelScope.launch{ + scrollEventFlow.collect { + getNotificationPage() + } + } + } + + override fun onScrollNearBottom() { + scrollEventChannel.trySend(Unit) + } override fun getNotificationPage() { - viewModelScope.launch { - if (hasNextPage.value) { + if (hasNextPage.value){ + getNotificationListJob?.cancel() + getNotificationListJob = viewModelScope.launch { getNotificationsUseCase(15, lastCreatedTime.value).collect { result -> result.onSuccess { _hasNextPage.value = it.hasNext @@ -56,5 +78,6 @@ class NotificationViewModelImpl @Inject constructor( } } } + } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt index 7ab4a84ce..afe14905c 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt @@ -160,7 +160,7 @@ class MyPageFragment : val threshold = 6 if (lastVisibleItemPosition >= totalItemCount - threshold) { - viewModel.getCapsulePage() + viewModel.onScrollNearBottom() } } }) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModel.kt index a622925d3..bd53dcbf7 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModel.kt @@ -27,6 +27,7 @@ interface MyPageViewModel { fun load() fun selectSpinnerItem(item:MyPageFragment.SpinnerCapsuleType) fun myPageEvent(event: MyPageEvent) + fun onScrollNearBottom() sealed class MyPageEvent { data class ShowToastMessage(val message : String) : MyPageEvent() diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt index 9711f7d0c..087040c3e 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt @@ -7,19 +7,24 @@ import com.droidblossom.archive.domain.model.secret.SecretCapsulePageRequest import com.droidblossom.archive.domain.usecase.member.MemberUseCase import com.droidblossom.archive.domain.usecase.secret.SecretCapsulePageUseCase import com.droidblossom.archive.presentation.base.BaseViewModel +import com.droidblossom.archive.presentation.base.BaseViewModel.Companion.throttleFirst import com.droidblossom.archive.presentation.model.mypage.CapsuleData import com.droidblossom.archive.util.DateUtils import com.droidblossom.archive.util.onFail import com.droidblossom.archive.util.onSuccess import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import java.util.concurrent.TimeUnit import javax.inject.Inject @HiltViewModel @@ -56,17 +61,34 @@ class MyPageViewModelImpl @Inject constructor( override val capsuleType: StateFlow get() = _capsuleType + private val scrollEventChannel = Channel(Channel.CONFLATED) + private val scrollEventFlow = + scrollEventChannel.receiveAsFlow().throttleFirst(1000, TimeUnit.MILLISECONDS) + + private var getCapsuleListJob: Job? = null + override var reloadMyInfo = false override var clearCapsule = false init { getMe() + viewModelScope.launch { + scrollEventFlow.collect { + getCapsulePage() + } + } } + override fun load() { getMe() clearCapsules(true) } + override fun onScrollNearBottom() { + scrollEventChannel.trySend(Unit) + } + + override fun myPageEvent(event: MyPageViewModel.MyPageEvent) { viewModelScope.launch { _myPageEvents.emit(event) @@ -104,8 +126,9 @@ class MyPageViewModelImpl @Inject constructor( } private fun getSecretCapsulePage() { - viewModelScope.launch { - if (hasNextPage.value) { + if (hasNextPage.value){ + getCapsuleListJob?.cancel() + getCapsuleListJob = viewModelScope.launch{ secretCapsulePageUseCase( SecretCapsulePageRequest( 15, @@ -113,19 +136,10 @@ class MyPageViewModelImpl @Inject constructor( ).toDto() ).collect { result -> result.onSuccess { - withContext(Dispatchers.Default) { - val currentIds = myCapsules.value.map { capsule -> capsule.capsuleId }.toSet() - val newCapsules = it.capsules.filter { capsule -> capsule.capsuleId !in currentIds } - Log.d("뭐냐", "${myCapsules.value + newCapsules}") - withContext(Dispatchers.Main) { - if (myCapsules.value.isEmpty()) { - _myCapsules.emit(newCapsules) - } else { - _myCapsules.emit(myCapsules.value + newCapsules) - } - _hasNextPage.value = it.hasNext - _lastCreatedTime.value = myCapsules.value.last().createdDate - } + _hasNextPage.value = it.hasNext + _myCapsules.emit(myCapsules.value + it.capsules) + if (myCapsules.value.isNotEmpty()){ + _lastCreatedTime.value = myCapsules.value.last().createdDate } }.onFail { myPageEvent(MyPageViewModel.MyPageEvent.ShowToastMessage("정보 불러오기 실패")) @@ -161,7 +175,7 @@ class MyPageViewModelImpl @Inject constructor( } } - override fun clearCapsules(setting:Boolean) { + override fun clearCapsules(setting: Boolean) { clearCapsule = setting viewModelScope.launch { _myCapsules.value = listOf() diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptViewModel.kt index 02cba3fb1..0823589a2 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptViewModel.kt @@ -11,9 +11,11 @@ interface FriendAcceptViewModel { val friendAcceptList: StateFlow> fun getFriendAcceptList() + fun onScrollNearBottom() fun denyRequest(friend: Friend) fun acceptRequest(friend: Friend) + sealed class FriendAcceptEvent { data class ShowToastMessage(val message: String) : FriendAcceptEvent() } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptViewModelImpl.kt index 6a501a50e..71304e063 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptViewModelImpl.kt @@ -1,5 +1,6 @@ package com.droidblossom.archive.presentation.ui.mypage.friendaccept +import android.util.Log import androidx.lifecycle.viewModelScope import com.droidblossom.archive.domain.model.friend.Friend import com.droidblossom.archive.domain.model.friend.FriendAcceptRequest @@ -12,13 +13,19 @@ import com.droidblossom.archive.util.DateUtils import com.droidblossom.archive.util.onFail import com.droidblossom.archive.util.onSuccess import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import java.util.concurrent.TimeUnit import javax.inject.Inject @HiltViewModel @@ -39,19 +46,34 @@ class FriendAcceptViewModelImpl @Inject constructor( private val friendHasNextPage = MutableStateFlow(true) private val friendLastCreatedTime = MutableStateFlow(DateUtils.dataServerString) + private val scrollEventChannel = Channel(Channel.CONFLATED) + private val scrollEventFlow = scrollEventChannel.receiveAsFlow().throttleFirst(1000, TimeUnit.MILLISECONDS) - override fun getFriendAcceptList() { + private var getAcceptRequestListJob: Job? = null + init { viewModelScope.launch { + scrollEventFlow.collect { + getFriendAcceptList() + } + } + } + + override fun onScrollNearBottom() { + scrollEventChannel.trySend(Unit) + } + + override fun getFriendAcceptList() { + getAcceptRequestListJob?.cancel() + getAcceptRequestListJob = viewModelScope.launch { if (friendHasNextPage.value) { friendsRequestsPageUseCase(15, friendLastCreatedTime.value).collect { result -> result.onSuccess { friendHasNextPage.value = it.hasNext - if (friendAcceptList.value.isEmpty()) { - _friendAcceptList.emit(it.friends) - } else { - _friendAcceptList.emit(friendAcceptList.value + it.friends) + _friendAcceptList.emit(friendAcceptList.value + it.friends) + if (friendAcceptList.value.isNotEmpty()) { + friendLastCreatedTime.value = it.friends.last().createdAt } - friendLastCreatedTime.value = it.friends.last().createdAt + }.onFail { _friendAcceptEvent.emit( FriendViewModel.FriendEvent.ShowToastMessage( diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/page/FriendAcceptFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/page/FriendAcceptFragment.kt index 8784e22de..c9e9eacac 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/page/FriendAcceptFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/page/FriendAcceptFragment.kt @@ -1,6 +1,7 @@ package com.droidblossom.archive.presentation.ui.mypage.friendaccept.page import android.os.Bundle +import android.util.Log import android.view.View import androidx.fragment.app.activityViewModels import androidx.lifecycle.Lifecycle @@ -50,8 +51,8 @@ class FriendAcceptFragment : val totalItemCount = layoutManager.itemCount val lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition() - if (totalItemCount - lastVisibleItemPosition <= 1) { - viewModel.getFriendAcceptList() + if (totalItemCount - lastVisibleItemPosition <= 5) { + viewModel.onScrollNearBottom() } } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinFragment.kt index 19ec026e8..31c23fcf0 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinFragment.kt @@ -65,7 +65,7 @@ class SkinFragment : BaseFragment(R.layo val lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition() if (totalItemCount - lastVisibleItemPosition <= 5) { - viewModel.getSkinList() + viewModel.onScrollNearBottom() } } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinViewModel.kt index 86a6e891d..c291e275f 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinViewModel.kt @@ -23,6 +23,7 @@ interface SkinViewModel { fun updateMySkinsUI() fun clearSkins() + fun onScrollNearBottom() sealed class SkinEvent { object ToSkinMake : SkinEvent() diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinViewModelImpl.kt index c848e9639..b2239e4e1 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinViewModelImpl.kt @@ -6,18 +6,23 @@ import com.droidblossom.archive.data.dto.capsule_skin.request.CapsuleSkinsPageRe import com.droidblossom.archive.domain.model.common.CapsuleSkinSummary import com.droidblossom.archive.domain.usecase.capsule_skin.CapsuleSkinsPageUseCase import com.droidblossom.archive.presentation.base.BaseViewModel +import com.droidblossom.archive.presentation.base.BaseViewModel.Companion.throttleFirst import com.droidblossom.archive.util.DateUtils import com.droidblossom.archive.util.onFail import com.droidblossom.archive.util.onSuccess import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import java.util.concurrent.TimeUnit import javax.inject.Inject @HiltViewModel @@ -51,10 +56,28 @@ class SkinViewModelImpl @Inject constructor( override val isSearchOpen: StateFlow get() = _isSearchOpen + private val scrollEventChannel = Channel(Channel.CONFLATED) + private val scrollEventFlow = scrollEventChannel.receiveAsFlow().throttleFirst(1000, TimeUnit.MILLISECONDS) - override fun getSkinList() { + private var getSkinLstJob: Job? = null + + init { viewModelScope.launch { - if (hasNextSkins.value) { + scrollEventFlow.collect{ + getSkinList() + } + } + } + + override fun onScrollNearBottom() { + scrollEventChannel.trySend(Unit) + } + + + override fun getSkinList() { + if (hasNextSkins.value){ + getSkinLstJob?.cancel() + getSkinLstJob = viewModelScope.launch { capsuleSkinsPageUseCase( CapsuleSkinsPageRequestDto( 15, @@ -62,20 +85,11 @@ class SkinViewModelImpl @Inject constructor( ) ).collect { result -> result.onSuccess { - withContext(Dispatchers.Default) { - val currentIds = skins.value.map { skin -> skin.id }.toSet() - val newCapsules = it.skins.filter { skin -> skin.id !in currentIds } - withContext(Dispatchers.Main) { - if (skins.value.isEmpty()){ - _skins.emit(newCapsules) - }else{ - _skins.emit(skins.value + newCapsules) - } - _hasNextSkins.value = it.hasNext - _lastCreatedSkinTime.value = skins.value.last().createdAt - } + _hasNextSkins.value = it.hasNext + _skins.emit(skins.value + it.skins) + if (skins.value.isNotEmpty()) { + _lastCreatedSkinTime.value = skins.value.last().createdAt } - }.onFail { _skinEvents.emit(SkinViewModel.SkinEvent.ShowToastMessage("스킨 불러오기 실패.")) } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendFragment.kt index fecf0fd75..903af635f 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendFragment.kt @@ -148,7 +148,7 @@ class SocialFriendFragment : BaseFragment(Channel.CONFLATED) + private val scrollEventFlow = + scrollEventChannel.receiveAsFlow().throttleFirst(1000, TimeUnit.MILLISECONDS) + + private var getSecretCapsuleListJob: Job? = null + init { + viewModelScope.launch { + scrollEventFlow.collect { + getPublicCapsulePage() + } + } getPublicCapsulePage() } + override fun onScrollNearBottom() { + scrollEventChannel.trySend(Unit) + } + override fun socialFriendEvent(event: SocialFriendViewModel.SocialFriendEvent) { viewModelScope.launch { _socialFriendEvents.emit(event) @@ -69,8 +89,9 @@ class SocialFriendViewModelImpl @Inject constructor( override fun getPublicCapsulePage() { - viewModelScope.launch { - if (hasNextPage.value) { + if (hasNextPage.value){ + getSecretCapsuleListJob?.cancel() + getSecretCapsuleListJob = viewModelScope.launch { publicCapsulePageUseCase( PublicCapsuleSliceRequestDto( 15, @@ -78,19 +99,9 @@ class SocialFriendViewModelImpl @Inject constructor( ) ).collect { result -> result.onSuccess { - withContext(Dispatchers.Default) { - val currentIds = publicCapsules.value.map { capsule -> capsule.capsuleId }.toSet() - val newCapsules = it.publicCapsules.filter { capsule -> capsule.capsuleId !in currentIds } - withContext(Dispatchers.Main) { - if (publicCapsules.value.isEmpty()) { - _publicCapsules.emit(newCapsules) - } else { - _publicCapsules.emit(publicCapsules.value + newCapsules) - } - _hasNextPage.value = it.hasNext - _lastCreatedTime.value = publicCapsules.value.last().createdDate - } - } + _hasNextPage.value = it.hasNext + _publicCapsules.emit(publicCapsules.value + it.publicCapsules) + _lastCreatedTime.value = publicCapsules.value.last().createdDate }.onFail { socialFriendEvent(SocialFriendViewModel.SocialFriendEvent.ShowToastMessage("공개캡슐 불러오기 실패")) } From d96dd00e3a802a485605a7083d66a8494dceeb13 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Sun, 5 May 2024 15:03:14 +0900 Subject: [PATCH 241/301] =?UTF-8?q?fix:=20=EC=9E=A6=EC=9D=80=20API=20?= =?UTF-8?q?=ED=98=B8=EC=B6=9C,=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EC=A4=91?= =?UTF-8?q?=EB=B3=B5=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0=20-=20?= =?UTF-8?q?=EB=94=94=EB=B0=94=EC=9A=B4=EC=8B=B1,=20=EC=BD=94=EB=A3=A8?= =?UTF-8?q?=ED=8B=B4=20=EB=A6=AC=EC=86=8C=EC=8A=A4=20=EA=B4=80=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../createcapsule/CreateCapsule2Fragment.kt | 2 +- .../createcapsule/CreateCapsuleViewModel.kt | 1 + .../CreateCapsuleViewModelImpl.kt | 55 ++++++++++++++++--- .../ui/mypage/MyPageViewModelImpl.kt | 6 +- .../ui/mypage/friend/FriendViewModel.kt | 1 + .../ui/mypage/friend/FriendViewModelImpl.kt | 30 +++++++++- .../mypage/friend/page/FriendListFragment.kt | 2 +- .../friendaccept/FriendAcceptViewModelImpl.kt | 4 +- 8 files changed, 85 insertions(+), 16 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsule2Fragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsule2Fragment.kt index 35c142fd5..44f5fcc40 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsule2Fragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsule2Fragment.kt @@ -135,7 +135,7 @@ class CreateCapsule2Fragment : val lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition() if (totalItemCount - lastVisibleItemPosition <= 5) { - viewModel.getSkinList() + viewModel.onScrollNearBottom() } } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModel.kt index 07eefc233..68cbb414d 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModel.kt @@ -59,6 +59,7 @@ interface CreateCapsuleViewModel { val dueTime : StateFlow val address : StateFlow + fun onScrollNearBottom() fun move1To2() fun choseCapsuleType(type: Int) fun move2To3() diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt index 72547f3a7..cc066f992 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt @@ -17,6 +17,7 @@ import com.droidblossom.archive.domain.usecase.open.PublicCapsuleCreateUseCase import com.droidblossom.archive.domain.usecase.s3.S3UrlsGetUseCase import com.droidblossom.archive.domain.usecase.secret.SecretCapsuleCreateUseCase import com.droidblossom.archive.presentation.base.BaseViewModel +import com.droidblossom.archive.presentation.base.BaseViewModel.Companion.throttleFirst import com.droidblossom.archive.util.DateUtils import com.droidblossom.archive.util.S3Util import com.droidblossom.archive.util.onFail @@ -24,15 +25,19 @@ import com.droidblossom.archive.util.onSuccess import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll +import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch import java.io.File +import java.util.concurrent.TimeUnit import javax.inject.Inject @HiltViewModel @@ -172,6 +177,25 @@ class CreateCapsuleViewModelImpl @Inject constructor( private var imageNames = listOf() private var videoNames = listOf() + private val scrollEventChannel = Channel(Channel.CONFLATED) + private val scrollEventFlow = + scrollEventChannel.receiveAsFlow().throttleFirst(1000, TimeUnit.MILLISECONDS) + + private var getLstJob: Job? = null + + init { + viewModelScope.launch { + scrollEventFlow.collect { + getSkinList() + } + } + } + + override fun onScrollNearBottom() { + scrollEventChannel.trySend(Unit) + } + + //create1 override fun move1To2() { viewModelScope.launch { @@ -242,8 +266,9 @@ class CreateCapsuleViewModelImpl @Inject constructor( } override fun getSkinList() { - viewModelScope.launch { - if (hasNextSkins.value) { + if (hasNextSkins.value) { + getLstJob?.cancel() + getLstJob = viewModelScope.launch { capsuleSkinsPageUseCase( CapsuleSkinsPageRequestDto( 15, @@ -542,12 +567,20 @@ class CreateCapsuleViewModelImpl @Inject constructor( longitude = capsuleLongitude.value, title = capsuleTitle.value, ) - ).collect{ result -> + ).collect { result -> result.onSuccess { - _create3Events.emit(CreateCapsuleViewModel.Create3Event.ShowToastMessage("캡슐이 생성되었습니다.")) + _create3Events.emit( + CreateCapsuleViewModel.Create3Event.ShowToastMessage( + "캡슐이 생성되었습니다." + ) + ) Log.d("캡슐생성", "$it") }.onFail { - _create3Events.emit(CreateCapsuleViewModel.Create3Event.ShowToastMessage("캡슐이 생성 실패했습니다..")) + _create3Events.emit( + CreateCapsuleViewModel.Create3Event.ShowToastMessage( + "캡슐이 생성 실패했습니다.." + ) + ) } } } @@ -568,10 +601,18 @@ class CreateCapsuleViewModelImpl @Inject constructor( ) ).collect { result -> result.onSuccess { - _create3Events.emit(CreateCapsuleViewModel.Create3Event.ShowToastMessage("캡슐이 생성되었습니다.")) + _create3Events.emit( + CreateCapsuleViewModel.Create3Event.ShowToastMessage( + "캡슐이 생성되었습니다." + ) + ) Log.d("캡슐생성", "$it") }.onFail { - _create3Events.emit(CreateCapsuleViewModel.Create3Event.ShowToastMessage("캡슐이 생성 실패했습니다..")) + _create3Events.emit( + CreateCapsuleViewModel.Create3Event.ShowToastMessage( + "캡슐이 생성 실패했습니다.." + ) + ) } } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt index 087040c3e..ea77edf31 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt @@ -126,9 +126,9 @@ class MyPageViewModelImpl @Inject constructor( } private fun getSecretCapsulePage() { - if (hasNextPage.value){ + if (hasNextPage.value) { getCapsuleListJob?.cancel() - getCapsuleListJob = viewModelScope.launch{ + getCapsuleListJob = viewModelScope.launch { secretCapsulePageUseCase( SecretCapsulePageRequest( 15, @@ -138,7 +138,7 @@ class MyPageViewModelImpl @Inject constructor( result.onSuccess { _hasNextPage.value = it.hasNext _myCapsules.emit(myCapsules.value + it.capsules) - if (myCapsules.value.isNotEmpty()){ + if (myCapsules.value.isNotEmpty()) { _lastCreatedTime.value = myCapsules.value.last().createdDate } }.onFail { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModel.kt index f5ad0d9eb..99049c618 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModel.kt @@ -17,6 +17,7 @@ interface FriendViewModel { //group val isGroupSearchOpen: StateFlow + fun onScrollNearBottomFriend() //friend fun openSearchFriend() diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModelImpl.kt index 935f27d9c..cdba64078 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModelImpl.kt @@ -5,19 +5,24 @@ import com.droidblossom.archive.domain.model.friend.Friend import com.droidblossom.archive.domain.usecase.friend.FriendDeleteUseCase import com.droidblossom.archive.domain.usecase.friend.FriendsPageUseCase import com.droidblossom.archive.presentation.base.BaseViewModel +import com.droidblossom.archive.presentation.base.BaseViewModel.Companion.throttleFirst import com.droidblossom.archive.presentation.ui.home.notification.NotificationViewModel import com.droidblossom.archive.presentation.ui.mypage.friend.addfriend.AddFriendViewModel import com.droidblossom.archive.util.DateUtils import com.droidblossom.archive.util.onFail import com.droidblossom.archive.util.onSuccess import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.Job +import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch +import java.util.concurrent.TimeUnit import javax.inject.Inject @HiltViewModel @@ -55,6 +60,24 @@ class FriendViewModelImpl @Inject constructor( override val isGroupSearchOpen: StateFlow get() = _isGroupSearchOpen + private val scrollFriendEventChannel = Channel(Channel.CONFLATED) + private val scrollFriendEventFlow = + scrollFriendEventChannel.receiveAsFlow().throttleFirst(1000, TimeUnit.MILLISECONDS) + + private var getFriendLstJob: Job? = null + + init { + viewModelScope.launch { + scrollFriendEventFlow.collect { + getFriendList() + } + } + } + + override fun onScrollNearBottomFriend() { + scrollFriendEventChannel.trySend(Unit) + } + //friend override fun openSearchFriend() { viewModelScope.launch { @@ -73,12 +96,13 @@ class FriendViewModelImpl @Inject constructor( } override fun getFriendList() { - viewModelScope.launch { - if (friendHasNextPage.value) { + if (friendHasNextPage.value) { + getFriendLstJob?.cancel() + getFriendLstJob = viewModelScope.launch { friendsPageUseCase(15, friendLastCreatedTime.value).collect { result -> result.onSuccess { friendHasNextPage.value = it.hasNext - if (friendListUI.value.isEmpty()){ + if (friendListUI.value.isEmpty()) { _friendListUI.emit(it.friends) _friendList.emit(it.friends) } else { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/FriendListFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/FriendListFragment.kt index 9cc8810b0..b099feaec 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/FriendListFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/FriendListFragment.kt @@ -50,7 +50,7 @@ class FriendListFragment : val lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition() if (totalItemCount - lastVisibleItemPosition <= 3) { - viewModel.getFriendList() + viewModel.onScrollNearBottomFriend() } } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptViewModelImpl.kt index 71304e063..a23fabc30 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptViewModelImpl.kt @@ -47,9 +47,11 @@ class FriendAcceptViewModelImpl @Inject constructor( private val friendLastCreatedTime = MutableStateFlow(DateUtils.dataServerString) private val scrollEventChannel = Channel(Channel.CONFLATED) - private val scrollEventFlow = scrollEventChannel.receiveAsFlow().throttleFirst(1000, TimeUnit.MILLISECONDS) + private val scrollEventFlow = + scrollEventChannel.receiveAsFlow().throttleFirst(1000, TimeUnit.MILLISECONDS) private var getAcceptRequestListJob: Job? = null + init { viewModelScope.launch { scrollEventFlow.collect { From 8bec370a5eab94a2b8790426c4713fdcb5c3c33c Mon Sep 17 00:00:00 2001 From: comst19 Date: Sun, 5 May 2024 15:14:45 +0900 Subject: [PATCH 242/301] =?UTF-8?q?feat:=20API=20=ED=86=B5=EC=8B=A0=20?= =?UTF-8?q?=EC=8B=9C=EB=8F=84=20=EC=A0=84=20=EB=84=A4=ED=8A=B8=EC=9B=8C?= =?UTF-8?q?=ED=81=AC=20=EC=97=B0=EA=B2=B0=20=EB=81=8A=EA=B9=80=20=EA=B0=90?= =?UTF-8?q?=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/presentation/base/BaseActivity.kt | 17 ++++++++++------- .../archive/presentation/model/AppEvent.kt | 6 ++++++ .../com/droidblossom/archive/util/ApiHandler.kt | 3 +++ 3 files changed, 19 insertions(+), 7 deletions(-) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/model/AppEvent.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseActivity.kt index 8d30cb9ac..377078874 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseActivity.kt @@ -16,6 +16,7 @@ import androidx.databinding.DataBindingUtil import androidx.databinding.ViewDataBinding import com.droidblossom.archive.presentation.customview.HomeSnackBarSmall import com.droidblossom.archive.presentation.customview.LoadingDialog +import com.droidblossom.archive.presentation.model.AppEvent import com.droidblossom.archive.util.ClipboardUtil import kotlinx.coroutines.Job import org.greenrobot.eventbus.EventBus @@ -49,15 +50,17 @@ abstract class BaseActivity(@LayoutRes v EventBus.getDefault().unregister(this); } - @Subscribe(threadMode = ThreadMode.MAIN) - fun printId(event: Map) { - // 스낵바 호출로 바꾸면 될듯? - Log.d("이베", "$event") - binding.root.let { rootView -> - //HomeSnackBarSmall(rootView).show() + @Subscribe + fun onEvent(event: AppEvent) { + when (event) { + is AppEvent.NetworkDisconnectedEvent -> { + showToastMessage("네트워크") + } + is AppEvent.NotificationReceivedEvent -> { + showToastMessage("알림") + } } } - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) _binding = DataBindingUtil.setContentView(this, layoutResource) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/model/AppEvent.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/model/AppEvent.kt new file mode 100644 index 000000000..77c34d07f --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/model/AppEvent.kt @@ -0,0 +1,6 @@ +package com.droidblossom.archive.presentation.model + +sealed class AppEvent { + object NetworkDisconnectedEvent : AppEvent() + data class NotificationReceivedEvent(val message: String) : AppEvent() +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/ApiHandler.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/ApiHandler.kt index 528dfe857..43db9418f 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/ApiHandler.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/ApiHandler.kt @@ -2,6 +2,8 @@ package com.droidblossom.archive.util import com.droidblossom.archive.ARchiveApplication import com.droidblossom.archive.di.RetrofitModule +import com.droidblossom.archive.presentation.model.AppEvent +import org.greenrobot.eventbus.EventBus import retrofit2.Response suspend fun apiHandler( @@ -9,6 +11,7 @@ suspend fun apiHandler( mapper: (T) -> R ): RetrofitResult { if (ARchiveApplication.isOnline().not()) { + EventBus.getDefault().post(AppEvent.NetworkDisconnectedEvent) return RetrofitResult.Error(Exception(RetrofitModule.NETWORK_EXCEPTION_OFFLINE)) } From 34179ce1fdfb3621ff64315e3224fdc17d851a5c Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Sun, 5 May 2024 15:31:08 +0900 Subject: [PATCH 243/301] =?UTF-8?q?refact=20:=20api=20=EB=B0=9B=EC=98=81,?= =?UTF-8?q?=20=EC=B9=9C=EA=B5=AC=20=EC=9A=94=EC=B2=AD=20=EB=B0=9B=EC=95=98?= =?UTF-8?q?=EC=9D=84=EB=95=8C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../response/FriendsSearchResponseDto.kt | 5 ++- .../model/friend/FriendsSearchResponse.kt | 3 +- .../addfriend/AddFriendViewModelImpl.kt | 2 +- .../archive/util/BindingAdapter.kt | 44 ++++++++++++------- .../src/main/res/layout/item_add_friend.xml | 7 +-- 5 files changed, 40 insertions(+), 21 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendsSearchResponseDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendsSearchResponseDto.kt index c5cb64518..ba823d1e2 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendsSearchResponseDto.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendsSearchResponseDto.kt @@ -9,6 +9,8 @@ data class FriendsSearchResponseDto( val originName : String?, val nickname: String, val isFriend: Boolean, + val isFriendInviteToFriend : Boolean, + val isFriendInviteToMe : Boolean, val isFriendRequest :Boolean, ) : Serializable { @@ -17,7 +19,8 @@ data class FriendsSearchResponseDto( profileUrl = this.profileUrl, nickname = this.nickname, isFriend = this.isFriend, - isFriendRequest = this.isFriendRequest, + isFriendInviteToFriend = this.isFriendInviteToFriend, + isFriendInviteToMe = this.isFriendInviteToMe, name = this.originName ?: "", isChecked = false ) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendsSearchResponse.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendsSearchResponse.kt index 494d880ac..c7e86f647 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendsSearchResponse.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/friend/FriendsSearchResponse.kt @@ -5,7 +5,8 @@ data class FriendsSearchResponse ( val profileUrl : String, val nickname : String, val isFriend : Boolean, - var isFriendRequest : Boolean, + var isFriendInviteToFriend : Boolean, + var isFriendInviteToMe : Boolean, var isChecked : Boolean, val name : String ) \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt index de9dba2b2..aa1b4faf7 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendViewModelImpl.kt @@ -164,7 +164,7 @@ class AddFriendViewModelImpl @Inject constructor( it.id == id } if (index != -1) { - newList[index].isFriendRequest = true + newList[index].isFriendInviteToFriend = true newList[index].isChecked = false viewModelScope.launch { _addFriendListUI.emit(newList) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/BindingAdapter.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/BindingAdapter.kt index 966ee760a..8667f2bb5 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/BindingAdapter.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/BindingAdapter.kt @@ -99,12 +99,12 @@ fun TextView.displayRemainingTime(totalSeconds: Int) { } @BindingAdapter("bind:culcLastDays") -fun TextView.culcLastDays(date : String){ +fun TextView.culcLastDays(date: String) { this.text = DateUtils.calcLastDate(date) } @BindingAdapter("bind:animateFAB") -fun CardView.animateFAB(y : Float){ +fun CardView.animateFAB(y: Float) { ObjectAnimator.ofFloat(this, "translationY", y).apply { start() } } @@ -126,7 +126,7 @@ fun TextView.setFormattedDateTime(dateString: String) { @BindingAdapter("bind:displayCreationDateTimeNullFormatted") fun TextView.setFormattedDateTimeNull(dateString: String?) { - if (!dateString.isNullOrEmpty()){ + if (!dateString.isNullOrEmpty()) { try { val parser = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX", Locale.getDefault()) val date = parser.parse(dateString) @@ -137,7 +137,7 @@ fun TextView.setFormattedDateTimeNull(dateString: String?) { } catch (e: Exception) { this.text = "날짜 형식 오류" } - }else{ + } else { this.text = "일반 캡슐 입니다" } } @@ -183,17 +183,20 @@ fun setLayoutHeight(view: View, height: Float) { } @BindingAdapter("bind:setCapsuleType2Img") -fun ImageView.setCapsuleType2Img(type: String?){ - when(type) { +fun ImageView.setCapsuleType2Img(type: String?) { + when (type) { "SECRET" -> { this.setImageResource(R.drawable.ic_secret_marker_24) } + "PUBLIC" -> { this.setImageResource(R.drawable.ic_public_marker_24) } - "GROUP" ->{ + + "GROUP" -> { this.setImageResource(R.drawable.ic_group_marker_24) } + else -> {} } } @@ -201,7 +204,8 @@ fun ImageView.setCapsuleType2Img(type: String?){ @BindingAdapter("bind:tabMarginEnd") fun TabLayout.setTabItemMargin(marginEndDp: Int) { val marginEndPx = TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_DIP, marginEndDp.toFloat(), resources.displayMetrics).toInt() + TypedValue.COMPLEX_UNIT_DIP, marginEndDp.toFloat(), resources.displayMetrics + ).toInt() val tabs = getChildAt(0) as ViewGroup for (i in 0 until tabs.childCount) { @@ -213,15 +217,25 @@ fun TabLayout.setTabItemMargin(marginEndDp: Int) { requestLayout() } -@BindingAdapter(value = ["bind:isFriend", "bind:isRequest","bind:friendName"], requireAll = false) -fun TextView.addFriendText(isFriend: Boolean, isRequest : Boolean, name : String) { - if (isFriend || isRequest){ - if (isFriend){ +@BindingAdapter( + value = ["bind:isFriend", "bind:isInviteToMe", "bind:isInviteToFriend", "bind:friendName"], + requireAll = false +) +fun TextView.addFriendText( + isFriend: Boolean, + isInviteToMe: Boolean, + isInviteToFriend: Boolean, + name: String +) { + if (isFriend || isInviteToFriend || isInviteToMe) { + if (isFriend) { this.text = "이미 친구입니다." - } else { + } else if (isInviteToFriend) { this.text = "요청을 보냈습니다." + } else { + this.text = "요청을 받았습니다,확인해주세요." } - }else { + } else { this.text = name } } @@ -232,7 +246,7 @@ fun TextView.formatCountWithK(count: Int, showDecimal: Boolean) { if (count < 1000) { this.text = count.toString() - }else{ + } else { if (showDecimal) { val thousands = count / 1000 val remainder = (count % 1000) / 100 diff --git a/frontend/ARchive/app/src/main/res/layout/item_add_friend.xml b/frontend/ARchive/app/src/main/res/layout/item_add_friend.xml index c1941fdad..4c899ac4b 100644 --- a/frontend/ARchive/app/src/main/res/layout/item_add_friend.xml +++ b/frontend/ARchive/app/src/main/res/layout/item_add_friend.xml @@ -18,7 +18,7 @@ + android:clickable="@{item.friend || item.isFriendInviteToMe || item.isFriendInviteToFriend ? false : true}"> + bind:isInviteToMe="@{item.friendInviteToMe}" + bind:isInviteToFriend="@{item.friendInviteToFriend}" /> Date: Sun, 5 May 2024 17:54:48 +0900 Subject: [PATCH 244/301] =?UTF-8?q?feat:=20=EB=82=B4=EA=B0=80=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=ED=95=9C=20=EA=B3=B5=EA=B0=9C=20=EC=BA=A1=EC=8A=90=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=20api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/dto/common/PagingRequestDto.kt | 6 ++++ .../MyPublicCapsulePageResponseDto.kt | 15 +++++++++ .../response/MyPublicCapsuleResponseDto.kt | 23 +++++++++++++ .../response/SecretCapsulePageResponseDto.kt | 4 +-- .../data/repository/PublicRepositoryImpl.kt | 10 ++++-- .../data/source/remote/api/PublicService.kt | 6 ++++ .../domain/model/secret/CapsulePageList.kt | 9 ++++++ .../open/MyPublicCapsulePageUseCase.kt | 32 +++++++++++++++++++ 8 files changed, 101 insertions(+), 4 deletions(-) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/common/PagingRequestDto.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/open/response/MyPublicCapsulePageResponseDto.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/open/response/MyPublicCapsuleResponseDto.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/secret/CapsulePageList.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/open/MyPublicCapsulePageUseCase.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/common/PagingRequestDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/common/PagingRequestDto.kt new file mode 100644 index 000000000..d28d9a90f --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/common/PagingRequestDto.kt @@ -0,0 +1,6 @@ +package com.droidblossom.archive.data.dto.common + +data class PagingRequestDto( + val size : Int, + val createdAt: String +) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/open/response/MyPublicCapsulePageResponseDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/open/response/MyPublicCapsulePageResponseDto.kt new file mode 100644 index 000000000..4f6182d3d --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/open/response/MyPublicCapsulePageResponseDto.kt @@ -0,0 +1,15 @@ +package com.droidblossom.archive.data.dto.open.response + +import com.droidblossom.archive.data.dto.secret.response.SecretCapsuleResponseDto +import com.droidblossom.archive.domain.model.secret.CapsulePageList + +data class MyPublicCapsulePageResponseDto ( + val publicCapsules: List, + val hasNext: Boolean, +){ + fun toModel() = CapsulePageList( + capsules = this.publicCapsules.map { it.toUIModel() }, + hasNext = this.hasNext, + hasPrevious = this.hasNext, + ) +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/open/response/MyPublicCapsuleResponseDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/open/response/MyPublicCapsuleResponseDto.kt new file mode 100644 index 000000000..417457992 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/open/response/MyPublicCapsuleResponseDto.kt @@ -0,0 +1,23 @@ +package com.droidblossom.archive.data.dto.open.response + +import com.droidblossom.archive.presentation.model.mypage.CapsuleData + +data class MyPublicCapsuleResponseDto( + val capsuleId : Long, + val skinUrl : String, + val dueDate : String?, + val createdAt : String, + val title : String, + val isOpened : Boolean, + val capsuleType : String, +){ + fun toUIModel() = CapsuleData( + capsuleId = this.capsuleId, + capsuleSkinUrl = this.skinUrl, + createdDate = this.createdAt, + dueDate = this.dueDate, + isOpened = this.isOpened, + title = this.title, + capsuleType = this.capsuleType + ) +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/secret/response/SecretCapsulePageResponseDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/secret/response/SecretCapsulePageResponseDto.kt index 9e311eb77..90ab7c282 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/secret/response/SecretCapsulePageResponseDto.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/secret/response/SecretCapsulePageResponseDto.kt @@ -1,13 +1,13 @@ package com.droidblossom.archive.data.dto.secret.response -import com.droidblossom.archive.domain.model.secret.SecretCapsulePage +import com.droidblossom.archive.domain.model.secret.CapsulePageList data class SecretCapsulePageResponseDto( val capsules: List, val hasNext: Boolean, val hasPrevious: Boolean ){ - fun toModel() = SecretCapsulePage( + fun toModel() = CapsulePageList( capsules = this.capsules.map { it.toUIModel() }, hasNext = this.hasNext, hasPrevious = this.hasPrevious, diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/PublicRepositoryImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/PublicRepositoryImpl.kt index 14cea5e7f..b4e7a5aaa 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/PublicRepositoryImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/PublicRepositoryImpl.kt @@ -4,13 +4,15 @@ import com.droidblossom.archive.data.dto.ResponseBody import com.droidblossom.archive.data.dto.common.CapsuleCreateRequestDto import com.droidblossom.archive.data.dto.common.CapsuleDetailResponseDto import com.droidblossom.archive.data.dto.common.CapsuleSummaryResponseDto +import com.droidblossom.archive.data.dto.common.PagingRequestDto import com.droidblossom.archive.data.dto.common.toModel -import com.droidblossom.archive.data.dto.open.request.PublicCapsuleSliceRequestDto +import com.droidblossom.archive.data.dto.open.response.MyPublicCapsulePageResponseDto import com.droidblossom.archive.data.dto.open.response.PublicCapsuleSliceResponseDto import com.droidblossom.archive.data.source.remote.api.PublicService import com.droidblossom.archive.domain.model.common.CapsuleDetail import com.droidblossom.archive.domain.model.common.CapsuleSummaryResponse import com.droidblossom.archive.domain.model.open.PublicCapsuleSliceResponse +import com.droidblossom.archive.domain.model.secret.CapsulePageList import com.droidblossom.archive.domain.repository.PublicRepository import com.droidblossom.archive.util.RetrofitResult import com.droidblossom.archive.util.apiHandler @@ -31,7 +33,11 @@ class PublicRepositoryImpl @Inject constructor( return apiHandler({ api.getPublicCapsuleDetailApi(capsuleId) }) { response: ResponseBody -> response.result.toModel()} } - override suspend fun getPublicCapsulesPage(request: PublicCapsuleSliceRequestDto): RetrofitResult { + override suspend fun getPublicCapsulesPage(request: PagingRequestDto): RetrofitResult { return apiHandler({ api.getPublicCapsulesPageApi(request.size, request.createdAt) }){ response: ResponseBody -> response.result.toModel() } } + + override suspend fun getMyPublicCapsulesPage(request: PagingRequestDto): RetrofitResult { + return apiHandler({ api.getMyPublicCapsulePageApi(request.size, request.createdAt) }){ response: ResponseBody -> response.result.toModel() } + } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/PublicService.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/PublicService.kt index 3644761e1..2a267ee0c 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/PublicService.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/PublicService.kt @@ -4,6 +4,7 @@ import com.droidblossom.archive.data.dto.ResponseBody import com.droidblossom.archive.data.dto.common.CapsuleCreateRequestDto import com.droidblossom.archive.data.dto.common.CapsuleDetailResponseDto import com.droidblossom.archive.data.dto.common.CapsuleSummaryResponseDto +import com.droidblossom.archive.data.dto.open.response.MyPublicCapsulePageResponseDto import com.droidblossom.archive.data.dto.open.response.PublicCapsuleSliceResponseDto import retrofit2.Response import retrofit2.http.Body @@ -35,4 +36,9 @@ interface PublicService { @Query("created_at") createdAt: String ) : Response> + @GET("public-capsules/my") + suspend fun getMyPublicCapsulePageApi( + @Query("size") size : Int, + @Query("created_at") createdAt: String + ) : Response> } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/secret/CapsulePageList.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/secret/CapsulePageList.kt new file mode 100644 index 000000000..5a8841f34 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/secret/CapsulePageList.kt @@ -0,0 +1,9 @@ +package com.droidblossom.archive.domain.model.secret + +import com.droidblossom.archive.presentation.model.mypage.CapsuleData + +data class CapsulePageList( + val capsules: List, + val hasNext: Boolean, + val hasPrevious: Boolean +) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/open/MyPublicCapsulePageUseCase.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/open/MyPublicCapsulePageUseCase.kt new file mode 100644 index 000000000..55bee8c79 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/open/MyPublicCapsulePageUseCase.kt @@ -0,0 +1,32 @@ +package com.droidblossom.archive.domain.usecase.open + +import android.util.Log +import com.droidblossom.archive.data.dto.common.PagingRequestDto +import com.droidblossom.archive.domain.model.secret.CapsulePageList +import com.droidblossom.archive.domain.repository.PublicRepository +import com.droidblossom.archive.util.RetrofitResult +import com.droidblossom.archive.util.onException +import com.droidblossom.archive.util.onFail +import com.droidblossom.archive.util.onSuccess +import kotlinx.coroutines.flow.flow +import javax.inject.Inject + +class MyPublicCapsulePageUseCase @Inject constructor( + private val repository: PublicRepository +) { + suspend operator fun invoke(request: PagingRequestDto) = + flow> { + try { + emit(repository.getMyPublicCapsulesPage(request).onSuccess { + + }.onFail { + + }.onException { + throw Exception(it) + }) + } catch (e: Exception) { + Log.d("예외확인", "$e") + e.printStackTrace() + } + } +} \ No newline at end of file From 35058ab9dcea2b466bbdffc7bb2382f1399a6599 Mon Sep 17 00:00:00 2001 From: comst19 Date: Sun, 5 May 2024 17:56:51 +0900 Subject: [PATCH 245/301] =?UTF-8?q?refact:=20=ED=8E=98=EC=9D=B4=EC=A7=95?= =?UTF-8?q?=20API=20=EC=9A=94=EC=B2=AD=20=ED=91=9C=EC=A4=80=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../request/CapsuleSkinsPageRequestDto.kt | 6 ---- .../dto/friend/response/FriendResponseDto.kt | 14 ++++---- .../request/PublicCapsuleSliceRequestDto.kt | 6 ---- .../request/SecretCapsulePageRequestDto.kt | 6 ---- .../repository/CapsuleSkinRepositoryImpl.kt | 9 ++--- .../data/repository/FriendRepositoryImpl.kt | 9 ++--- .../data/repository/KakaoRepositoryImpl.kt | 8 ++--- .../data/repository/MemberRepositoryImpl.kt | 8 ++--- .../data/repository/SecretRepositoryImpl.kt | 6 ++-- .../capsule_skin/CapsuleSkinsPageRequest.kt | 14 -------- .../domain/model/secret/SecretCapsulePage.kt | 9 ----- .../model/secret/SecretCapsulePageRequest.kt | 14 -------- .../repository/CapsuleSkinRepository.kt | 5 ++- .../domain/repository/FriendRepository.kt | 5 +-- .../domain/repository/MemberRepository.kt | 3 +- .../domain/repository/PublicRepository.kt | 6 ++-- .../domain/repository/SecretRepository.kt | 6 ++-- .../capsule_skin/CapsuleSkinsMakeUseCase.kt | 1 - .../capsule_skin/CapsuleSkinsPageUseCase.kt | 4 +-- .../capsule_skin/CapsuleSkinsSearchUseCase.kt | 1 - .../usecase/friend/FriendsPageUseCase.kt | 5 +-- .../friend/FriendsRequestsPageUseCase.kt | 5 +-- .../usecase/member/GetNotificationsUseCase.kt | 5 +-- .../usecase/open/PublicCapsulePageUseCase.kt | 6 ++-- .../secret/SecretCapsulePageUseCase.kt | 8 ++--- .../CreateCapsuleViewModelImpl.kt | 5 ++- .../notification/NotificationViewModelImpl.kt | 16 ++++++--- .../ui/mypage/MyPageViewModelImpl.kt | 36 +++++++++++++------ .../ui/mypage/friend/FriendViewModelImpl.kt | 8 ++++- .../friendaccept/FriendAcceptViewModelImpl.kt | 8 ++++- .../presentation/ui/skin/SkinViewModelImpl.kt | 8 ++--- .../page/friend/SocialFriendViewModelImpl.kt | 7 ++-- 32 files changed, 113 insertions(+), 144 deletions(-) delete mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/capsule_skin/request/CapsuleSkinsPageRequestDto.kt delete mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/open/request/PublicCapsuleSliceRequestDto.kt delete mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/secret/request/SecretCapsulePageRequestDto.kt delete mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/capsule_skin/CapsuleSkinsPageRequest.kt delete mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/secret/SecretCapsulePage.kt delete mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/secret/SecretCapsulePageRequest.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/capsule_skin/request/CapsuleSkinsPageRequestDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/capsule_skin/request/CapsuleSkinsPageRequestDto.kt deleted file mode 100644 index e33b9f8fc..000000000 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/capsule_skin/request/CapsuleSkinsPageRequestDto.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.droidblossom.archive.data.dto.capsule_skin.request - -data class CapsuleSkinsPageRequestDto ( - val size : Int, - val createdAt: String -) \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendResponseDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendResponseDto.kt index 67d8c2fbe..729a3c519 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendResponseDto.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/friend/response/FriendResponseDto.kt @@ -8,11 +8,11 @@ data class FriendResponseDto( val nickname: String, val profileUrl: String ) { - fun toModel() = Friend( - createdAt = this.createdAt, - id = this.id, - nickname = this.nickname, - profileUrl = this.profileUrl, - isOpenDelete = false - ) + fun toModel() = Friend( + createdAt = this.createdAt, + id = this.id, + nickname = this.nickname, + profileUrl = this.profileUrl, + isOpenDelete = false + ) } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/open/request/PublicCapsuleSliceRequestDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/open/request/PublicCapsuleSliceRequestDto.kt deleted file mode 100644 index eaf60cc29..000000000 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/open/request/PublicCapsuleSliceRequestDto.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.droidblossom.archive.data.dto.open.request - -data class PublicCapsuleSliceRequestDto ( - val size : Int, - val createdAt: String -) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/secret/request/SecretCapsulePageRequestDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/secret/request/SecretCapsulePageRequestDto.kt deleted file mode 100644 index 6c55d8f1a..000000000 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/secret/request/SecretCapsulePageRequestDto.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.droidblossom.archive.data.dto.secret.request - -data class SecretCapsulePageRequestDto( - val size : Int, - val createdAt: String -) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/CapsuleSkinRepositoryImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/CapsuleSkinRepositoryImpl.kt index bc5435f28..ecc7ef752 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/CapsuleSkinRepositoryImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/CapsuleSkinRepositoryImpl.kt @@ -2,31 +2,26 @@ package com.droidblossom.archive.data.repository import com.droidblossom.archive.data.dto.ResponseBody import com.droidblossom.archive.data.dto.capsule_skin.request.CapsuleSkinsMakeRequestDto -import com.droidblossom.archive.data.dto.capsule_skin.request.CapsuleSkinsPageRequestDto import com.droidblossom.archive.data.dto.capsule_skin.request.CapsuleSkinsSearchPageRequestDto import com.droidblossom.archive.data.dto.capsule_skin.response.CapsuleSkinsMakeResponseDto import com.droidblossom.archive.data.dto.capsule_skin.response.CapsuleSkinsPageResponseDto import com.droidblossom.archive.data.dto.capsule_skin.response.CapsuleSkinsSearchPageResponseDto -import com.droidblossom.archive.data.dto.common.CapsuleSkinSummaryResponseDto +import com.droidblossom.archive.data.dto.common.PagingRequestDto import com.droidblossom.archive.data.dto.common.toModel import com.droidblossom.archive.data.source.remote.api.CapsuleSkinService import com.droidblossom.archive.domain.model.capsule_skin.CapsuleSkinsMakeResponse import com.droidblossom.archive.domain.model.capsule_skin.CapsuleSkinsPageResponse import com.droidblossom.archive.domain.model.capsule_skin.CapsuleSkinsSearchPageResponse -import com.droidblossom.archive.domain.model.common.CapsuleSkinSummary import com.droidblossom.archive.domain.repository.CapsuleSkinRepository import com.droidblossom.archive.util.RetrofitResult import com.droidblossom.archive.util.apiHandler -import okhttp3.MediaType.Companion.toMediaType -import okhttp3.MultipartBody -import okhttp3.RequestBody.Companion.asRequestBody import javax.inject.Inject class CapsuleSkinRepositoryImpl @Inject constructor( private val api : CapsuleSkinService ) : CapsuleSkinRepository{ - override suspend fun getCapsuleSkinPage(request: CapsuleSkinsPageRequestDto): RetrofitResult { + override suspend fun getCapsuleSkinPage(request: PagingRequestDto): RetrofitResult { return apiHandler({ api.getCapsuleSkinsPageApi(request.size, request.createdAt) }) { response: ResponseBody -> response.result.toModel() } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/FriendRepositoryImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/FriendRepositoryImpl.kt index b38a10931..6590753ec 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/FriendRepositoryImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/FriendRepositoryImpl.kt @@ -2,6 +2,7 @@ package com.droidblossom.archive.data.repository import com.droidblossom.archive.data.dto.ResponseBody import com.droidblossom.archive.data.dto.capsule_skin.response.CapsuleSkinsPageResponseDto +import com.droidblossom.archive.data.dto.common.PagingRequestDto import com.droidblossom.archive.data.dto.common.toModel import com.droidblossom.archive.data.dto.friend.request.FriendAcceptRequestDto import com.droidblossom.archive.data.dto.friend.request.FriendReqRequestDto @@ -46,12 +47,12 @@ class FriendRepositoryImpl @Inject constructor( return apiHandler({ api.postFriendsSearchPhoneApi(request) }) {response: ResponseBody -> response.result.toModel()} } - override suspend fun getFriendsPage(size: Int, createdAt: String): RetrofitResult { - return apiHandler({api.getFriendsPageApi(size, createdAt)}) {response: ResponseBody -> response.result.toModel()} + override suspend fun getFriendsPage(request: PagingRequestDto): RetrofitResult { + return apiHandler({api.getFriendsPageApi(request.size, request.createdAt)}) {response: ResponseBody -> response.result.toModel()} } - override suspend fun getFriendsRequestsPage(size: Int, createdAt: String): RetrofitResult { - return apiHandler({api.getFriendsRequestsPageApi(size, createdAt)}) {response: ResponseBody -> response.result.toModel()} + override suspend fun getFriendsRequestsPage(request: PagingRequestDto): RetrofitResult { + return apiHandler({api.getFriendsRequestsPageApi(request.size, request.createdAt)}) {response: ResponseBody -> response.result.toModel()} } override suspend fun deleteFriend(friendId: Long): RetrofitResult { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/KakaoRepositoryImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/KakaoRepositoryImpl.kt index b7ffab30f..6f64eafa6 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/KakaoRepositoryImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/KakaoRepositoryImpl.kt @@ -5,15 +5,15 @@ import com.droidblossom.archive.domain.repository.KakaoRepository import javax.inject.Inject import javax.inject.Named -class KakaoRepositoryImpl @Inject constructor( +class KakaoRepositoryImpl @Inject constructor( @Named("kakao") private val api: KakaoService ) : KakaoRepository { override suspend fun getAddress(x: String, y: String): String { - val response = api.getAddressApi(x,y) - return if (response.isSuccessful){ + val response = api.getAddressApi(x, y) + return if (response.isSuccessful) { response.body()?.documents?.first()?.road_address?.road_name ?: "null" - }else{ + } else { "null" } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/MemberRepositoryImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/MemberRepositoryImpl.kt index 72110142a..9150681d8 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/MemberRepositoryImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/MemberRepositoryImpl.kt @@ -2,6 +2,7 @@ package com.droidblossom.archive.data.repository import com.droidblossom.archive.data.dto.ResponseBody import com.droidblossom.archive.data.dto.auth.response.HealthResponseDto +import com.droidblossom.archive.data.dto.common.PagingRequestDto import com.droidblossom.archive.data.dto.common.toModel import com.droidblossom.archive.data.dto.member.request.FcmTokenRequsetDto import com.droidblossom.archive.data.dto.member.request.MemberStatusRequestDto @@ -39,13 +40,12 @@ class MemberRepositoryImpl @Inject constructor( } override suspend fun getNotifications( - size: Int, - createdAt: String + request: PagingRequestDto ): RetrofitResult { return apiHandler({ api.getNotifications( - size = size, - createdAt = createdAt + size = request.size, + createdAt = request.createdAt ) }) { response: ResponseBody -> response.result.toModel() } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/SecretRepositoryImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/SecretRepositoryImpl.kt index a9fddeacf..acc06cb7d 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/SecretRepositoryImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/SecretRepositoryImpl.kt @@ -4,15 +4,15 @@ import com.droidblossom.archive.data.dto.ResponseBody import com.droidblossom.archive.data.dto.common.toModel import com.droidblossom.archive.data.dto.common.CapsuleCreateRequestDto import com.droidblossom.archive.data.dto.secret.request.SecretCapsuleModifyRequestDto -import com.droidblossom.archive.data.dto.secret.request.SecretCapsulePageRequestDto import com.droidblossom.archive.data.dto.common.CapsuleDetailResponseDto import com.droidblossom.archive.data.dto.secret.response.SecretCapsuleModifyResponseDto import com.droidblossom.archive.data.dto.secret.response.SecretCapsulePageResponseDto import com.droidblossom.archive.data.dto.common.CapsuleSummaryResponseDto +import com.droidblossom.archive.data.dto.common.PagingRequestDto import com.droidblossom.archive.data.source.remote.api.SecretService import com.droidblossom.archive.domain.model.common.CapsuleDetail import com.droidblossom.archive.domain.model.secret.SecretCapsuleModify -import com.droidblossom.archive.domain.model.secret.SecretCapsulePage +import com.droidblossom.archive.domain.model.secret.CapsulePageList import com.droidblossom.archive.domain.model.common.CapsuleSummaryResponse import com.droidblossom.archive.domain.repository.SecretRepository import com.droidblossom.archive.util.RetrofitResult @@ -23,7 +23,7 @@ class SecretRepositoryImpl @Inject constructor( private val api: SecretService ) : SecretRepository { - override suspend fun getSecretCapsulePage(request: SecretCapsulePageRequestDto): RetrofitResult { + override suspend fun getSecretCapsulePage(request: PagingRequestDto): RetrofitResult { return apiHandler({ api.getSecretCapsulePageApi(request.size, request.createdAt) }) { response: ResponseBody -> response.result.toModel() } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/capsule_skin/CapsuleSkinsPageRequest.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/capsule_skin/CapsuleSkinsPageRequest.kt deleted file mode 100644 index b6d71c397..000000000 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/capsule_skin/CapsuleSkinsPageRequest.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.droidblossom.archive.domain.model.capsule_skin - -import com.droidblossom.archive.data.dto.capsule_skin.request.CapsuleSkinsPageRequestDto - -data class CapsuleSkinsPageRequest( - val size : Int, - val createdAt: String -){ - fun toDto()= CapsuleSkinsPageRequestDto( - size = this.size, - createdAt = this.createdAt - ) -} - diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/secret/SecretCapsulePage.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/secret/SecretCapsulePage.kt deleted file mode 100644 index 4de497c10..000000000 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/secret/SecretCapsulePage.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.droidblossom.archive.domain.model.secret - -import com.droidblossom.archive.presentation.model.mypage.CapsuleData - -data class SecretCapsulePage( - val capsules: List, - val hasNext: Boolean, - val hasPrevious: Boolean -) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/secret/SecretCapsulePageRequest.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/secret/SecretCapsulePageRequest.kt deleted file mode 100644 index 13c50f515..000000000 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/model/secret/SecretCapsulePageRequest.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.droidblossom.archive.domain.model.secret - -import com.droidblossom.archive.data.dto.secret.request.SecretCapsulePageRequestDto - -data class SecretCapsulePageRequest( - val size : Int, - val createdAt: String -){ - fun toDto()= SecretCapsulePageRequestDto( - size = this.size, - createdAt = this.createdAt - ) -} - diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/CapsuleSkinRepository.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/CapsuleSkinRepository.kt index ca5a9c47b..3b7f9976d 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/CapsuleSkinRepository.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/CapsuleSkinRepository.kt @@ -1,17 +1,16 @@ package com.droidblossom.archive.domain.repository import com.droidblossom.archive.data.dto.capsule_skin.request.CapsuleSkinsMakeRequestDto -import com.droidblossom.archive.data.dto.capsule_skin.request.CapsuleSkinsPageRequestDto import com.droidblossom.archive.data.dto.capsule_skin.request.CapsuleSkinsSearchPageRequestDto +import com.droidblossom.archive.data.dto.common.PagingRequestDto import com.droidblossom.archive.domain.model.capsule_skin.CapsuleSkinsMakeResponse import com.droidblossom.archive.domain.model.capsule_skin.CapsuleSkinsPageResponse import com.droidblossom.archive.domain.model.capsule_skin.CapsuleSkinsSearchPageResponse -import com.droidblossom.archive.domain.model.common.CapsuleSkinSummary import com.droidblossom.archive.util.RetrofitResult interface CapsuleSkinRepository { - suspend fun getCapsuleSkinPage(request: CapsuleSkinsPageRequestDto) : RetrofitResult + suspend fun getCapsuleSkinPage(request: PagingRequestDto) : RetrofitResult suspend fun postCapsuleSkinMake(request: CapsuleSkinsMakeRequestDto) : RetrofitResult diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/FriendRepository.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/FriendRepository.kt index cc5b883dc..d79d2ced2 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/FriendRepository.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/FriendRepository.kt @@ -1,5 +1,6 @@ package com.droidblossom.archive.domain.repository +import com.droidblossom.archive.data.dto.common.PagingRequestDto import com.droidblossom.archive.data.dto.friend.request.FriendAcceptRequestDto import com.droidblossom.archive.data.dto.friend.request.FriendReqRequestDto import com.droidblossom.archive.data.dto.friend.request.FriendsReqRequestDto @@ -18,8 +19,8 @@ interface FriendRepository { suspend fun postFriendsAcceptRequest(request: FriendAcceptRequestDto) : RetrofitResult suspend fun postFriendsSearch(request: FriendsSearchRequestDto) : RetrofitResult suspend fun postFriendsSearchPhone(request : FriendsSearchPhoneRequestDto) : RetrofitResult - suspend fun getFriendsPage(size: Int ,createdAt : String) : RetrofitResult - suspend fun getFriendsRequestsPage(size: Int, createdAt: String) : RetrofitResult + suspend fun getFriendsPage(request: PagingRequestDto) : RetrofitResult + suspend fun getFriendsRequestsPage(request: PagingRequestDto) : RetrofitResult suspend fun deleteFriend(friendId: Long) : RetrofitResult suspend fun deleteFriendDeny(friendId: Long) : RetrofitResult } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/MemberRepository.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/MemberRepository.kt index 4ccd43725..74ff6f581 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/MemberRepository.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/MemberRepository.kt @@ -1,5 +1,6 @@ package com.droidblossom.archive.domain.repository +import com.droidblossom.archive.data.dto.common.PagingRequestDto import com.droidblossom.archive.data.dto.member.request.FcmTokenRequsetDto import com.droidblossom.archive.data.dto.member.request.MemberStatusRequestDto import com.droidblossom.archive.data.dto.member.request.NotificationEnabledRequestDto @@ -20,7 +21,7 @@ interface MemberRepository { suspend fun patchFcmToken(request: FcmTokenRequsetDto): RetrofitResult - suspend fun getNotifications(size:Int, createdAt: String) : RetrofitResult + suspend fun getNotifications(request: PagingRequestDto) : RetrofitResult suspend fun getText() : RetrofitResult } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/PublicRepository.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/PublicRepository.kt index 7794d61b9..1ad66d3c0 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/PublicRepository.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/PublicRepository.kt @@ -1,10 +1,11 @@ package com.droidblossom.archive.domain.repository import com.droidblossom.archive.data.dto.common.CapsuleCreateRequestDto -import com.droidblossom.archive.data.dto.open.request.PublicCapsuleSliceRequestDto +import com.droidblossom.archive.data.dto.common.PagingRequestDto import com.droidblossom.archive.domain.model.common.CapsuleDetail import com.droidblossom.archive.domain.model.common.CapsuleSummaryResponse import com.droidblossom.archive.domain.model.open.PublicCapsuleSliceResponse +import com.droidblossom.archive.domain.model.secret.CapsulePageList import com.droidblossom.archive.util.RetrofitResult interface PublicRepository { @@ -15,6 +16,7 @@ interface PublicRepository { suspend fun getPublicCapsuleDetail (capsuleId: Long) : RetrofitResult - suspend fun getPublicCapsulesPage(request: PublicCapsuleSliceRequestDto) : RetrofitResult + suspend fun getPublicCapsulesPage(request: PagingRequestDto) : RetrofitResult + suspend fun getMyPublicCapsulesPage(request: PagingRequestDto) : RetrofitResult } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/SecretRepository.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/SecretRepository.kt index ec5c8410c..74d8f98ca 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/SecretRepository.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/SecretRepository.kt @@ -1,17 +1,17 @@ package com.droidblossom.archive.domain.repository import com.droidblossom.archive.data.dto.common.CapsuleCreateRequestDto +import com.droidblossom.archive.data.dto.common.PagingRequestDto import com.droidblossom.archive.data.dto.secret.request.SecretCapsuleModifyRequestDto -import com.droidblossom.archive.data.dto.secret.request.SecretCapsulePageRequestDto import com.droidblossom.archive.domain.model.common.CapsuleDetail import com.droidblossom.archive.domain.model.secret.SecretCapsuleModify -import com.droidblossom.archive.domain.model.secret.SecretCapsulePage +import com.droidblossom.archive.domain.model.secret.CapsulePageList import com.droidblossom.archive.domain.model.common.CapsuleSummaryResponse import com.droidblossom.archive.util.RetrofitResult interface SecretRepository { - suspend fun getSecretCapsulePage (request: SecretCapsulePageRequestDto) : RetrofitResult + suspend fun getSecretCapsulePage (request: PagingRequestDto) : RetrofitResult suspend fun createSecretCapsule (request: CapsuleCreateRequestDto) : RetrofitResult diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule_skin/CapsuleSkinsMakeUseCase.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule_skin/CapsuleSkinsMakeUseCase.kt index 9b969a097..a95ab7af3 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule_skin/CapsuleSkinsMakeUseCase.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule_skin/CapsuleSkinsMakeUseCase.kt @@ -2,7 +2,6 @@ package com.droidblossom.archive.domain.usecase.capsule_skin import android.util.Log import com.droidblossom.archive.data.dto.capsule_skin.request.CapsuleSkinsMakeRequestDto -import com.droidblossom.archive.data.dto.capsule_skin.request.CapsuleSkinsPageRequestDto import com.droidblossom.archive.domain.repository.CapsuleSkinRepository import com.droidblossom.archive.util.onException import com.droidblossom.archive.util.onFail diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule_skin/CapsuleSkinsPageUseCase.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule_skin/CapsuleSkinsPageUseCase.kt index 686260f9a..66035b59f 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule_skin/CapsuleSkinsPageUseCase.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule_skin/CapsuleSkinsPageUseCase.kt @@ -1,7 +1,7 @@ package com.droidblossom.archive.domain.usecase.capsule_skin import android.util.Log -import com.droidblossom.archive.data.dto.capsule_skin.request.CapsuleSkinsPageRequestDto +import com.droidblossom.archive.data.dto.common.PagingRequestDto import com.droidblossom.archive.domain.repository.CapsuleSkinRepository import com.droidblossom.archive.util.onException import com.droidblossom.archive.util.onFail @@ -13,7 +13,7 @@ class CapsuleSkinsPageUseCase @Inject constructor( private val repository: CapsuleSkinRepository ){ - suspend operator fun invoke(request: CapsuleSkinsPageRequestDto) = + suspend operator fun invoke(request: PagingRequestDto) = flow{ try { emit(repository.getCapsuleSkinPage(request).onSuccess { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule_skin/CapsuleSkinsSearchUseCase.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule_skin/CapsuleSkinsSearchUseCase.kt index 54bf855d7..cdb932876 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule_skin/CapsuleSkinsSearchUseCase.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule_skin/CapsuleSkinsSearchUseCase.kt @@ -1,7 +1,6 @@ package com.droidblossom.archive.domain.usecase.capsule_skin import android.util.Log -import com.droidblossom.archive.data.dto.capsule_skin.request.CapsuleSkinsPageRequestDto import com.droidblossom.archive.data.dto.capsule_skin.request.CapsuleSkinsSearchPageRequestDto import com.droidblossom.archive.domain.repository.CapsuleSkinRepository import com.droidblossom.archive.util.onException diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsPageUseCase.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsPageUseCase.kt index 56990988a..70b3a1609 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsPageUseCase.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsPageUseCase.kt @@ -1,6 +1,7 @@ package com.droidblossom.archive.domain.usecase.friend import android.util.Log +import com.droidblossom.archive.data.dto.common.PagingRequestDto import com.droidblossom.archive.domain.repository.FriendRepository import com.droidblossom.archive.util.onException import com.droidblossom.archive.util.onFail @@ -11,10 +12,10 @@ import javax.inject.Inject class FriendsPageUseCase @Inject constructor( private val repository: FriendRepository ) { - suspend operator fun invoke(size: Int, createdAt: String) = + suspend operator fun invoke(request: PagingRequestDto) = flow { try { - emit(repository.getFriendsPage(size, createdAt).onSuccess { + emit(repository.getFriendsPage(request).onSuccess { }.onFail { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsRequestsPageUseCase.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsRequestsPageUseCase.kt index 7c733e7c4..f4ad6f920 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsRequestsPageUseCase.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/friend/FriendsRequestsPageUseCase.kt @@ -1,6 +1,7 @@ package com.droidblossom.archive.domain.usecase.friend import android.util.Log +import com.droidblossom.archive.data.dto.common.PagingRequestDto import com.droidblossom.archive.domain.repository.FriendRepository import com.droidblossom.archive.util.onException import com.droidblossom.archive.util.onFail @@ -11,10 +12,10 @@ import javax.inject.Inject class FriendsRequestsPageUseCase @Inject constructor( private val repository: FriendRepository ) { - suspend operator fun invoke(size: Int, createdAt: String) = + suspend operator fun invoke(request: PagingRequestDto) = flow { try { - emit(repository.getFriendsRequestsPage(size, createdAt).onSuccess { + emit(repository.getFriendsRequestsPage(request).onSuccess { }.onFail { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/member/GetNotificationsUseCase.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/member/GetNotificationsUseCase.kt index 16cde24cc..c11658d60 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/member/GetNotificationsUseCase.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/member/GetNotificationsUseCase.kt @@ -1,6 +1,7 @@ package com.droidblossom.archive.domain.usecase.member import android.util.Log +import com.droidblossom.archive.data.dto.common.PagingRequestDto import com.droidblossom.archive.data.dto.member.request.FcmTokenRequsetDto import com.droidblossom.archive.data.dto.member.request.NotificationEnabledRequestDto import com.droidblossom.archive.domain.model.member.NotificationPage @@ -15,10 +16,10 @@ import javax.inject.Inject class GetNotificationsUseCase @Inject constructor( private val repository: MemberRepository ) { - suspend operator fun invoke(size: Int, createdAt : String) = + suspend operator fun invoke(request: PagingRequestDto) = flow> { try { - emit(repository.getNotifications(size = size, createdAt = createdAt) + emit(repository.getNotifications(request) .onSuccess { }.onFail { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/open/PublicCapsulePageUseCase.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/open/PublicCapsulePageUseCase.kt index c61d93b67..a66ef398e 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/open/PublicCapsulePageUseCase.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/open/PublicCapsulePageUseCase.kt @@ -1,10 +1,8 @@ package com.droidblossom.archive.domain.usecase.open import android.util.Log -import com.droidblossom.archive.data.dto.open.request.PublicCapsuleSliceRequestDto -import com.droidblossom.archive.data.dto.secret.request.SecretCapsulePageRequestDto +import com.droidblossom.archive.data.dto.common.PagingRequestDto import com.droidblossom.archive.domain.model.open.PublicCapsuleSliceResponse -import com.droidblossom.archive.domain.model.secret.SecretCapsulePage import com.droidblossom.archive.domain.repository.PublicRepository import com.droidblossom.archive.util.RetrofitResult import com.droidblossom.archive.util.onException @@ -16,7 +14,7 @@ import javax.inject.Inject class PublicCapsulePageUseCase @Inject constructor( private val repository: PublicRepository ) { - suspend operator fun invoke(request: PublicCapsuleSliceRequestDto) = + suspend operator fun invoke(request: PagingRequestDto) = flow> { try { emit(repository.getPublicCapsulesPage(request).onSuccess { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/secret/SecretCapsulePageUseCase.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/secret/SecretCapsulePageUseCase.kt index 7b2140feb..2a2fc2417 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/secret/SecretCapsulePageUseCase.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/secret/SecretCapsulePageUseCase.kt @@ -1,8 +1,8 @@ package com.droidblossom.archive.domain.usecase.secret import android.util.Log -import com.droidblossom.archive.data.dto.secret.request.SecretCapsulePageRequestDto -import com.droidblossom.archive.domain.model.secret.SecretCapsulePage +import com.droidblossom.archive.data.dto.common.PagingRequestDto +import com.droidblossom.archive.domain.model.secret.CapsulePageList import com.droidblossom.archive.domain.repository.SecretRepository import com.droidblossom.archive.util.RetrofitResult import com.droidblossom.archive.util.onException @@ -14,8 +14,8 @@ import javax.inject.Inject class SecretCapsulePageUseCase @Inject constructor( private val repository: SecretRepository ) { - suspend operator fun invoke(request: SecretCapsulePageRequestDto) = - flow> { + suspend operator fun invoke(request: PagingRequestDto) = + flow> { try { emit(repository.getSecretCapsulePage(request).onSuccess { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt index cc066f992..f046d84e9 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt @@ -3,7 +3,7 @@ package com.droidblossom.archive.presentation.ui.home.createcapsule import android.net.Uri import android.util.Log import androidx.lifecycle.viewModelScope -import com.droidblossom.archive.data.dto.capsule_skin.request.CapsuleSkinsPageRequestDto +import com.droidblossom.archive.data.dto.common.PagingRequestDto import com.droidblossom.archive.domain.model.common.AddressData import com.droidblossom.archive.domain.model.common.CapsuleSkinSummary import com.droidblossom.archive.domain.model.common.Dummy @@ -17,7 +17,6 @@ import com.droidblossom.archive.domain.usecase.open.PublicCapsuleCreateUseCase import com.droidblossom.archive.domain.usecase.s3.S3UrlsGetUseCase import com.droidblossom.archive.domain.usecase.secret.SecretCapsuleCreateUseCase import com.droidblossom.archive.presentation.base.BaseViewModel -import com.droidblossom.archive.presentation.base.BaseViewModel.Companion.throttleFirst import com.droidblossom.archive.util.DateUtils import com.droidblossom.archive.util.S3Util import com.droidblossom.archive.util.onFail @@ -270,7 +269,7 @@ class CreateCapsuleViewModelImpl @Inject constructor( getLstJob?.cancel() getLstJob = viewModelScope.launch { capsuleSkinsPageUseCase( - CapsuleSkinsPageRequestDto( + PagingRequestDto( 15, _lastCreatedSkinTime.value ) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationViewModelImpl.kt index 81ff32040..5dbeb54ba 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationViewModelImpl.kt @@ -1,6 +1,7 @@ package com.droidblossom.archive.presentation.ui.home.notification import androidx.lifecycle.viewModelScope +import com.droidblossom.archive.data.dto.common.PagingRequestDto import com.droidblossom.archive.domain.model.member.NotificationModel import com.droidblossom.archive.domain.usecase.member.GetNotificationsUseCase import com.droidblossom.archive.presentation.base.BaseViewModel @@ -44,12 +45,13 @@ class NotificationViewModelImpl @Inject constructor( get() = _lastCreatedTime private val scrollEventChannel = Channel(Channel.CONFLATED) - private val scrollEventFlow = scrollEventChannel.receiveAsFlow().throttleFirst(1000, TimeUnit.MILLISECONDS) + private val scrollEventFlow = + scrollEventChannel.receiveAsFlow().throttleFirst(1000, TimeUnit.MILLISECONDS) private var getNotificationListJob: Job? = null init { - viewModelScope.launch{ + viewModelScope.launch { scrollEventFlow.collect { getNotificationPage() } @@ -59,11 +61,17 @@ class NotificationViewModelImpl @Inject constructor( override fun onScrollNearBottom() { scrollEventChannel.trySend(Unit) } + override fun getNotificationPage() { - if (hasNextPage.value){ + if (hasNextPage.value) { getNotificationListJob?.cancel() getNotificationListJob = viewModelScope.launch { - getNotificationsUseCase(15, lastCreatedTime.value).collect { result -> + getNotificationsUseCase( + PagingRequestDto( + 15, + lastCreatedTime.value + ) + ).collect { result -> result.onSuccess { _hasNextPage.value = it.hasNext _notifications.emit(notifications.value + it.notifications) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt index ea77edf31..19e1ddcdb 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt @@ -1,19 +1,17 @@ package com.droidblossom.archive.presentation.ui.mypage -import android.util.Log import androidx.lifecycle.viewModelScope +import com.droidblossom.archive.data.dto.common.PagingRequestDto import com.droidblossom.archive.domain.model.member.MemberDetail -import com.droidblossom.archive.domain.model.secret.SecretCapsulePageRequest import com.droidblossom.archive.domain.usecase.member.MemberUseCase +import com.droidblossom.archive.domain.usecase.open.MyPublicCapsulePageUseCase import com.droidblossom.archive.domain.usecase.secret.SecretCapsulePageUseCase import com.droidblossom.archive.presentation.base.BaseViewModel -import com.droidblossom.archive.presentation.base.BaseViewModel.Companion.throttleFirst import com.droidblossom.archive.presentation.model.mypage.CapsuleData import com.droidblossom.archive.util.DateUtils import com.droidblossom.archive.util.onFail import com.droidblossom.archive.util.onSuccess import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableSharedFlow @@ -23,14 +21,14 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import java.util.concurrent.TimeUnit import javax.inject.Inject @HiltViewModel class MyPageViewModelImpl @Inject constructor( private val memberUseCase: MemberUseCase, - private val secretCapsulePageUseCase: SecretCapsulePageUseCase + private val secretCapsulePageUseCase: SecretCapsulePageUseCase, + private val myPublicCapsulePageUseCase: MyPublicCapsulePageUseCase ) : BaseViewModel(), MyPageViewModel { private val _myPageEvents = MutableSharedFlow() @@ -130,10 +128,10 @@ class MyPageViewModelImpl @Inject constructor( getCapsuleListJob?.cancel() getCapsuleListJob = viewModelScope.launch { secretCapsulePageUseCase( - SecretCapsulePageRequest( + PagingRequestDto( 15, lastCreatedTime.value - ).toDto() + ) ).collect { result -> result.onSuccess { _hasNextPage.value = it.hasNext @@ -151,9 +149,25 @@ class MyPageViewModelImpl @Inject constructor( } private fun getPublicCapsulePage() { - viewModelScope.launch { - if (hasNextPage.value) { - _myCapsules.emit(listOf()) + if (hasNextPage.value) { + getCapsuleListJob?.cancel() + getCapsuleListJob = viewModelScope.launch { + myPublicCapsulePageUseCase( + PagingRequestDto( + 15, + lastCreatedTime.value + ) + ).collect { result -> + result.onSuccess { + _hasNextPage.value = it.hasNext + _myCapsules.emit(myCapsules.value + it.capsules) + if (myCapsules.value.isNotEmpty()) { + _lastCreatedTime.value = myCapsules.value.last().createdDate + } + }.onFail { + myPageEvent(MyPageViewModel.MyPageEvent.ShowToastMessage("정보 불러오기 실패")) + } + } myPageEvent(MyPageViewModel.MyPageEvent.HideLoading) } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModelImpl.kt index cdba64078..52e69e99b 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendViewModelImpl.kt @@ -1,6 +1,7 @@ package com.droidblossom.archive.presentation.ui.mypage.friend import androidx.lifecycle.viewModelScope +import com.droidblossom.archive.data.dto.common.PagingRequestDto import com.droidblossom.archive.domain.model.friend.Friend import com.droidblossom.archive.domain.usecase.friend.FriendDeleteUseCase import com.droidblossom.archive.domain.usecase.friend.FriendsPageUseCase @@ -99,7 +100,12 @@ class FriendViewModelImpl @Inject constructor( if (friendHasNextPage.value) { getFriendLstJob?.cancel() getFriendLstJob = viewModelScope.launch { - friendsPageUseCase(15, friendLastCreatedTime.value).collect { result -> + friendsPageUseCase( + PagingRequestDto( + 15, + friendLastCreatedTime.value + ) + ).collect { result -> result.onSuccess { friendHasNextPage.value = it.hasNext if (friendListUI.value.isEmpty()) { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptViewModelImpl.kt index a23fabc30..a42701b80 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/FriendAcceptViewModelImpl.kt @@ -2,6 +2,7 @@ package com.droidblossom.archive.presentation.ui.mypage.friendaccept import android.util.Log import androidx.lifecycle.viewModelScope +import com.droidblossom.archive.data.dto.common.PagingRequestDto import com.droidblossom.archive.domain.model.friend.Friend import com.droidblossom.archive.domain.model.friend.FriendAcceptRequest import com.droidblossom.archive.domain.usecase.friend.FriendDenyRequestUseCase @@ -68,7 +69,12 @@ class FriendAcceptViewModelImpl @Inject constructor( getAcceptRequestListJob?.cancel() getAcceptRequestListJob = viewModelScope.launch { if (friendHasNextPage.value) { - friendsRequestsPageUseCase(15, friendLastCreatedTime.value).collect { result -> + friendsRequestsPageUseCase( + PagingRequestDto( + 15, + friendLastCreatedTime.value + ) + ).collect { result -> result.onSuccess { friendHasNextPage.value = it.hasNext _friendAcceptList.emit(friendAcceptList.value + it.friends) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinViewModelImpl.kt index b2239e4e1..787f27e3c 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinViewModelImpl.kt @@ -1,17 +1,14 @@ package com.droidblossom.archive.presentation.ui.skin -import android.util.Log import androidx.lifecycle.viewModelScope -import com.droidblossom.archive.data.dto.capsule_skin.request.CapsuleSkinsPageRequestDto +import com.droidblossom.archive.data.dto.common.PagingRequestDto import com.droidblossom.archive.domain.model.common.CapsuleSkinSummary import com.droidblossom.archive.domain.usecase.capsule_skin.CapsuleSkinsPageUseCase import com.droidblossom.archive.presentation.base.BaseViewModel -import com.droidblossom.archive.presentation.base.BaseViewModel.Companion.throttleFirst import com.droidblossom.archive.util.DateUtils import com.droidblossom.archive.util.onFail import com.droidblossom.archive.util.onSuccess import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableSharedFlow @@ -21,7 +18,6 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import java.util.concurrent.TimeUnit import javax.inject.Inject @@ -79,7 +75,7 @@ class SkinViewModelImpl @Inject constructor( getSkinLstJob?.cancel() getSkinLstJob = viewModelScope.launch { capsuleSkinsPageUseCase( - CapsuleSkinsPageRequestDto( + PagingRequestDto( 15, lastCreatedSkinTime.value ) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModelImpl.kt index 04a37a3b8..3122a2e25 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/social/page/friend/SocialFriendViewModelImpl.kt @@ -1,16 +1,14 @@ package com.droidblossom.archive.presentation.ui.social.page.friend import androidx.lifecycle.viewModelScope -import com.droidblossom.archive.data.dto.open.request.PublicCapsuleSliceRequestDto +import com.droidblossom.archive.data.dto.common.PagingRequestDto import com.droidblossom.archive.domain.model.common.SocialCapsules import com.droidblossom.archive.domain.usecase.open.PublicCapsulePageUseCase import com.droidblossom.archive.presentation.base.BaseViewModel -import com.droidblossom.archive.presentation.base.BaseViewModel.Companion.throttleFirst import com.droidblossom.archive.util.DateUtils import com.droidblossom.archive.util.onFail import com.droidblossom.archive.util.onSuccess import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableSharedFlow @@ -20,7 +18,6 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import java.util.concurrent.TimeUnit import javax.inject.Inject @@ -93,7 +90,7 @@ class SocialFriendViewModelImpl @Inject constructor( getSecretCapsuleListJob?.cancel() getSecretCapsuleListJob = viewModelScope.launch { publicCapsulePageUseCase( - PublicCapsuleSliceRequestDto( + PagingRequestDto( 15, lastCreatedTime.value ) From 815d28fdb0962de05b6346847b524cb0e16b2c9b Mon Sep 17 00:00:00 2001 From: comst19 Date: Mon, 6 May 2024 17:30:07 +0900 Subject: [PATCH 246/301] =?UTF-8?q?refact:=20/Secret,=20/Public=20?= =?UTF-8?q?=EA=B2=BD=EB=A1=9C=20API=20URL=20=EB=B3=80=EA=B2=BD=20=EB=B0=98?= =?UTF-8?q?=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/common/CapsuleDetailResponseDto.kt | 6 ++++-- .../dto/common/CapsuleSummaryResponseDto.kt | 6 ++++-- .../response/MyPublicCapsulePageResponseDto.kt | 2 +- .../data/repository/PublicRepositoryImpl.kt | 2 +- .../data/repository/SecretRepositoryImpl.kt | 4 ++-- .../data/source/remote/api/PublicService.kt | 10 +++++----- .../data/source/remote/api/SecretService.kt | 14 +++++++------- .../domain/repository/PublicRepository.kt | 2 +- .../domain/repository/SecretRepository.kt | 4 ++-- .../open/PublicCapsuleSummaryUseCase.kt | 2 +- .../secret/SecretCapsuleModifyUseCase.kt | 2 +- .../secret/SecretCapsuleSummaryUseCase.kt | 2 +- .../ui/capsule/CapsuleDetailViewModelImpl.kt | 18 +++++++++--------- .../dialog/CapsulePreviewDialogFragment.kt | 4 ++-- .../dialog/CapsulePreviewDialogViewModel.kt | 4 ++-- .../CapsulePreviewDialogViewModelImpl.kt | 4 ++-- .../ui/mypage/MyPageViewModelImpl.kt | 1 + 17 files changed, 46 insertions(+), 41 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/common/CapsuleDetailResponseDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/common/CapsuleDetailResponseDto.kt index 36d7c9559..a9ae77315 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/common/CapsuleDetailResponseDto.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/common/CapsuleDetailResponseDto.kt @@ -5,10 +5,12 @@ import com.droidblossom.archive.domain.model.common.SocialCapsules data class CapsuleDetailResponseDto( val address: String, - val roadName: String, + val roadName: String?, val capsuleSkinUrl: String, val content: String?, val createdDate: String, + val latitude: Double, + val longitude: Double, val profileUrl: String, val dueDate: String?, val isOpened: Boolean, @@ -20,7 +22,7 @@ data class CapsuleDetailResponseDto( ){ fun toModel() = CapsuleDetail( address = this.address, - roadName = this.roadName, + roadName = this.roadName ?: "", capsuleSkinUrl = this.capsuleSkinUrl, content = this.content ?: "", createdDate = this.createdDate, diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/common/CapsuleSummaryResponseDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/common/CapsuleSummaryResponseDto.kt index bc8ec65c4..2bc764e3d 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/common/CapsuleSummaryResponseDto.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/common/CapsuleSummaryResponseDto.kt @@ -8,8 +8,10 @@ data class CapsuleSummaryResponseDto( val skinUrl: String, val title: String, val dueDate: String?, + val latitude: Double, + val longitude: Double, val address: String, - val roadName: String, + val roadName: String?, val isOpened: Boolean, val createdAt: String ){ @@ -20,7 +22,7 @@ data class CapsuleSummaryResponseDto( title = this.title, dueDate = this.dueDate ?: "", address = this.address, - roadName = this.roadName, + roadName = this.roadName ?: "", isOpened = this.isOpened, createdAt = this.createdAt, ) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/open/response/MyPublicCapsulePageResponseDto.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/open/response/MyPublicCapsulePageResponseDto.kt index 4f6182d3d..3effed842 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/open/response/MyPublicCapsulePageResponseDto.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/dto/open/response/MyPublicCapsulePageResponseDto.kt @@ -4,7 +4,7 @@ import com.droidblossom.archive.data.dto.secret.response.SecretCapsuleResponseDt import com.droidblossom.archive.domain.model.secret.CapsulePageList data class MyPublicCapsulePageResponseDto ( - val publicCapsules: List, + val publicCapsules: List, val hasNext: Boolean, ){ fun toModel() = CapsulePageList( diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/PublicRepositoryImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/PublicRepositoryImpl.kt index b4e7a5aaa..d72e6c790 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/PublicRepositoryImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/PublicRepositoryImpl.kt @@ -25,7 +25,7 @@ class PublicRepositoryImpl @Inject constructor( return apiHandler({ api.postPublicCapsuleApi(request) }) { response: ResponseBody -> response.result.toModel() } } - override suspend fun getPublicCapsuleSummary(capsuleId: Int): RetrofitResult { + override suspend fun getPublicCapsuleSummary(capsuleId: Long): RetrofitResult { return apiHandler({ api.getPublicCapsuleSummaryApi(capsuleId) }) { response: ResponseBody -> response.result.toModel()} } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/SecretRepositoryImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/SecretRepositoryImpl.kt index acc06cb7d..db963c533 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/SecretRepositoryImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/SecretRepositoryImpl.kt @@ -35,12 +35,12 @@ class SecretRepositoryImpl @Inject constructor( return apiHandler({ api.getSecretCapsuleDetailApi(capsuleId) }) { response: ResponseBody -> response.result.toModel() } } - override suspend fun getSecretCapsuleSummary (capsuleId: Int) : RetrofitResult { + override suspend fun getSecretCapsuleSummary (capsuleId: Long) : RetrofitResult { return apiHandler({ api.getSecretCapsuleSummaryApi(capsuleId) }) { response: ResponseBody -> response.result.toModel()} } override suspend fun modifySecretCapsule( - capsuleId: Int, + capsuleId: Long, request: SecretCapsuleModifyRequestDto ): RetrofitResult { return apiHandler({ api.modifySecretCapsuleApi(capsuleId , request) }) { response: ResponseBody -> response.result.toModel() } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/PublicService.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/PublicService.kt index 2a267ee0c..9b08d0ac9 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/PublicService.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/PublicService.kt @@ -15,22 +15,22 @@ import retrofit2.http.Query interface PublicService { - @POST("capsules/public") + @POST("public-capsules") suspend fun postPublicCapsuleApi( @Body request : CapsuleCreateRequestDto ) : Response> - @GET("public/capsules/{capsule_id}/summary") + @GET("public-capsules/{capsule_id}/summary") suspend fun getPublicCapsuleSummaryApi( - @Path("capsule_id") capsuleId : Int, + @Path("capsule_id") capsuleId : Long, ) : Response> - @GET("public/capsules/{capsule_id}/detail") + @GET("public-capsules/{capsule_id}/detail") suspend fun getPublicCapsuleDetailApi( @Path("capsule_id") capsuleId : Long, ) : Response> - @GET("public/capsules") + @GET("public-capsules") suspend fun getPublicCapsulesPageApi( @Query("size") size : Int, @Query("created_at") createdAt: String diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/SecretService.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/SecretService.kt index 53705f910..d1469b508 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/SecretService.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/SecretService.kt @@ -17,31 +17,31 @@ import retrofit2.http.Query interface SecretService { - @GET("secret/capsules") + @GET("secret-capsules") suspend fun getSecretCapsulePageApi( @Query("size") size : Int, @Query("created_at") createdAt: String ) : Response> - @POST("capsules/secret") + @POST("secret-capsules") suspend fun postSecretCapsuleApi( @Body request : CapsuleCreateRequestDto ) : Response> - @GET("secret/capsules/{capsule_id}/detail") + @GET("secret-capsules/{capsule_id}/detail") suspend fun getSecretCapsuleDetailApi( @Path("capsule_id") capsuleId : Long, ) : Response> - @GET("secret/capsules/{capsule_id}/summary") + @GET("secret-capsules/{capsule_id}/summary") suspend fun getSecretCapsuleSummaryApi( - @Path("capsule_id") capsuleId : Int, + @Path("capsule_id") capsuleId : Long, ) : Response> //미정 (1/27 기준) - @PATCH("secret/capsules/{capsule_id}") + @PATCH("secret-capsules/{capsule_id}") suspend fun modifySecretCapsuleApi( - @Path("capsule_id") capsuleId : Int, + @Path("capsule_id") capsuleId : Long, @Body request : SecretCapsuleModifyRequestDto ) : Response> diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/PublicRepository.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/PublicRepository.kt index 1ad66d3c0..7767336d9 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/PublicRepository.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/PublicRepository.kt @@ -12,7 +12,7 @@ interface PublicRepository { suspend fun createPublicCapsule (request: CapsuleCreateRequestDto) : RetrofitResult - suspend fun getPublicCapsuleSummary (capsuleId: Int) : RetrofitResult + suspend fun getPublicCapsuleSummary (capsuleId: Long) : RetrofitResult suspend fun getPublicCapsuleDetail (capsuleId: Long) : RetrofitResult diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/SecretRepository.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/SecretRepository.kt index 74d8f98ca..f1246ad52 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/SecretRepository.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/SecretRepository.kt @@ -17,6 +17,6 @@ interface SecretRepository { suspend fun getSecretCapsuleDetail (capsuleId: Long) : RetrofitResult - suspend fun getSecretCapsuleSummary (capsuleId: Int) : RetrofitResult - suspend fun modifySecretCapsule (capsuleId: Int, request: SecretCapsuleModifyRequestDto) : RetrofitResult + suspend fun getSecretCapsuleSummary (capsuleId: Long) : RetrofitResult + suspend fun modifySecretCapsule (capsuleId: Long, request: SecretCapsuleModifyRequestDto) : RetrofitResult } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/open/PublicCapsuleSummaryUseCase.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/open/PublicCapsuleSummaryUseCase.kt index 40a0034fc..8663992ab 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/open/PublicCapsuleSummaryUseCase.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/open/PublicCapsuleSummaryUseCase.kt @@ -11,7 +11,7 @@ import javax.inject.Inject class PublicCapsuleSummaryUseCase @Inject constructor( private val repository: PublicRepository ) { - suspend operator fun invoke(capsuleId :Int) = + suspend operator fun invoke(capsuleId :Long) = flow { try { emit(repository.getPublicCapsuleSummary(capsuleId).onSuccess { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/secret/SecretCapsuleModifyUseCase.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/secret/SecretCapsuleModifyUseCase.kt index 05c4ca50d..e90ee3e25 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/secret/SecretCapsuleModifyUseCase.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/secret/SecretCapsuleModifyUseCase.kt @@ -14,7 +14,7 @@ import javax.inject.Inject data class SecretCapsuleModifyUseCase @Inject constructor( private val repository: SecretRepository ) { - suspend operator fun invoke(capsuleId :Int,request: SecretCapsuleModifyRequestDto) = + suspend operator fun invoke(capsuleId :Long,request: SecretCapsuleModifyRequestDto) = flow> { try { emit(repository.modifySecretCapsule(capsuleId, request).onSuccess { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/secret/SecretCapsuleSummaryUseCase.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/secret/SecretCapsuleSummaryUseCase.kt index 010291618..df094ed36 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/secret/SecretCapsuleSummaryUseCase.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/secret/SecretCapsuleSummaryUseCase.kt @@ -12,7 +12,7 @@ class SecretCapsuleSummaryUseCase @Inject constructor( private val repository : SecretRepository ) { - suspend operator fun invoke(capsuleId :Int) = + suspend operator fun invoke(capsuleId :Long) = flow { try { emit(repository.getSecretCapsuleSummary(capsuleId).onSuccess { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/capsule/CapsuleDetailViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/capsule/CapsuleDetailViewModelImpl.kt index 039fce63e..1778a8a90 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/capsule/CapsuleDetailViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/capsule/CapsuleDetailViewModelImpl.kt @@ -21,11 +21,11 @@ import javax.inject.Inject class CapsuleDetailViewModelImpl @Inject constructor( private val secretCapsuleDetailUseCase: SecretCapsuleDetailUseCase, private val publicCapsuleDetailUseCase: PublicCapsuleDetailUseCase -): BaseViewModel(), CapsuleDetailViewModel { +) : BaseViewModel(), CapsuleDetailViewModel { private val _detailEvent = MutableSharedFlow() override val detailEvents: SharedFlow - get() = _detailEvent.asSharedFlow() + get() = _detailEvent.asSharedFlow() private val _capsuleDetail = MutableStateFlow(CapsuleDetail()) override val capsuleDetail: StateFlow @@ -33,12 +33,12 @@ class CapsuleDetailViewModelImpl @Inject constructor( override fun getSecretCapsuleDetail(id: Long) { viewModelScope.launch { - secretCapsuleDetailUseCase(id).collect{ result -> + secretCapsuleDetailUseCase(id).collect { result -> result.onSuccess { detail -> - Log.d("디테일","${detail}") + Log.d("디테일", "${detail}") _capsuleDetail.emit(detail) - }.onFail { - Log.d("디테일","${it}") + }.onFail { + Log.d("디테일", "${it}") _detailEvent.emit(CapsuleDetailViewModel.DetailEvent.ShowToastMessage("상세정보 불러오기 실패")) } } @@ -47,12 +47,12 @@ class CapsuleDetailViewModelImpl @Inject constructor( override fun getPublicCapsuleDetail(id: Long) { viewModelScope.launch { - publicCapsuleDetailUseCase(id).collect{ result -> + publicCapsuleDetailUseCase(id).collect { result -> result.onSuccess { detail -> - Log.d("디테일","${detail}") + Log.d("디테일", "${detail}") _capsuleDetail.emit(detail) }.onFail { - Log.d("디테일","${it}") + Log.d("디테일", "${it}") _detailEvent.emit(CapsuleDetailViewModel.DetailEvent.ShowToastMessage("상세정보 불러오기 실패")) } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogFragment.kt index 485a1e498..83968f0c1 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogFragment.kt @@ -36,8 +36,8 @@ class CapsulePreviewDialogFragment : } - val capsuleId: Int by lazy { - arguments?.getString("capsule_id")!!.toInt() + val capsuleId: Long by lazy { + arguments?.getString("capsule_id")!!.toLong() } val capsuleType by lazy { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogViewModel.kt index 48bb135dd..58dbc0f5e 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogViewModel.kt @@ -33,8 +33,8 @@ interface CapsulePreviewDialogViewModel { fun setCalledFromCamera(calledFromCamera : Boolean) - fun getSecretCapsuleSummary(capsuleId: Int) - fun getPublicCapsuleSummary(capsuleId: Int) + fun getSecretCapsuleSummary(capsuleId: Long) + fun getPublicCapsuleSummary(capsuleId: Long) sealed class CapsulePreviewDialogEvent{ data class ShowToastMessage(val message : String) : CapsulePreviewDialogEvent() diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogViewModelImpl.kt index 0e9d4f793..a60bf06c0 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogViewModelImpl.kt @@ -86,7 +86,7 @@ class CapsulePreviewDialogViewModelImpl @Inject constructor( override fun setCalledFromCamera(calledFromCamera : Boolean){ _calledFromCamera.value = calledFromCamera } - override fun getSecretCapsuleSummary(capsuleId: Int) { + override fun getSecretCapsuleSummary(capsuleId: Long) { viewModelScope.launch { secretCapsuleSummaryUseCase(capsuleId).collect { result -> result.onSuccess { @@ -102,7 +102,7 @@ class CapsulePreviewDialogViewModelImpl @Inject constructor( } } - override fun getPublicCapsuleSummary(capsuleId: Int) { + override fun getPublicCapsuleSummary(capsuleId: Long) { viewModelScope.launch { publicCapsuleSummaryUseCase(capsuleId).collect { result -> result.onSuccess { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt index 19e1ddcdb..d2d5cbfc3 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt @@ -1,5 +1,6 @@ package com.droidblossom.archive.presentation.ui.mypage +import android.util.Log import androidx.lifecycle.viewModelScope import com.droidblossom.archive.data.dto.common.PagingRequestDto import com.droidblossom.archive.domain.model.member.MemberDetail From 977b2c97a079a42b76b1303a2313d81b1740c836 Mon Sep 17 00:00:00 2001 From: comst19 Date: Mon, 6 May 2024 17:40:01 +0900 Subject: [PATCH 247/301] =?UTF-8?q?refact:=20/Capsules=20=EA=B2=BD?= =?UTF-8?q?=EB=A1=9C=20API=20URL=20=EB=B3=80=EA=B2=BD=EC=82=AC=ED=95=AD=20?= =?UTF-8?q?=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/data/source/remote/api/CapsuleService.kt | 4 ++-- .../archive/presentation/ui/camera/CameraViewModelImpl.kt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/CapsuleService.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/CapsuleService.kt index bfcd7ad2f..907322e9e 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/CapsuleService.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/CapsuleService.kt @@ -18,7 +18,7 @@ interface CapsuleService { @Path("capsule_id") capsuleId : Long, ) : Response> - @GET("capsules/nearby") + @GET("capsules/my/map/nearby") suspend fun getNearbyCapsulesHomeApi( @Query("latitude") latitude : Double, @Query("longitude") longitude : Double, @@ -26,7 +26,7 @@ interface CapsuleService { @Query("capsule_type") capsuleType : String ) : Response> - @GET("capsules/nearby/ar") + @GET("capsules/my/ar/nearby") suspend fun getNearbyCapsulesARApi( @Query("latitude") latitude : Double, @Query("longitude") longitude : Double, diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModelImpl.kt index a14548e96..6e526e25f 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModelImpl.kt @@ -55,7 +55,7 @@ class CameraViewModelImpl@Inject constructor( override fun getCapsules(latitude: Double, longitude: Double) : List { viewModelScope.launch { - nearbyCapsulesARUseCase(latitude,longitude,0.5,"ALL").collect{ result-> + nearbyCapsulesARUseCase(latitude,longitude,0.1,"ALL").collect{ result-> result.onSuccess { _capsuleList.emit(it.capsuleAnchors) _capsuleListSize.value = _capsuleList.value.size From e9378d230860d2f7ac0eb4e20bceb8bda1beab53 Mon Sep 17 00:00:00 2001 From: comst19 Date: Mon, 6 May 2024 21:42:32 +0900 Subject: [PATCH 248/301] =?UTF-8?q?refact:=20=EA=B7=BC=EC=B2=98=EC=9D=98?= =?UTF-8?q?=20=EC=BA=A1=EC=8A=90=20=EC=A1=B0=ED=9A=8C(=EC=B9=9C=EA=B5=AC,?= =?UTF-8?q?=20=EB=82=98)=20API=20=EC=B6=94=EA=B0=80=EB=A1=9C=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/data/repository/CapsuleRepositoryImpl.kt | 8 ++++---- .../archive/data/source/remote/api/CapsuleService.kt | 4 ++-- .../archive/domain/repository/CapsuleRepository.kt | 4 ++-- ...apsulesHomeUseCase.kt => NearbyMyCapsulesARUseCase.kt} | 4 ++-- ...apsulesARUseCase.kt => NearbyMyCapsulesHomeUseCase.kt} | 4 ++-- .../archive/presentation/ui/camera/CameraViewModelImpl.kt | 6 +++--- .../archive/presentation/ui/home/HomeViewModelImpl.kt | 6 +++--- 7 files changed, 18 insertions(+), 18 deletions(-) rename frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule/{NearbyCapsulesHomeUseCase.kt => NearbyMyCapsulesARUseCase.kt} (89%) rename frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule/{NearbyCapsulesARUseCase.kt => NearbyMyCapsulesHomeUseCase.kt} (85%) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/CapsuleRepositoryImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/CapsuleRepositoryImpl.kt index 18f409265..a731d0b4b 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/CapsuleRepositoryImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/CapsuleRepositoryImpl.kt @@ -23,22 +23,22 @@ class CapsuleRepositoryImpl @Inject constructor( return apiHandler({ api.patchCapsuleOpenApi(capsuleId = capsuleId) }) { response: ResponseBody -> response.result.toModel() } } - override suspend fun nearbyCapsulesHome( + override suspend fun nearbyMyCapsulesHome( latitude: Double, longitude: Double, distance: Double, capsuleType: String ): RetrofitResult { - return apiHandler({ api.getNearbyCapsulesHomeApi(latitude = latitude, longitude = longitude, distance= distance, capsuleType= capsuleType) }) { response : ResponseBody -> response.result.toMarkerModel() } + return apiHandler({ api.getNearbyMyCapsulesHomeApi(latitude = latitude, longitude = longitude, distance= distance, capsuleType= capsuleType) }) { response : ResponseBody -> response.result.toMarkerModel() } } - override suspend fun nearbyCapsulesAR( + override suspend fun nearbyMyCapsulesAR( latitude: Double, longitude: Double, distance: Double, capsuleType: String ): RetrofitResult { - return apiHandler({ api.getNearbyCapsulesARApi(latitude = latitude, longitude = longitude, distance= distance, capsuleType= capsuleType) }) { response : ResponseBody -> response.result.toAnchorModel() } + return apiHandler({ api.getNearbyMyCapsulesARApi(latitude = latitude, longitude = longitude, distance= distance, capsuleType= capsuleType) }) { response : ResponseBody -> response.result.toAnchorModel() } } override suspend fun getAddress( diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/CapsuleService.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/CapsuleService.kt index 907322e9e..e48a46a6b 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/CapsuleService.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/CapsuleService.kt @@ -19,7 +19,7 @@ interface CapsuleService { ) : Response> @GET("capsules/my/map/nearby") - suspend fun getNearbyCapsulesHomeApi( + suspend fun getNearbyMyCapsulesHomeApi( @Query("latitude") latitude : Double, @Query("longitude") longitude : Double, @Query("distance") distance : Double, @@ -27,7 +27,7 @@ interface CapsuleService { ) : Response> @GET("capsules/my/ar/nearby") - suspend fun getNearbyCapsulesARApi( + suspend fun getNearbyMyCapsulesARApi( @Query("latitude") latitude : Double, @Query("longitude") longitude : Double, @Query("distance") distance : Double, diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/CapsuleRepository.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/CapsuleRepository.kt index 71289e227..b4003174c 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/CapsuleRepository.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/CapsuleRepository.kt @@ -13,13 +13,13 @@ interface CapsuleRepository { capsuleId : Long ): RetrofitResult - suspend fun nearbyCapsulesHome( + suspend fun nearbyMyCapsulesHome( latitude: Double, longitude: Double, distance: Double, capsuleType: String ): RetrofitResult - suspend fun nearbyCapsulesAR( + suspend fun nearbyMyCapsulesAR( latitude: Double, longitude: Double, distance: Double, diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule/NearbyCapsulesHomeUseCase.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule/NearbyMyCapsulesARUseCase.kt similarity index 89% rename from frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule/NearbyCapsulesHomeUseCase.kt rename to frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule/NearbyMyCapsulesARUseCase.kt index c87216c68..770acac9b 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule/NearbyCapsulesHomeUseCase.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule/NearbyMyCapsulesARUseCase.kt @@ -8,14 +8,14 @@ import com.droidblossom.archive.util.onSuccess import kotlinx.coroutines.flow.flow import javax.inject.Inject -class NearbyCapsulesHomeUseCase @Inject constructor( +class NearbyMyCapsulesARUseCase @Inject constructor( private val repository: CapsuleRepository ) { suspend operator fun invoke(latitude: Double, longitude: Double, distance: Double, capsuleType: String) = flow { try { - emit(repository.nearbyCapsulesHome(latitude, longitude, distance, capsuleType).onSuccess { + emit(repository.nearbyMyCapsulesAR(latitude, longitude, distance, capsuleType).onSuccess { Log.d("성패", "$it") }.onFail { Log.d("실패", "$it") diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule/NearbyCapsulesARUseCase.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule/NearbyMyCapsulesHomeUseCase.kt similarity index 85% rename from frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule/NearbyCapsulesARUseCase.kt rename to frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule/NearbyMyCapsulesHomeUseCase.kt index 2f6ddce15..e54737672 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule/NearbyCapsulesARUseCase.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule/NearbyMyCapsulesHomeUseCase.kt @@ -8,14 +8,14 @@ import com.droidblossom.archive.util.onSuccess import kotlinx.coroutines.flow.flow import javax.inject.Inject -class NearbyCapsulesARUseCase @Inject constructor( +class NearbyMyCapsulesHomeUseCase @Inject constructor( private val repository: CapsuleRepository ) { suspend operator fun invoke(latitude: Double, longitude: Double, distance: Double, capsuleType: String) = flow { try { - emit(repository.nearbyCapsulesAR(latitude, longitude, distance, capsuleType).onSuccess { + emit(repository.nearbyMyCapsulesHome(latitude, longitude, distance, capsuleType).onSuccess { Log.d("성패", "$it") }.onFail { Log.d("실패", "$it") diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModelImpl.kt index 6e526e25f..707ba4a5c 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModelImpl.kt @@ -2,7 +2,7 @@ package com.droidblossom.archive.presentation.ui.camera import androidx.lifecycle.viewModelScope import com.droidblossom.archive.domain.model.capsule.CapsuleAnchor -import com.droidblossom.archive.domain.usecase.capsule.NearbyCapsulesARUseCase +import com.droidblossom.archive.domain.usecase.capsule.NearbyMyCapsulesARUseCase import com.droidblossom.archive.presentation.base.BaseViewModel import com.droidblossom.archive.util.onFail import com.droidblossom.archive.util.onSuccess @@ -19,7 +19,7 @@ import javax.inject.Inject @HiltViewModel class CameraViewModelImpl@Inject constructor( - private val nearbyCapsulesARUseCase: NearbyCapsulesARUseCase + private val nearbyMyCapsulesARUseCase: NearbyMyCapsulesARUseCase ) : BaseViewModel(), CameraViewModel { private val _cameraEvents = MutableSharedFlow() @@ -55,7 +55,7 @@ class CameraViewModelImpl@Inject constructor( override fun getCapsules(latitude: Double, longitude: Double) : List { viewModelScope.launch { - nearbyCapsulesARUseCase(latitude,longitude,0.1,"ALL").collect{ result-> + nearbyMyCapsulesARUseCase(latitude,longitude,0.1,"ALL").collect{ result-> result.onSuccess { _capsuleList.emit(it.capsuleAnchors) _capsuleListSize.value = _capsuleList.value.size diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeViewModelImpl.kt index 65eca73e5..91ecc33ed 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeViewModelImpl.kt @@ -3,7 +3,7 @@ package com.droidblossom.archive.presentation.ui.home import android.util.Log import androidx.lifecycle.viewModelScope import com.droidblossom.archive.domain.model.capsule.CapsuleMarker -import com.droidblossom.archive.domain.usecase.capsule.NearbyCapsulesHomeUseCase +import com.droidblossom.archive.domain.usecase.capsule.NearbyMyCapsulesHomeUseCase import com.droidblossom.archive.presentation.base.BaseViewModel import com.droidblossom.archive.util.onFail import com.droidblossom.archive.util.onSuccess @@ -18,7 +18,7 @@ import javax.inject.Inject @HiltViewModel class HomeViewModelImpl @Inject constructor( - private val nearbyCapsulesHomeUseCase: NearbyCapsulesHomeUseCase + private val nearbyMyCapsulesHomeUseCase: NearbyMyCapsulesHomeUseCase ) : BaseViewModel(), HomeViewModel { private val _homeEvents = MutableSharedFlow() @@ -109,7 +109,7 @@ class HomeViewModelImpl @Inject constructor( override fun getNearbyCapsules(latitude: Double, longitude: Double, distance: Double, capsuleType: String) { Log.d("티티","$latitude, $longitude, $distance, $capsuleType") viewModelScope.launch { - nearbyCapsulesHomeUseCase(latitude,longitude, distance, capsuleType ).collect{ result-> + nearbyMyCapsulesHomeUseCase(latitude,longitude, distance, capsuleType ).collect{ result-> result.onSuccess { _capsuleList.emit(it.capsuleMarkers) }.onFail { From 9b4912d4b6fb45cc3326c99d4582756788fa5bef Mon Sep 17 00:00:00 2001 From: comst19 Date: Mon, 6 May 2024 22:08:25 +0900 Subject: [PATCH 249/301] =?UTF-8?q?feat:=20=EA=B7=BC=EC=B2=98=EC=97=90=20?= =?UTF-8?q?=EC=B9=9C=EA=B5=AC=EA=B0=80=20=EB=A7=8C=EB=93=A0=20=EA=B3=B5?= =?UTF-8?q?=EA=B0=9C=20=EC=BA=A1=EC=8A=90=EC=9D=84=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=ED=95=9C=EB=8B=A4=20-=20AR?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/repository/CapsuleRepositoryImpl.kt | 8 +++++ .../data/source/remote/api/CapsuleService.kt | 7 ++++ .../domain/repository/CapsuleRepository.kt | 6 ++++ .../capsule/NearbyFriendsCapsulesARUseCase.kt | 32 +++++++++++++++++++ .../presentation/ui/camera/CameraFragment.kt | 4 +++ .../presentation/ui/camera/CameraViewModel.kt | 4 ++- .../ui/camera/CameraViewModelImpl.kt | 32 ++++++++++++++++--- 7 files changed, 88 insertions(+), 5 deletions(-) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule/NearbyFriendsCapsulesARUseCase.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/CapsuleRepositoryImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/CapsuleRepositoryImpl.kt index a731d0b4b..667c6573d 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/CapsuleRepositoryImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/CapsuleRepositoryImpl.kt @@ -41,6 +41,14 @@ class CapsuleRepositoryImpl @Inject constructor( return apiHandler({ api.getNearbyMyCapsulesARApi(latitude = latitude, longitude = longitude, distance= distance, capsuleType= capsuleType) }) { response : ResponseBody -> response.result.toAnchorModel() } } + override suspend fun nearbyFriendsCapsulesAR( + latitude: Double, + longitude: Double, + distance: Double, + ): RetrofitResult { + return apiHandler({ api.getNearbyFriendsCapsulesARApi(latitude = latitude, longitude = longitude, distance= distance) }) { response : ResponseBody -> response.result.toAnchorModel() } + } + override suspend fun getAddress( latitude: Double, longitude: Double diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/CapsuleService.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/CapsuleService.kt index e48a46a6b..3f0bb34b6 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/CapsuleService.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/CapsuleService.kt @@ -34,6 +34,13 @@ interface CapsuleService { @Query("capsule_type") capsuleType : String ) : Response> + @GET("capsules/friends/ar/nearby") + suspend fun getNearbyFriendsCapsulesARApi( + @Query("latitude") latitude : Double, + @Query("longitude") longitude : Double, + @Query("distance") distance : Double, + ) : Response> + @GET("map/full-address") suspend fun getAddressApi( @Query("latitude") latitude : Double, diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/CapsuleRepository.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/CapsuleRepository.kt index b4003174c..569c226a0 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/CapsuleRepository.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/CapsuleRepository.kt @@ -26,6 +26,12 @@ interface CapsuleRepository { capsuleType: String ): RetrofitResult + suspend fun nearbyFriendsCapsulesAR( + latitude: Double, + longitude: Double, + distance: Double, + ): RetrofitResult + suspend fun getAddress( latitude: Double, longitude: Double, diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule/NearbyFriendsCapsulesARUseCase.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule/NearbyFriendsCapsulesARUseCase.kt new file mode 100644 index 000000000..8cb995d12 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule/NearbyFriendsCapsulesARUseCase.kt @@ -0,0 +1,32 @@ +package com.droidblossom.archive.domain.usecase.capsule + +import android.util.Log +import com.droidblossom.archive.domain.repository.CapsuleRepository +import com.droidblossom.archive.util.onException +import com.droidblossom.archive.util.onFail +import com.droidblossom.archive.util.onSuccess +import kotlinx.coroutines.flow.flow +import javax.inject.Inject + +class NearbyFriendsCapsulesARUseCase @Inject constructor( + private val repository: CapsuleRepository +) { + + suspend operator fun invoke(latitude: Double, longitude: Double, distance: Double) = + flow { + try { + emit(repository.nearbyFriendsCapsulesAR(latitude, longitude, distance).onSuccess { + Log.d("성패", "$it") + }.onFail { + Log.d("실패", "$it") + }.onException { + Log.d("실패", "$it") + throw Exception(it) + }) + } catch (e: Exception) { + Log.d("예외확인", "$e") + e.printStackTrace() + } + } + +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt index 5cc4684cc..f5ebae733 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt @@ -58,6 +58,10 @@ class CameraFragment : sheet.show(parentFragmentManager, "CapsulePreviewDialog") } + is CameraViewModel.CameraEvent.DismissLoading -> { + dismissLoading() + } + else -> {} } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModel.kt index 005a474e4..4ca512012 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModel.kt @@ -12,7 +12,7 @@ interface CameraViewModel { val capsuleListSize: SharedFlow val anchorNodes: StateFlow> - fun getCapsules(latitude: Double, longitude: Double,): List + fun getCapsules(latitude: Double, longitude: Double) fun cameraEvent(event : CameraEvent) @@ -22,5 +22,7 @@ interface CameraViewModel { sealed class CameraEvent{ data class ShowCapsulePreviewDialog(val capsuleId: String, val capsuleType: String) : CameraEvent() + object DismissLoading : CameraEvent() + } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModelImpl.kt index 707ba4a5c..32ecd9650 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModelImpl.kt @@ -2,6 +2,7 @@ package com.droidblossom.archive.presentation.ui.camera import androidx.lifecycle.viewModelScope import com.droidblossom.archive.domain.model.capsule.CapsuleAnchor +import com.droidblossom.archive.domain.usecase.capsule.NearbyFriendsCapsulesARUseCase import com.droidblossom.archive.domain.usecase.capsule.NearbyMyCapsulesARUseCase import com.droidblossom.archive.presentation.base.BaseViewModel import com.droidblossom.archive.util.onFail @@ -19,7 +20,8 @@ import javax.inject.Inject @HiltViewModel class CameraViewModelImpl@Inject constructor( - private val nearbyMyCapsulesARUseCase: NearbyMyCapsulesARUseCase + private val nearbyMyCapsulesARUseCase: NearbyMyCapsulesARUseCase, + private val nearbyFriendsCapsulesARUseCase: NearbyFriendsCapsulesARUseCase ) : BaseViewModel(), CameraViewModel { private val _cameraEvents = MutableSharedFlow() @@ -53,17 +55,39 @@ class CameraViewModelImpl@Inject constructor( } } - override fun getCapsules(latitude: Double, longitude: Double) : List { + override fun getCapsules(latitude: Double, longitude: Double){ + if (true) getMyCapsules(latitude, longitude) + else getFriendsCapsules(latitude, longitude) + } + + private fun getMyCapsules(latitude: Double, longitude: Double){ viewModelScope.launch { nearbyMyCapsulesARUseCase(latitude,longitude,0.1,"ALL").collect{ result-> result.onSuccess { _capsuleList.emit(it.capsuleAnchors) _capsuleListSize.value = _capsuleList.value.size + if (capsuleList.value.isEmpty()){ + cameraEvent(CameraViewModel.CameraEvent.DismissLoading) + } }.onFail { - + cameraEvent(CameraViewModel.CameraEvent.DismissLoading) + } + } + } + } + private fun getFriendsCapsules(latitude: Double, longitude: Double){ + viewModelScope.launch { + nearbyFriendsCapsulesARUseCase(latitude,longitude,0.1).collect{ result-> + result.onSuccess { + _capsuleList.emit(it.capsuleAnchors) + _capsuleListSize.value = _capsuleList.value.size + if (capsuleList.value.isEmpty()) { + cameraEvent(CameraViewModel.CameraEvent.DismissLoading) + } + }.onFail { + cameraEvent(CameraViewModel.CameraEvent.DismissLoading) } } } - return capsuleList.value } } \ No newline at end of file From bbfed99dfb3fdd6a4d8a982ebd87657bc7ba8f9b Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Tue, 7 May 2024 14:27:33 +0900 Subject: [PATCH 250/301] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC=20=EC=BA=A1?= =?UTF-8?q?=EC=8A=90=20=EC=A7=80=EB=8F=84=EC=97=90=EC=84=9C=20=EB=B3=B4?= =?UTF-8?q?=EA=B8=B0=20api=20ussecase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/repository/CapsuleRepositoryImpl.kt | 9 ++++++ .../data/source/remote/api/CapsuleService.kt | 7 ++++ .../domain/repository/CapsuleRepository.kt | 7 ++++ .../NearbyFriendsCapsulesHomeUseCase.kt | 32 +++++++++++++++++++ 4 files changed, 55 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule/NearbyFriendsCapsulesHomeUseCase.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/CapsuleRepositoryImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/CapsuleRepositoryImpl.kt index 667c6573d..ddb5f753f 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/CapsuleRepositoryImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/repository/CapsuleRepositoryImpl.kt @@ -32,6 +32,15 @@ class CapsuleRepositoryImpl @Inject constructor( return apiHandler({ api.getNearbyMyCapsulesHomeApi(latitude = latitude, longitude = longitude, distance= distance, capsuleType= capsuleType) }) { response : ResponseBody -> response.result.toMarkerModel() } } + override suspend fun nearbyFriendsCapsulesHome( + latitude: Double, + longitude: Double, + distance: Double + ): RetrofitResult { + return apiHandler({ api.getNearbyFriendsCapsulesHomeApi(latitude = latitude, longitude = longitude, distance= distance ) }) { response : ResponseBody -> response.result.toMarkerModel() } + + } + override suspend fun nearbyMyCapsulesAR( latitude: Double, longitude: Double, diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/CapsuleService.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/CapsuleService.kt index 3f0bb34b6..c5d882fd7 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/CapsuleService.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/data/source/remote/api/CapsuleService.kt @@ -26,6 +26,13 @@ interface CapsuleService { @Query("capsule_type") capsuleType : String ) : Response> + @GET("capsules/friends/map/nearby") + suspend fun getNearbyFriendsCapsulesHomeApi( + @Query("latitude") latitude : Double, + @Query("longitude") longitude : Double, + @Query("distance") distance : Double, + ) : Response> + @GET("capsules/my/ar/nearby") suspend fun getNearbyMyCapsulesARApi( @Query("latitude") latitude : Double, diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/CapsuleRepository.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/CapsuleRepository.kt index 569c226a0..48e97bc0d 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/CapsuleRepository.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/repository/CapsuleRepository.kt @@ -19,6 +19,13 @@ interface CapsuleRepository { distance: Double, capsuleType: String ): RetrofitResult + + suspend fun nearbyFriendsCapsulesHome( + latitude: Double, + longitude: Double, + distance: Double, + ): RetrofitResult + suspend fun nearbyMyCapsulesAR( latitude: Double, longitude: Double, diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule/NearbyFriendsCapsulesHomeUseCase.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule/NearbyFriendsCapsulesHomeUseCase.kt new file mode 100644 index 000000000..3b7870152 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/domain/usecase/capsule/NearbyFriendsCapsulesHomeUseCase.kt @@ -0,0 +1,32 @@ +package com.droidblossom.archive.domain.usecase.capsule + +import android.util.Log +import com.droidblossom.archive.domain.repository.CapsuleRepository +import com.droidblossom.archive.util.onException +import com.droidblossom.archive.util.onFail +import com.droidblossom.archive.util.onSuccess +import kotlinx.coroutines.flow.flow +import javax.inject.Inject + +class NearbyFriendsCapsulesHomeUseCase @Inject constructor( + private val repository: CapsuleRepository +) { + + suspend operator fun invoke(latitude: Double, longitude: Double, distance: Double) = + flow { + try { + emit(repository.nearbyFriendsCapsulesHome(latitude, longitude, distance).onSuccess { + Log.d("성패", "$it") + }.onFail { + Log.d("실패", "$it") + }.onException { + Log.d("실패", "$it") + throw Exception(it) + }) + } catch (e: Exception) { + Log.d("예외확인", "$e") + e.printStackTrace() + } + } + +} \ No newline at end of file From 8570ce5d5bca869c81ee86a0012849d205b8f0a2 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Tue, 7 May 2024 14:52:02 +0900 Subject: [PATCH 251/301] =?UTF-8?q?feat:=20=EC=A7=80=EB=8F=84=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=B9=9C=EA=B5=AC=EA=B0=80=20=EB=A7=8C=EB=93=A0=20?= =?UTF-8?q?=EC=BA=A1=EC=8A=90=20=EB=B3=B4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ui/home/HomeFragment.kt | 22 ++++++- .../presentation/ui/home/HomeViewModel.kt | 5 +- .../presentation/ui/home/HomeViewModelImpl.kt | 59 +++++++++++++++---- .../app/src/main/res/layout/fragment_home.xml | 25 ++++++-- 4 files changed, 93 insertions(+), 18 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt index f8339d4b5..5ec6464ab 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt @@ -186,6 +186,24 @@ class HomeFragment : BaseFragment(R.layo } } } + viewLifecycleOwner.lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.isFriendsCapsuleDisplay.collect { state -> + if (state && ( viewModel.filterCapsuleSelect.value == HomeViewModel.CapsuleFilter.ALL + || viewModel.filterCapsuleSelect.value == HomeViewModel.CapsuleFilter.PUBLIC )){ + clusterer.map?.let { map -> + val cameraTarget = map.cameraPosition.target + viewModel.getNearbyFriendsCapsules( + cameraTarget.latitude, + cameraTarget.longitude, + getRadiusForCurrentZoom(), + ) + } + } + } + } + } + viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.capsuleList.collect { @@ -247,7 +265,7 @@ class HomeFragment : BaseFragment(R.layo private fun fetchCapsulesNearUser() { locationUtil.getCurrentLocation { latitude, longitude -> val radius = if (clusterer.map != null) getRadiusForCurrentZoom() else DEFATULTRADIUS - viewModel.getNearbyCapsules( + viewModel.getNearbyMyCapsules( latitude, longitude, radius, @@ -259,7 +277,7 @@ class HomeFragment : BaseFragment(R.layo private fun fetchCapsulesInCameraFocus() { clusterer.map?.let { map -> val cameraTarget = map.cameraPosition.target - viewModel.getNearbyCapsules( + viewModel.getNearbyMyCapsules( cameraTarget.latitude, cameraTarget.longitude, getRadiusForCurrentZoom(), diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeViewModel.kt index 307a0a4b4..365bb248a 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeViewModel.kt @@ -10,6 +10,7 @@ interface HomeViewModel { val isClickedFAB : StateFlow val existsNotification : StateFlow val followLocation : StateFlow + val isFriendsCapsuleDisplay : StateFlow val capsuleList: StateFlow> val homeEvents: SharedFlow @@ -21,9 +22,11 @@ interface HomeViewModel { fun clickFollowBtn() fun clickNotificationBtn() fun clickFAB() + fun clickFriendsDisplay() fun resetNearbyCapsules() - fun getNearbyCapsules(latitude: Double, longitude: Double, distance: Double, capsuleType: String) + fun getNearbyMyCapsules(latitude: Double, longitude: Double, distance: Double, capsuleType: String) + fun getNearbyFriendsCapsules(latitude: Double, longitude: Double, distance: Double) fun homeEvent(event:HomeEvent) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeViewModelImpl.kt index 91ecc33ed..1b9cecd4d 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeViewModelImpl.kt @@ -3,6 +3,7 @@ package com.droidblossom.archive.presentation.ui.home import android.util.Log import androidx.lifecycle.viewModelScope import com.droidblossom.archive.domain.model.capsule.CapsuleMarker +import com.droidblossom.archive.domain.usecase.capsule.NearbyFriendsCapsulesHomeUseCase import com.droidblossom.archive.domain.usecase.capsule.NearbyMyCapsulesHomeUseCase import com.droidblossom.archive.presentation.base.BaseViewModel import com.droidblossom.archive.util.onFail @@ -18,7 +19,8 @@ import javax.inject.Inject @HiltViewModel class HomeViewModelImpl @Inject constructor( - private val nearbyMyCapsulesHomeUseCase: NearbyMyCapsulesHomeUseCase + private val nearbyMyCapsulesHomeUseCase: NearbyMyCapsulesHomeUseCase, + private val nearbyFriendsCapsulesHomeUseCase: NearbyFriendsCapsulesHomeUseCase, ) : BaseViewModel(), HomeViewModel { private val _homeEvents = MutableSharedFlow() @@ -39,7 +41,11 @@ class HomeViewModelImpl @Inject constructor( private val _followLocation: MutableStateFlow = MutableStateFlow(false) override val followLocation: StateFlow get() = _followLocation - private val _capsuleList= MutableStateFlow>(listOf()) + + private val _isFriendsCapsuleDisplay = MutableStateFlow(false) + override val isFriendsCapsuleDisplay: StateFlow + get() = _isFriendsCapsuleDisplay + private val _capsuleList = MutableStateFlow>(listOf()) override val capsuleList: StateFlow> get() = _capsuleList @@ -64,7 +70,8 @@ class HomeViewModelImpl @Inject constructor( if (filterCapsuleSelect.value == HomeViewModel.CapsuleFilter.GROUP) _filterCapsuleSelect.emit(HomeViewModel.CapsuleFilter.ALL) else - _filterCapsuleSelect.emit(HomeViewModel.CapsuleFilter.GROUP) } + _filterCapsuleSelect.emit(HomeViewModel.CapsuleFilter.GROUP) + } } override fun selectSecret() { @@ -73,7 +80,8 @@ class HomeViewModelImpl @Inject constructor( if (filterCapsuleSelect.value == HomeViewModel.CapsuleFilter.SECRET) _filterCapsuleSelect.emit(HomeViewModel.CapsuleFilter.ALL) else - _filterCapsuleSelect.emit(HomeViewModel.CapsuleFilter.SECRET) } + _filterCapsuleSelect.emit(HomeViewModel.CapsuleFilter.SECRET) + } } override fun selectHotPlace() { @@ -81,7 +89,8 @@ class HomeViewModelImpl @Inject constructor( if (filterCapsuleSelect.value == HomeViewModel.CapsuleFilter.HOT) _filterCapsuleSelect.emit(HomeViewModel.CapsuleFilter.ALL) else - _filterCapsuleSelect.emit(HomeViewModel.CapsuleFilter.HOT) } + _filterCapsuleSelect.emit(HomeViewModel.CapsuleFilter.HOT) + } } override fun clickFollowBtn() { @@ -100,20 +109,50 @@ class HomeViewModelImpl @Inject constructor( viewModelScope.launch { _isClickedFAB.emit(!isClickedFAB.value) } } - override fun resetNearbyCapsules(){ + override fun clickFriendsDisplay() { + viewModelScope.launch { _isFriendsCapsuleDisplay.emit(!isFriendsCapsuleDisplay.value) } + } + + override fun resetNearbyCapsules() { viewModelScope.launch { _capsuleList.emit(emptyList()) } } - override fun getNearbyCapsules(latitude: Double, longitude: Double, distance: Double, capsuleType: String) { - Log.d("티티","$latitude, $longitude, $distance, $capsuleType") + override fun getNearbyMyCapsules( + latitude: Double, + longitude: Double, + distance: Double, + capsuleType: String + ) { + Log.d("티티", "$latitude, $longitude, $distance, $capsuleType") viewModelScope.launch { - nearbyMyCapsulesHomeUseCase(latitude,longitude, distance, capsuleType ).collect{ result-> + nearbyMyCapsulesHomeUseCase( + latitude, + longitude, + distance, + capsuleType + ).collect { result -> result.onSuccess { _capsuleList.emit(it.capsuleMarkers) }.onFail { - Log.d("티티","getNearbyCapsules 실패") + Log.d("티티", "getNearbyCapsules 실패") + } + } + } + } + + override fun getNearbyFriendsCapsules(latitude: Double, longitude: Double, distance: Double) { + viewModelScope.launch { + nearbyFriendsCapsulesHomeUseCase( + latitude, + longitude, + distance, + ).collect { result -> + result.onSuccess { + _capsuleList.emit(capsuleList.value + it.capsuleMarkers) + }.onFail { + Log.d("티티", "getNearbyCapsules 실패") } } } diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_home.xml b/frontend/ARchive/app/src/main/res/layout/fragment_home.xml index 179184749..88a291687 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_home.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_home.xml @@ -27,13 +27,13 @@ + app:layout_constraintTop_toTopOf="parent" /> + + From 882fc5681d6345cd9de6259175c35dc9fdac1b42 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Tue, 7 May 2024 15:14:26 +0900 Subject: [PATCH 252/301] =?UTF-8?q?refact=20:=20=EC=A7=80=EB=8F=84?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EB=82=B4=EC=9C=84=EC=B9=98,=20refash=20?= =?UTF-8?q?=EC=8B=9C=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ui/home/HomeFragment.kt | 56 +++++++++++++++---- 1 file changed, 44 insertions(+), 12 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt index 5ec6464ab..19d34da65 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt @@ -66,7 +66,7 @@ class HomeFragment : BaseFragment(R.layo override fun updateLeafMarker(info: LeafMarkerInfo, marker: Marker) { super.updateLeafMarker(info, marker) val key = info.key as CapsuleClusteringKey - marker.icon = when(key.capsuleType){ + marker.icon = when (key.capsuleType) { CapsuleType.SECRET -> OverlayImage.fromResource(R.drawable.ic_marker_pin_secret) CapsuleType.GROUP -> OverlayImage.fromResource(R.drawable.ic_marker_pin_group) CapsuleType.PUBLIC -> OverlayImage.fromResource(R.drawable.ic_marker_pin_public) @@ -97,6 +97,7 @@ class HomeFragment : BaseFragment(R.layo } map.toMap() } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) locationUtil = LocationUtil(requireContext()) @@ -189,15 +190,22 @@ class HomeFragment : BaseFragment(R.layo viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.isFriendsCapsuleDisplay.collect { state -> - if (state && ( viewModel.filterCapsuleSelect.value == HomeViewModel.CapsuleFilter.ALL - || viewModel.filterCapsuleSelect.value == HomeViewModel.CapsuleFilter.PUBLIC )){ - clusterer.map?.let { map -> - val cameraTarget = map.cameraPosition.target + clusterer.map?.let { map -> + if (state && (viewModel.filterCapsuleSelect.value == HomeViewModel.CapsuleFilter.ALL + || viewModel.filterCapsuleSelect.value == HomeViewModel.CapsuleFilter.PUBLIC) + ) { viewModel.getNearbyFriendsCapsules( - cameraTarget.latitude, - cameraTarget.longitude, + map.cameraPosition.target.latitude, + map.cameraPosition.target.longitude, getRadiusForCurrentZoom(), ) + } else { + viewModel.getNearbyMyCapsules( + map.cameraPosition.target.latitude, + map.cameraPosition.target.longitude, + getRadiusForCurrentZoom(), + viewModel.filterCapsuleSelect.value.name + ) } } } @@ -207,7 +215,7 @@ class HomeFragment : BaseFragment(R.layo viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.capsuleList.collect { - clusterer.map?.let{ _ -> + clusterer.map?.let { _ -> // 마커 지우는 로직 clusterer.clear() // 마커 찍는 로직 @@ -256,7 +264,11 @@ class HomeFragment : BaseFragment(R.layo private fun addMarker(capsuleList: List) { val keyTagMap: Map = capsuleList.associate { - CapsuleClusteringKey(id = it.id, capsuleType = it.capsuleType, position = LatLng(it.latitude, it.longitude)) to null + CapsuleClusteringKey( + id = it.id, + capsuleType = it.capsuleType, + position = LatLng(it.latitude, it.longitude) + ) to null } clusterer.addAll(keyTagMap) @@ -271,6 +283,15 @@ class HomeFragment : BaseFragment(R.layo radius, viewModel.filterCapsuleSelect.value.toString() ) + if (viewModel.isFriendsCapsuleDisplay.value && (viewModel.filterCapsuleSelect.value == HomeViewModel.CapsuleFilter.ALL + || viewModel.filterCapsuleSelect.value == HomeViewModel.CapsuleFilter.PUBLIC) + ){ + viewModel.getNearbyFriendsCapsules( + latitude, + longitude, + getRadiusForCurrentZoom() + ) + } } } @@ -283,6 +304,15 @@ class HomeFragment : BaseFragment(R.layo getRadiusForCurrentZoom(), viewModel.filterCapsuleSelect.value.toString() ) + if (viewModel.isFriendsCapsuleDisplay.value && (viewModel.filterCapsuleSelect.value == HomeViewModel.CapsuleFilter.ALL + || viewModel.filterCapsuleSelect.value == HomeViewModel.CapsuleFilter.PUBLIC) + ){ + viewModel.getNearbyFriendsCapsules( + cameraTarget.latitude, + cameraTarget.longitude, + getRadiusForCurrentZoom() + ) + } } } @@ -290,9 +320,11 @@ class HomeFragment : BaseFragment(R.layo private fun getRadiusForCurrentZoom(): Double { val currentZoom = clusterer.map?.cameraPosition?.zoom ?: return FIXZOOM - val closestZoomLevel = zoomToRadiusMap.keys.minByOrNull { kotlin.math.abs(it - currentZoom) } + val closestZoomLevel = + zoomToRadiusMap.keys.minByOrNull { kotlin.math.abs(it - currentZoom) } - return zoomToRadiusMap[closestZoomLevel] ?: throw IllegalArgumentException("Invalid zoom level: $currentZoom") + return zoomToRadiusMap[closestZoomLevel] + ?: throw IllegalArgumentException("Invalid zoom level: $currentZoom") } override fun onRequestPermissionsResult( @@ -327,7 +359,7 @@ class HomeFragment : BaseFragment(R.layo override fun onHiddenChanged(hidden: Boolean) { super.onHiddenChanged(hidden) - if (!hidden){ + if (!hidden) { fetchCapsulesInCameraFocus() } } From 74b415cd3a278d1263e527e246a5b183721c04cd Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Tue, 7 May 2024 15:22:04 +0900 Subject: [PATCH 253/301] =?UTF-8?q?design=20:=20=ED=99=88=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/src/main/res/drawable/ic_refresh_24.xml | 5 +++++ .../ARchive/app/src/main/res/layout/fragment_home.xml | 10 ++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 frontend/ARchive/app/src/main/res/drawable/ic_refresh_24.xml diff --git a/frontend/ARchive/app/src/main/res/drawable/ic_refresh_24.xml b/frontend/ARchive/app/src/main/res/drawable/ic_refresh_24.xml new file mode 100644 index 000000000..98469ca33 --- /dev/null +++ b/frontend/ARchive/app/src/main/res/drawable/ic_refresh_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_home.xml b/frontend/ARchive/app/src/main/res/layout/fragment_home.xml index 88a291687..5b53f194b 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_home.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_home.xml @@ -131,10 +131,12 @@ android:background="@drawable/corner_radius_22" android:backgroundTint="@color/white_alpha60" android:scaleType="center" + android:src="@drawable/ic_refresh_24" + android:tint="@color/main_1" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toBottomOf="@id/notificationBtn" /> - From 2f254563a5fa9cbd9caab34ad555f67b878a901d Mon Sep 17 00:00:00 2001 From: comst19 Date: Tue, 7 May 2024 19:14:40 +0900 Subject: [PATCH 254/301] =?UTF-8?q?design:=20CapsulePreviewDialog=EC=97=90?= =?UTF-8?q?=20Menu=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dialog/CapsulePreviewDialogFragment.kt | 43 +++++++++++++++++++ .../app/src/main/res/drawable/ic_menu_24.xml | 15 +++++++ ...rectangle_solid_corner_10dp_stroke_1dp.xml | 15 +++++++ .../fragment_capsule_preview_dialog.xml | 28 ++++++++++++ .../main/res/layout/popup_menu_capsule.xml | 37 ++++++++++++++++ 5 files changed, 138 insertions(+) create mode 100644 frontend/ARchive/app/src/main/res/drawable/ic_menu_24.xml create mode 100644 frontend/ARchive/app/src/main/res/drawable/rectangle_solid_corner_10dp_stroke_1dp.xml create mode 100644 frontend/ARchive/app/src/main/res/layout/popup_menu_capsule.xml diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogFragment.kt index 83968f0c1..a890f1957 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogFragment.kt @@ -6,10 +6,18 @@ import android.animation.ObjectAnimator import android.content.DialogInterface import android.os.Bundle import android.view.Gravity +import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.view.animation.LinearInterpolator +import android.widget.Button +import android.widget.LinearLayout +import android.widget.PopupMenu +import android.widget.PopupWindow +import android.widget.TextView +import android.widget.Toast import androidx.constraintlayout.widget.ConstraintSet +import androidx.core.content.ContextCompat import androidx.fragment.app.setFragmentResult import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle @@ -17,6 +25,7 @@ import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import com.droidblossom.archive.R import com.droidblossom.archive.databinding.FragmentCapsulePreviewDialogBinding +import com.droidblossom.archive.databinding.PopupMenuCapsuleBinding import com.droidblossom.archive.presentation.base.BaseDialogFragment import com.droidblossom.archive.presentation.ui.capsule.CapsuleDetailActivity import com.droidblossom.archive.presentation.ui.home.HomeFragment @@ -106,6 +115,10 @@ class CapsulePreviewDialogFragment : viewModel.openCapsule(capsuleId.toLong()) } } + + capsuleMenuImg.setOnClickListener { view -> + showPopupMenu(view) + } } } private fun initObserver() { @@ -206,6 +219,36 @@ class CapsulePreviewDialogFragment : animator.start() } + private fun showPopupMenu(view: View) { + val binding = PopupMenuCapsuleBinding.inflate(LayoutInflater.from(requireContext()), null, false) + + val density = requireContext().resources.displayMetrics.density + val widthPixels = (120 * density).toInt() + + val popupWindow = PopupWindow(binding.root, + widthPixels, + LinearLayout.LayoutParams.WRAP_CONTENT, + true) + + popupWindow.contentView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED) + val popupWidth = popupWindow.contentView.measuredWidth + + binding.menuMap.setOnClickListener { + + popupWindow.dismiss() + } + binding.menuModify.setOnClickListener { + + popupWindow.dismiss() + } + binding.menuDelete.setOnClickListener { + + popupWindow.dismiss() + } + + val xOffset = (view.width / 2) - (popupWidth / 2) - 200 + popupWindow.showAsDropDown(view, xOffset, -view.height) + } override fun onDismiss(dialog: DialogInterface) { super.onDismiss(dialog) val capsuleState = Bundle().apply { diff --git a/frontend/ARchive/app/src/main/res/drawable/ic_menu_24.xml b/frontend/ARchive/app/src/main/res/drawable/ic_menu_24.xml new file mode 100644 index 000000000..015c411fa --- /dev/null +++ b/frontend/ARchive/app/src/main/res/drawable/ic_menu_24.xml @@ -0,0 +1,15 @@ + + + + + \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/drawable/rectangle_solid_corner_10dp_stroke_1dp.xml b/frontend/ARchive/app/src/main/res/drawable/rectangle_solid_corner_10dp_stroke_1dp.xml new file mode 100644 index 000000000..fcaf1ed40 --- /dev/null +++ b/frontend/ARchive/app/src/main/res/drawable/rectangle_solid_corner_10dp_stroke_1dp.xml @@ -0,0 +1,15 @@ + + + + + + + + + + \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_capsule_preview_dialog.xml b/frontend/ARchive/app/src/main/res/layout/fragment_capsule_preview_dialog.xml index 067689067..7460b0a13 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_capsule_preview_dialog.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_capsule_preview_dialog.xml @@ -142,6 +142,34 @@ bind:imageUrl="@{vm.capsuleSummaryResponse.profileUrl}" bind:placeholder="@{@drawable/app_symbol}"/> + + + + + + + + + + + + \ No newline at end of file From 8f2ec25b9afc5d6e6e97997183bbea55836948bd Mon Sep 17 00:00:00 2001 From: comst19 Date: Tue, 7 May 2024 20:41:56 +0900 Subject: [PATCH 255/301] =?UTF-8?q?design:=20CapsuleDetail=EC=97=90=20Menu?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/capsule/CapsuleDetailActivity.kt | 37 ++++++++++++++++ .../dialog/CapsulePreviewDialogFragment.kt | 42 ++++++++++++------- .../res/layout/activity_capsule_detail.xml | 12 +++++- 3 files changed, 75 insertions(+), 16 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/capsule/CapsuleDetailActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/capsule/CapsuleDetailActivity.kt index 8bb50696c..6d2a534c3 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/capsule/CapsuleDetailActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/capsule/CapsuleDetailActivity.kt @@ -3,13 +3,19 @@ package com.droidblossom.archive.presentation.ui.capsule import android.content.Context 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 android.widget.LinearLayout +import android.widget.PopupWindow import androidx.activity.viewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import com.droidblossom.archive.R import com.droidblossom.archive.databinding.ActivityCapsuleDetailBinding +import com.droidblossom.archive.databinding.PopupMenuCapsuleBinding import com.droidblossom.archive.domain.model.common.ContentType import com.droidblossom.archive.domain.model.common.ContentUrl import com.droidblossom.archive.presentation.base.BaseActivity @@ -74,6 +80,9 @@ class CapsuleDetailActivity : binding.closeBtn.setOnClickListener { finish() } + binding.capsuleMenuImg.setOnClickListener { view -> + showPopupMenu(view) + } } private fun initRVA() { @@ -83,6 +92,34 @@ class CapsuleDetailActivity : } + private fun showPopupMenu(view: View) { + val popupMenuBinding = PopupMenuCapsuleBinding.inflate(LayoutInflater.from(this), null, false) + + val density = this.resources.displayMetrics.density + val widthPixels = (120 * density).toInt() + + val popupWindow = PopupWindow(popupMenuBinding.root, + widthPixels, + LinearLayout.LayoutParams.WRAP_CONTENT, + true) + + popupWindow.contentView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED) + val popupWidth = popupWindow.contentView.measuredWidth + + val xOffset = (view.width / 2) - (popupWidth / 2) - 213 + popupWindow.showAsDropDown(view, xOffset, -view.height) + + popupMenuBinding.menuMap.setOnClickListener { + popupWindow.dismiss() + } + popupMenuBinding.menuModify.setOnClickListener { + popupWindow.dismiss() + } + popupMenuBinding.menuDelete.setOnClickListener { + popupWindow.dismiss() + } + } + override fun observeData() { lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogFragment.kt index a890f1957..a643b4c7b 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogFragment.kt @@ -121,6 +121,7 @@ class CapsulePreviewDialogFragment : } } } + private fun initObserver() { viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { @@ -138,7 +139,7 @@ class CapsulePreviewDialogFragment : } } - viewLifecycleOwner.lifecycleScope.launch{ + viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.capsulePreviewDialogEvents.collect { event -> when (event) { @@ -197,14 +198,19 @@ class CapsulePreviewDialogFragment : constraintSet.applyTo(constraintLayout) } - private fun moveCapsuleDetail(){ - val intent = CapsuleDetailActivity.newIntent(requireContext(), capsuleId.toLong(), capsuleType!!) + private fun moveCapsuleDetail() { + val intent = + CapsuleDetailActivity.newIntent(requireContext(), capsuleId.toLong(), capsuleType!!) startActivity(intent) } private fun animateProgressBar() { - viewModel.capsulePreviewDialogEvent(CapsulePreviewDialogViewModel.CapsulePreviewDialogEvent.ShowToastMessage("캡슐이 열리는 중입니다.")) + viewModel.capsulePreviewDialogEvent( + CapsulePreviewDialogViewModel.CapsulePreviewDialogEvent.ShowToastMessage( + "캡슐이 열리는 중입니다." + ) + ) val animator = ObjectAnimator.ofInt(binding.openProgressBar, "progress", 0, 100).apply { duration = 2000 // 2초 동안 interpolator = LinearInterpolator() // 여기에 LinearInterpolator 적용 @@ -220,39 +226,40 @@ class CapsulePreviewDialogFragment : } private fun showPopupMenu(view: View) { - val binding = PopupMenuCapsuleBinding.inflate(LayoutInflater.from(requireContext()), null, false) + val popupMenuBinding = + PopupMenuCapsuleBinding.inflate(LayoutInflater.from(requireContext()), null, false) val density = requireContext().resources.displayMetrics.density val widthPixels = (120 * density).toInt() - val popupWindow = PopupWindow(binding.root, + val popupWindow = PopupWindow( + popupMenuBinding.root, widthPixels, LinearLayout.LayoutParams.WRAP_CONTENT, - true) + true + ) popupWindow.contentView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED) val popupWidth = popupWindow.contentView.measuredWidth - binding.menuMap.setOnClickListener { - + popupMenuBinding.menuMap.setOnClickListener { popupWindow.dismiss() } - binding.menuModify.setOnClickListener { - + popupMenuBinding.menuModify.setOnClickListener { popupWindow.dismiss() } - binding.menuDelete.setOnClickListener { - + popupMenuBinding.menuDelete.setOnClickListener { popupWindow.dismiss() } val xOffset = (view.width / 2) - (popupWidth / 2) - 200 popupWindow.showAsDropDown(view, xOffset, -view.height) } + override fun onDismiss(dialog: DialogInterface) { super.onDismiss(dialog) val capsuleState = Bundle().apply { - putInt("capsuleIndex",capsuleIndex) + putInt("capsuleIndex", capsuleIndex) putLong("capsuleId", capsuleId.toLong()) putBoolean("isOpened", viewModel.capsuleOpenState.value) } @@ -260,7 +267,12 @@ class CapsulePreviewDialogFragment : } companion object { - fun newInstance(capsuleIndex: String, capsuleId: String, capsuleType: String, calledFromCamera : Boolean): CapsulePreviewDialogFragment { + fun newInstance( + capsuleIndex: String, + capsuleId: String, + capsuleType: String, + calledFromCamera: Boolean + ): CapsulePreviewDialogFragment { val args = Bundle().apply { putString("capsule_index", capsuleIndex) putString("capsule_id", capsuleId) diff --git a/frontend/ARchive/app/src/main/res/layout/activity_capsule_detail.xml b/frontend/ARchive/app/src/main/res/layout/activity_capsule_detail.xml index f248c10f5..07600a942 100644 --- a/frontend/ARchive/app/src/main/res/layout/activity_capsule_detail.xml +++ b/frontend/ARchive/app/src/main/res/layout/activity_capsule_detail.xml @@ -206,10 +206,20 @@ android:textAppearance="@style/TextAppearance.App.caption4" android:textColor="@color/gray_700" app:layout_constraintBottom_toBottomOf="@id/userProfileImg" - app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintEnd_toStartOf="@id/capsuleMenuImg" app:layout_constraintStart_toEndOf="@id/userProfileImg" app:layout_constraintTop_toTopOf="@id/userProfileImg" /> + + Date: Tue, 7 May 2024 20:53:26 +0900 Subject: [PATCH 256/301] =?UTF-8?q?refact=20:=20=EB=B9=84=EB=B0=80,=20?= =?UTF-8?q?=EA=B7=B8=EB=A3=B9=EC=9D=BC=EB=95=8C=20=EC=B9=9C=EA=B5=AC=20ono?= =?UTF-8?q?ff=20=EC=88=A8=EA=B8=B0=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ui/home/HomeFragment.kt | 70 ++++++++++--------- .../app/src/main/res/layout/fragment_home.xml | 1 + 2 files changed, 37 insertions(+), 34 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt index 19d34da65..d91f5cb2a 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt @@ -50,41 +50,43 @@ class HomeFragment : BaseFragment(R.layo // https://navermaps.github.io/android-map-sdk/guide-ko/5-8.html private val clusterer: Clusterer = - Clusterer.Builder().clusterMarkerUpdater(object : DefaultClusterMarkerUpdater(){ - override fun updateClusterMarker(info: ClusterMarkerInfo, marker: Marker) { - super.updateClusterMarker(info, marker) - marker.icon = OverlayImage.fromResource(R.drawable.ic_cluster_marker_46) - marker.captionColor = Color.BLACK - marker.captionHaloColor = ContextCompat.getColor(requireContext(), R.color.main_bg_1) - marker.captionTextSize = if (info.size >= 100) 15f else 18f - marker.onClickListener = Overlay.OnClickListener{ - // 클러스터된 거 클릭이벤트 - 나중에 클러스터에 행당된 캡슐들 사이드에 보여주거나 하면 좋을듯? - true - } - } - }).leafMarkerUpdater(object : DefaultLeafMarkerUpdater(){ - override fun updateLeafMarker(info: LeafMarkerInfo, marker: Marker) { - super.updateLeafMarker(info, marker) - val key = info.key as CapsuleClusteringKey - marker.icon = when (key.capsuleType) { - CapsuleType.SECRET -> OverlayImage.fromResource(R.drawable.ic_marker_pin_secret) - CapsuleType.GROUP -> OverlayImage.fromResource(R.drawable.ic_marker_pin_group) - CapsuleType.PUBLIC -> OverlayImage.fromResource(R.drawable.ic_marker_pin_public) + Clusterer.Builder() + .clusterMarkerUpdater(object : DefaultClusterMarkerUpdater() { + override fun updateClusterMarker(info: ClusterMarkerInfo, marker: Marker) { + super.updateClusterMarker(info, marker) + marker.icon = OverlayImage.fromResource(R.drawable.ic_cluster_marker_46) + marker.captionColor = Color.BLACK + marker.captionHaloColor = + ContextCompat.getColor(requireContext(), R.color.main_bg_1) + marker.captionTextSize = if (info.size >= 100) 15f else 18f + marker.onClickListener = Overlay.OnClickListener { + // 클러스터된 거 클릭이벤트 - 나중에 클러스터에 행당된 캡슐들 사이드에 보여주거나 하면 좋을듯? + true + } } - marker.onClickListener = Overlay.OnClickListener { - viewModel.homeEvent( - HomeViewModel.HomeEvent.ShowCapsulePreviewDialog( - key.id.toString(), - key.capsuleType.toString() + }).leafMarkerUpdater(object : DefaultLeafMarkerUpdater() { + override fun updateLeafMarker(info: LeafMarkerInfo, marker: Marker) { + super.updateLeafMarker(info, marker) + val key = info.key as CapsuleClusteringKey + marker.icon = when (key.capsuleType) { + CapsuleType.SECRET -> OverlayImage.fromResource(R.drawable.ic_marker_pin_secret) + CapsuleType.GROUP -> OverlayImage.fromResource(R.drawable.ic_marker_pin_group) + CapsuleType.PUBLIC -> OverlayImage.fromResource(R.drawable.ic_marker_pin_public) + } + marker.onClickListener = Overlay.OnClickListener { + viewModel.homeEvent( + HomeViewModel.HomeEvent.ShowCapsulePreviewDialog( + key.id.toString(), + key.capsuleType.toString() + ) ) - ) - true + true + } } - } - }) - .minZoom(MINZOOM.toInt()+2) - .maxZoom(MAXZOOM.toInt()-2) - .build() + }) + .minZoom(MINZOOM.toInt() + 2) + .maxZoom(MAXZOOM.toInt() - 2) + .build() private val zoomToRadiusMap: Map by lazy { @@ -285,7 +287,7 @@ class HomeFragment : BaseFragment(R.layo ) if (viewModel.isFriendsCapsuleDisplay.value && (viewModel.filterCapsuleSelect.value == HomeViewModel.CapsuleFilter.ALL || viewModel.filterCapsuleSelect.value == HomeViewModel.CapsuleFilter.PUBLIC) - ){ + ) { viewModel.getNearbyFriendsCapsules( latitude, longitude, @@ -306,7 +308,7 @@ class HomeFragment : BaseFragment(R.layo ) if (viewModel.isFriendsCapsuleDisplay.value && (viewModel.filterCapsuleSelect.value == HomeViewModel.CapsuleFilter.ALL || viewModel.filterCapsuleSelect.value == HomeViewModel.CapsuleFilter.PUBLIC) - ){ + ) { viewModel.getNearbyFriendsCapsules( cameraTarget.latitude, cameraTarget.longitude, diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_home.xml b/frontend/ARchive/app/src/main/res/layout/fragment_home.xml index 5b53f194b..6f1ae4b09 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_home.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_home.xml @@ -147,6 +147,7 @@ android:background="@drawable/corner_radius_22" android:backgroundTint="@{vm.isFriendsCapsuleDisplay ? @color/main_alpha85 : @color/white_alpha60}" android:gravity="center" + android:visibility="@{vm.filterCapsuleSelect==filter.PUBLIC || vm.filterCapsuleSelect == filter.ALL ? View.VISIBLE : View.GONE }" android:onClick="@{()->vm.clickFriendsDisplay()}" android:scaleType="center" android:text="@{vm.isFriendsCapsuleDisplay ? `친구\nON` : `친구\nOFF` }" From c43edca32f974a10816cf2f0de6c2c9e7ac1455f Mon Sep 17 00:00:00 2001 From: comst19 Date: Thu, 9 May 2024 20:46:44 +0900 Subject: [PATCH 257/301] =?UTF-8?q?design:=20AR=ED=99=94=EB=A9=B4=EC=97=90?= =?UTF-8?q?=20filter=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/res/layout/fragment_camera.xml | 105 +++++++++++++++++- 1 file changed, 102 insertions(+), 3 deletions(-) diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_camera.xml b/frontend/ARchive/app/src/main/res/layout/fragment_camera.xml index 34c9fd2c8..791f4fbcb 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_camera.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_camera.xml @@ -1,9 +1,19 @@ + + + - + android:layout_height="match_parent"> + + + + + + + + + + + + + + + + \ No newline at end of file From 02ea08ccc76e05eaa21d870d5132be64fcea8f14 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Fri, 10 May 2024 01:25:06 +0900 Subject: [PATCH 258/301] =?UTF-8?q?fact=20:=20ar=20=EC=B9=9C=EA=B5=AC=20?= =?UTF-8?q?=EC=BA=A1=EC=8A=90=20=EB=B3=B4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ui/camera/CameraFragment.kt | 31 ++++++++++++++---- .../presentation/ui/camera/CameraViewModel.kt | 4 +++ .../ui/camera/CameraViewModelImpl.kt | 14 +++++++- .../src/main/res/layout/fragment_camera.xml | 32 +++++++++++++++++-- 4 files changed, 70 insertions(+), 11 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt index f5ebae733..c943b4a47 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt @@ -4,6 +4,8 @@ import android.Manifest import android.os.Bundle import android.util.Log import android.view.View +import androidx.core.content.ContentProviderCompat.requireContext +import androidx.core.location.LocationManagerCompat.getCurrentLocation import androidx.fragment.app.FragmentManager import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle @@ -24,6 +26,7 @@ import com.google.ar.sceneform.rendering.ViewAttachmentManager import dagger.hilt.android.AndroidEntryPoint import io.github.sceneview.ar.ARSceneView import io.github.sceneview.ar.node.AnchorNode +import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.filter import kotlinx.coroutines.launch @@ -33,11 +36,13 @@ class CameraFragment : FragmentManagerProvider { override val viewModel: CameraViewModelImpl by viewModels() + // private val capsules: MutableList = mutableListOf() lateinit var arSceneView: ARSceneView private lateinit var session: Session private lateinit var config: Config private lateinit var viewAttachmentManager: ViewAttachmentManager + private val locationUtil by lazy { LocationUtil(requireContext()) } override fun provideFragmentManager(): FragmentManager { return parentFragmentManager @@ -83,8 +88,8 @@ class CameraFragment : viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.capsuleList.collect { capsuleList -> - arSceneView.onSessionUpdated = { session, frame -> - if (viewModel.anchorNodes.value.isEmpty()) { + if (capsuleList.isNotEmpty()) { + arSceneView.onSessionUpdated = { session, frame -> Log.d("CameraFragmentAR", "earth setting start") val earth = session.earth if (earth == null) { @@ -113,8 +118,22 @@ class CameraFragment : } } } + arSceneView.onSessionUpdated = { _, _ -> } } + } + } + } + } + viewLifecycleOwner.lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.isFriendsCapsuleDisplay.collect { state -> + viewModel.clearAnchorNode() + arSceneView.childNodes.forEach { + arSceneView.removeChildNode(it) + } + locationUtil.getCurrentLocation { latitude, longitude -> + viewModel.getCapsules(latitude = latitude, longitude = longitude) } } } @@ -129,10 +148,8 @@ class CameraFragment : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) showLoading(requireContext()) - val locationUtil = LocationUtil(requireContext()) - locationUtil.getCurrentLocation { latitude, longitude -> - viewModel.getCapsules(latitude = latitude, longitude = longitude) - } + binding.vm = viewModel + arSceneView = binding.sceneView viewAttachmentManager = ViewAttachmentManager( @@ -152,7 +169,7 @@ class CameraFragment : private fun addAnchorNode(anchor: Anchor, capsule: CapsuleAnchor) { - Log.d("CameraFragmentAR", "addAnchorNode added") + Log.d("CameraFragmentAR", "${capsule.id} addAnchorNode added") arSceneView.let { sceneView -> viewAttachmentManager.let { attachManager -> ARContentNode( diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModel.kt index 4ca512012..ae89b1194 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModel.kt @@ -11,6 +11,8 @@ interface CameraViewModel { val cameraEvents: SharedFlow val capsuleListSize: SharedFlow val anchorNodes: StateFlow> + val isFriendsCapsuleDisplay : StateFlow + fun getCapsules(latitude: Double, longitude: Double) @@ -19,6 +21,8 @@ interface CameraViewModel { fun addAnchorNode(anchorNode: AnchorNode) fun clearAnchorNode() + fun clickFriendsDisplay() + sealed class CameraEvent{ data class ShowCapsulePreviewDialog(val capsuleId: String, val capsuleType: String) : CameraEvent() diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModelImpl.kt index 32ecd9650..2a01ed29b 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModelImpl.kt @@ -1,5 +1,6 @@ package com.droidblossom.archive.presentation.ui.camera +import android.util.Log import androidx.lifecycle.viewModelScope import com.droidblossom.archive.domain.model.capsule.CapsuleAnchor import com.droidblossom.archive.domain.usecase.capsule.NearbyFriendsCapsulesARUseCase @@ -38,6 +39,11 @@ class CameraViewModelImpl@Inject constructor( private val _anchorNodes = MutableStateFlow>(mutableListOf()) override val anchorNodes get() = _anchorNodes + private val _isFriendsCapsuleDisplay = MutableStateFlow(false) + override val isFriendsCapsuleDisplay: StateFlow + get() = _isFriendsCapsuleDisplay + + override fun addAnchorNode(anchorNode: AnchorNode) { val updatedList = _anchorNodes.value.toMutableList() updatedList.add(anchorNode) @@ -46,6 +52,7 @@ class CameraViewModelImpl@Inject constructor( override fun clearAnchorNode() { _anchorNodes.value = mutableListOf() + _capsuleListSize.value = 0 } @@ -56,7 +63,7 @@ class CameraViewModelImpl@Inject constructor( } override fun getCapsules(latitude: Double, longitude: Double){ - if (true) getMyCapsules(latitude, longitude) + if (!isFriendsCapsuleDisplay.value) getMyCapsules(latitude, longitude) else getFriendsCapsules(latitude, longitude) } @@ -83,6 +90,7 @@ class CameraViewModelImpl@Inject constructor( _capsuleListSize.value = _capsuleList.value.size if (capsuleList.value.isEmpty()) { cameraEvent(CameraViewModel.CameraEvent.DismissLoading) + _capsuleList.emit(listOf()) } }.onFail { cameraEvent(CameraViewModel.CameraEvent.DismissLoading) @@ -90,4 +98,8 @@ class CameraViewModelImpl@Inject constructor( } } } + + override fun clickFriendsDisplay() { + viewModelScope.launch { _isFriendsCapsuleDisplay.emit(!isFriendsCapsuleDisplay.value) } + } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_camera.xml b/frontend/ARchive/app/src/main/res/layout/fragment_camera.xml index 34c9fd2c8..c1880b0b3 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_camera.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_camera.xml @@ -1,12 +1,17 @@ + xmlns:tools="http://schemas.android.com/tools" + xmlns:app="http://schemas.android.com/apk/res-auto"> + + - @@ -16,5 +21,26 @@ android:layout_width="match_parent" android:layout_height="match_parent" /> - + + + + \ No newline at end of file From 5a993a05d411fe15d373728515e441a366b8e118 Mon Sep 17 00:00:00 2001 From: comst19 Date: Fri, 10 May 2024 01:45:37 +0900 Subject: [PATCH 259/301] =?UTF-8?q?Revert=20"design:=20AR=ED=99=94?= =?UTF-8?q?=EB=A9=B4=EC=97=90=20filter=20=EC=B6=94=EA=B0=80"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit c43edca32f974a10816cf2f0de6c2c9e7ac1455f. --- .../src/main/res/layout/fragment_camera.xml | 105 +----------------- 1 file changed, 3 insertions(+), 102 deletions(-) diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_camera.xml b/frontend/ARchive/app/src/main/res/layout/fragment_camera.xml index 791f4fbcb..34c9fd2c8 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_camera.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_camera.xml @@ -1,19 +1,9 @@ - - - - - - - - - - - - - - - - - - - - + android:layout_height="match_parent" /> \ No newline at end of file From ef919c72b67258db24b9bcd3de75d28ea894878b Mon Sep 17 00:00:00 2001 From: comst19 Date: Fri, 10 May 2024 03:01:59 +0900 Subject: [PATCH 260/301] =?UTF-8?q?feat:=20AR=20Camera=20=ED=95=84?= =?UTF-8?q?=ED=84=B0=EB=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ui/camera/CameraFragment.kt | 61 ++++++----- .../presentation/ui/camera/CameraViewModel.kt | 15 ++- .../ui/camera/CameraViewModelImpl.kt | 55 +++++++--- .../src/main/res/layout/fragment_camera.xml | 101 ++++++++++++++---- 4 files changed, 168 insertions(+), 64 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt index c943b4a47..83bd2c679 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt @@ -4,8 +4,7 @@ import android.Manifest import android.os.Bundle import android.util.Log import android.view.View -import androidx.core.content.ContentProviderCompat.requireContext -import androidx.core.location.LocationManagerCompat.getCurrentLocation +import android.view.ViewGroup import androidx.fragment.app.FragmentManager import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle @@ -26,9 +25,9 @@ import com.google.ar.sceneform.rendering.ViewAttachmentManager import dagger.hilt.android.AndroidEntryPoint import io.github.sceneview.ar.ARSceneView import io.github.sceneview.ar.node.AnchorNode -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.filter import kotlinx.coroutines.launch +import java.util.Date @AndroidEntryPoint class CameraFragment : @@ -36,14 +35,11 @@ class CameraFragment : FragmentManagerProvider { override val viewModel: CameraViewModelImpl by viewModels() - - // private val capsules: MutableList = mutableListOf() lateinit var arSceneView: ARSceneView private lateinit var session: Session private lateinit var config: Config private lateinit var viewAttachmentManager: ViewAttachmentManager private val locationUtil by lazy { LocationUtil(requireContext()) } - override fun provideFragmentManager(): FragmentManager { return parentFragmentManager } @@ -63,6 +59,10 @@ class CameraFragment : sheet.show(parentFragmentManager, "CapsulePreviewDialog") } + is CameraViewModel.CameraEvent.ShowLoading -> { + showLoading(requireContext()) + } + is CameraViewModel.CameraEvent.DismissLoading -> { dismissLoading() } @@ -77,7 +77,7 @@ class CameraFragment : repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.anchorNodes .filter { anchorNodes -> - anchorNodes.size == viewModel.capsuleListSize.value + anchorNodes.size == viewModel.capsuleListSize } .collect { dismissLoading() @@ -88,8 +88,8 @@ class CameraFragment : viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.capsuleList.collect { capsuleList -> - if (capsuleList.isNotEmpty()) { - arSceneView.onSessionUpdated = { session, frame -> + arSceneView.onSessionUpdated = { session, frame -> + if (viewModel.capsuleList.value.isNotEmpty() && !viewModel.isCapsulesAdded) { Log.d("CameraFragmentAR", "earth setting start") val earth = session.earth if (earth == null) { @@ -116,28 +116,16 @@ class CameraFragment : ) addAnchorNode(earthAnchor, capsule) } + viewModel.isCapsulesAdded = true } } - arSceneView.onSessionUpdated = { _, _ -> } } - } - } - } - } - viewLifecycleOwner.lifecycleScope.launch { - repeatOnLifecycle(Lifecycle.State.STARTED) { - viewModel.isFriendsCapsuleDisplay.collect { state -> - viewModel.clearAnchorNode() - arSceneView.childNodes.forEach { - arSceneView.removeChildNode(it) - } - locationUtil.getCurrentLocation { latitude, longitude -> - viewModel.getCapsules(latitude = latitude, longitude = longitude) } } } } + } val permissionList = arrayOf( @@ -149,13 +137,18 @@ class CameraFragment : super.onViewCreated(view, savedInstanceState) showLoading(requireContext()) binding.vm = viewModel - + binding.view = this arSceneView = binding.sceneView viewAttachmentManager = ViewAttachmentManager( arSceneView.context, arSceneView ) + + val layoutParams = binding.filterAll.layoutParams as ViewGroup.MarginLayoutParams + layoutParams.topMargin += getStatusBarHeight() + binding.filterAll.layoutParams = layoutParams + initView() createSession() } @@ -165,6 +158,7 @@ class CameraFragment : config.geospatialMode = Config.GeospatialMode.ENABLED config.planeFindingMode = Config.PlaneFindingMode.DISABLED } + } @@ -195,6 +189,7 @@ class CameraFragment : ) } } + } private fun createSession() { @@ -207,15 +202,16 @@ class CameraFragment : override fun onResume() { super.onResume() viewAttachmentManager.onResume() + locationUtil.getCurrentLocation { latitude, longitude -> + viewModel.getCapsules(latitude = latitude, longitude = longitude) + } } override fun onHiddenChanged(hidden: Boolean) { super.onHiddenChanged(hidden) if (hidden) { dismissLoading() - for (anchorNode in viewModel.anchorNodes.value) { - arSceneView.removeChildNode(anchorNode) - } + arSceneView.clearChildNodes() viewModel.clearAnchorNode() viewAttachmentManager.onPause() arSceneView.session?.pause() @@ -223,16 +219,25 @@ class CameraFragment : showLoading(requireContext()) arSceneView.session?.resume() viewAttachmentManager.onResume() - val locationUtil = LocationUtil(requireContext()) locationUtil.getCurrentLocation { latitude, longitude -> viewModel.getCapsules(latitude = latitude, longitude = longitude) } } } + fun onClickFilter(capsuleFilterType: CameraViewModel.CapsuleFilterType) { + showLoading(requireContext()) + arSceneView.clearChildNodes() + viewModel.clearAnchorNode() + locationUtil.getCurrentLocation { latitude, longitude -> + viewModel.selectFilter(capsuleFilterType, latitude = latitude, longitude = longitude) + } + } + companion object { const val TAG = "CAMERA" fun newIntent() = CameraFragment() } + } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModel.kt index ae89b1194..ee0a89625 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModel.kt @@ -7,13 +7,15 @@ import kotlinx.coroutines.flow.StateFlow interface CameraViewModel { + val selectedCapsuleFilter : StateFlow val capsuleList:StateFlow> val cameraEvents: SharedFlow - val capsuleListSize: SharedFlow + var capsuleListSize: Int val anchorNodes: StateFlow> val isFriendsCapsuleDisplay : StateFlow + val isCapsulesAdded: Boolean - + fun selectFilter(capsuleFilterType: CapsuleFilterType, latitude: Double, longitude: Double) fun getCapsules(latitude: Double, longitude: Double) fun cameraEvent(event : CameraEvent) @@ -21,12 +23,19 @@ interface CameraViewModel { fun addAnchorNode(anchorNode: AnchorNode) fun clearAnchorNode() - fun clickFriendsDisplay() sealed class CameraEvent{ data class ShowCapsulePreviewDialog(val capsuleId: String, val capsuleType: String) : CameraEvent() + object ShowLoading : CameraEvent() object DismissLoading : CameraEvent() + } + enum class CapsuleFilterType(val description: String){ + FILTER_ALL("ALL"), + FILTER_SECRET("SECRET"), + FILTER_GROUP("GROUP"), + FILTER_PUBLIC_MY("PUBLIC"), + FILTER_PUBLIC_FRIEND("PUBLIC_FRIEND") } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModelImpl.kt index 2a01ed29b..060c2edbe 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraViewModelImpl.kt @@ -33,8 +33,7 @@ class CameraViewModelImpl@Inject constructor( override val capsuleList: StateFlow> get() = _capsuleList - private val _capsuleListSize = MutableStateFlow(-1) - override val capsuleListSize = _capsuleListSize.asStateFlow() + override var capsuleListSize = -1 private val _anchorNodes = MutableStateFlow>(mutableListOf()) override val anchorNodes get() = _anchorNodes @@ -43,16 +42,23 @@ class CameraViewModelImpl@Inject constructor( override val isFriendsCapsuleDisplay: StateFlow get() = _isFriendsCapsuleDisplay + private val _selectedCapsuleFilter = MutableStateFlow(CameraViewModel.CapsuleFilterType.FILTER_ALL) + override val selectedCapsuleFilter: StateFlow + get() = _selectedCapsuleFilter + override var isCapsulesAdded = false + override fun addAnchorNode(anchorNode: AnchorNode) { val updatedList = _anchorNodes.value.toMutableList() updatedList.add(anchorNode) _anchorNodes.value = updatedList } - override fun clearAnchorNode() { + isCapsulesAdded = false + capsuleListSize = -1 + _capsuleList.value = mutableListOf() _anchorNodes.value = mutableListOf() - _capsuleListSize.value = 0 + capsuleListSize = 0 } @@ -63,18 +69,40 @@ class CameraViewModelImpl@Inject constructor( } override fun getCapsules(latitude: Double, longitude: Double){ - if (!isFriendsCapsuleDisplay.value) getMyCapsules(latitude, longitude) - else getFriendsCapsules(latitude, longitude) + when(selectedCapsuleFilter.value){ + CameraViewModel.CapsuleFilterType.FILTER_ALL -> { + getMyCapsules(latitude, longitude) + } + CameraViewModel.CapsuleFilterType.FILTER_SECRET -> { + getMyCapsules(latitude, longitude) + } + CameraViewModel.CapsuleFilterType.FILTER_GROUP -> { + getMyCapsules(latitude, longitude) + } + CameraViewModel.CapsuleFilterType.FILTER_PUBLIC_MY -> { + getMyCapsules(latitude, longitude) + } + CameraViewModel.CapsuleFilterType.FILTER_PUBLIC_FRIEND -> { + getFriendsCapsules(latitude, longitude) + } + } + } + override fun selectFilter(capsuleFilterType: CameraViewModel.CapsuleFilterType, latitude: Double, longitude: Double){ + _selectedCapsuleFilter.value = capsuleFilterType + getCapsules(latitude, longitude) + } + private fun getMyCapsules(latitude: Double, longitude: Double){ viewModelScope.launch { - nearbyMyCapsulesARUseCase(latitude,longitude,0.1,"ALL").collect{ result-> + nearbyMyCapsulesARUseCase(latitude,longitude,0.1,selectedCapsuleFilter.value.description).collect{ result-> result.onSuccess { - _capsuleList.emit(it.capsuleAnchors) - _capsuleListSize.value = _capsuleList.value.size + capsuleListSize = it.capsuleAnchors.size + _capsuleList.value = it.capsuleAnchors if (capsuleList.value.isEmpty()){ cameraEvent(CameraViewModel.CameraEvent.DismissLoading) + capsuleListSize = 0 } }.onFail { cameraEvent(CameraViewModel.CameraEvent.DismissLoading) @@ -86,11 +114,11 @@ class CameraViewModelImpl@Inject constructor( viewModelScope.launch { nearbyFriendsCapsulesARUseCase(latitude,longitude,0.1).collect{ result-> result.onSuccess { - _capsuleList.emit(it.capsuleAnchors) - _capsuleListSize.value = _capsuleList.value.size + capsuleListSize = it.capsuleAnchors.size + _capsuleList.value = it.capsuleAnchors if (capsuleList.value.isEmpty()) { cameraEvent(CameraViewModel.CameraEvent.DismissLoading) - _capsuleList.emit(listOf()) + capsuleListSize = 0 } }.onFail { cameraEvent(CameraViewModel.CameraEvent.DismissLoading) @@ -99,7 +127,4 @@ class CameraViewModelImpl@Inject constructor( } } - override fun clickFriendsDisplay() { - viewModelScope.launch { _isFriendsCapsuleDisplay.emit(!isFriendsCapsuleDisplay.value) } - } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_camera.xml b/frontend/ARchive/app/src/main/res/layout/fragment_camera.xml index c1880b0b3..8bb024f46 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_camera.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_camera.xml @@ -4,10 +4,15 @@ xmlns:app="http://schemas.android.com/apk/res-auto"> - + + @@ -22,25 +27,85 @@ android:layout_height="match_parent" /> + + + + + android:onClick="@{()->view.onClickFilter(filter.FILTER_GROUP)}" + android:paddingHorizontal="14dp" + android:text="그룹" + android:textAppearance="@style/TextAppearance.App.caption1" + android:textColor="@{vm.selectedCapsuleFilter==filter.FILTER_GROUP ? @color/white : @color/gray_700 }" + app:layout_constraintStart_toEndOf="@id/filterSecret" + app:layout_constraintTop_toTopOf="@id/filterAll" /> + + + \ No newline at end of file From 3af4e53885b5c7ba946a57c647f115e5d1e5c94a Mon Sep 17 00:00:00 2001 From: comst19 Date: Fri, 10 May 2024 11:54:45 +0900 Subject: [PATCH 261/301] =?UTF-8?q?feat:=20CameraFragment=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99=20=EC=8B=9C=20=EA=B6=8C=ED=95=9C=20=ED=99=95?= =?UTF-8?q?=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/ARchive/app/build.gradle | 3 +++ .../archive/presentation/ui/MainActivity.kt | 23 +++++++++++++++---- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/frontend/ARchive/app/build.gradle b/frontend/ARchive/app/build.gradle index fb654f877..11c77ff95 100644 --- a/frontend/ARchive/app/build.gradle +++ b/frontend/ARchive/app/build.gradle @@ -189,6 +189,9 @@ dependencies { // ConcatAdapter implementation "androidx.recyclerview:recyclerview:1.3.2" + // TedPermission : https://github.com/ParkSangGwon/TedPermission + implementation("io.github.ParkSangGwon:tedpermission-normal:3.3.0") + } kapt { correctErrorTypes true diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt index 68155b6e2..0370aa2aa 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt @@ -1,5 +1,6 @@ package com.droidblossom.archive.presentation.ui +import android.Manifest import android.content.Context import android.content.Intent import android.os.Bundle @@ -19,12 +20,14 @@ import com.droidblossom.archive.presentation.ui.skin.SkinFragment import com.droidblossom.archive.presentation.ui.social.SocialFragment import com.droidblossom.archive.util.DataStoreUtils import com.droidblossom.archive.util.MyFirebaseMessagingService +import com.droidblossom.archive.util.PermissionsUtil import com.droidblossom.archive.util.onFail import com.droidblossom.archive.util.onSuccess +import com.gun0912.tedpermission.PermissionListener +import com.gun0912.tedpermission.normal.TedPermission import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import javax.inject.Inject @@ -39,7 +42,7 @@ class MainActivity : BaseActivity(R.layout.activi override val viewModel: Nothing? = null lateinit var viewBinding: ActivityMainBinding - + override fun observeData() {} override fun onCreate(savedInstanceState: Bundle?) { @@ -77,8 +80,20 @@ class MainActivity : BaseActivity(R.layout.activi private fun initBottomNav(){ binding.fab.setOnClickListener { - showFragment(CameraFragment.newIntent(), CameraFragment.TAG) - binding.bottomNavigation.selectedItemId = R.id.menuCamera + + TedPermission.create() + .setPermissionListener(object : PermissionListener{ + override fun onPermissionGranted() { + showFragment(CameraFragment.newIntent(), CameraFragment.TAG) + binding.bottomNavigation.selectedItemId = R.id.menuCamera + } + override fun onPermissionDenied(p0: MutableList?) { + showToastMessage("카메라 권한이 없으면 AR 기능을 사용할 수 없습니다.") + } + }) + .setDeniedMessage("카메라 권한이 필요해요. '설정'에서 권한을 허용하면 AR 기능을 이용할 수 있습니다.") + .setPermissions(Manifest.permission.CAMERA) + .check() } binding.bottomNavigation.setOnItemSelectedListener { From f921b94502245e040d56e30cfde962ab054d2ae4 Mon Sep 17 00:00:00 2001 From: comst19 Date: Fri, 10 May 2024 18:57:08 +0900 Subject: [PATCH 262/301] design: PermissionDialog Design --- .../main/res/drawable/ic_camera_outline.xml | 5 + .../main/res/drawable/ic_concats_outline.xml | 5 + .../main/res/drawable/ic_location_outline.xml | 14 +++ .../src/main/res/layout/dialog_permission.xml | 107 ++++++++++++++++++ 4 files changed, 131 insertions(+) create mode 100644 frontend/ARchive/app/src/main/res/drawable/ic_camera_outline.xml create mode 100644 frontend/ARchive/app/src/main/res/drawable/ic_concats_outline.xml create mode 100644 frontend/ARchive/app/src/main/res/drawable/ic_location_outline.xml create mode 100644 frontend/ARchive/app/src/main/res/layout/dialog_permission.xml diff --git a/frontend/ARchive/app/src/main/res/drawable/ic_camera_outline.xml b/frontend/ARchive/app/src/main/res/drawable/ic_camera_outline.xml new file mode 100644 index 000000000..0f37a729f --- /dev/null +++ b/frontend/ARchive/app/src/main/res/drawable/ic_camera_outline.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/ARchive/app/src/main/res/drawable/ic_concats_outline.xml b/frontend/ARchive/app/src/main/res/drawable/ic_concats_outline.xml new file mode 100644 index 000000000..28301fbdb --- /dev/null +++ b/frontend/ARchive/app/src/main/res/drawable/ic_concats_outline.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/ARchive/app/src/main/res/drawable/ic_location_outline.xml b/frontend/ARchive/app/src/main/res/drawable/ic_location_outline.xml new file mode 100644 index 000000000..02425c36b --- /dev/null +++ b/frontend/ARchive/app/src/main/res/drawable/ic_location_outline.xml @@ -0,0 +1,14 @@ + + + + diff --git a/frontend/ARchive/app/src/main/res/layout/dialog_permission.xml b/frontend/ARchive/app/src/main/res/layout/dialog_permission.xml new file mode 100644 index 000000000..ec36206bf --- /dev/null +++ b/frontend/ARchive/app/src/main/res/layout/dialog_permission.xml @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 1bb9cf04f2d7017f8f3e63e43fb6d060246b3abf Mon Sep 17 00:00:00 2001 From: comst19 Date: Fri, 10 May 2024 18:59:39 +0900 Subject: [PATCH 263/301] =?UTF-8?q?refact:=20Main=20->=20CameraFragment=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99=ED=95=98=EA=B8=B0=20=EC=A0=84=EC=97=90=20?= =?UTF-8?q?=EC=B9=B4=EB=A9=94=EB=9D=BC=20=EA=B6=8C=ED=95=9C=20=ED=99=95?= =?UTF-8?q?=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/presentation/base/BaseActivity.kt | 18 +++++ .../archive/presentation/base/BaseFragment.kt | 18 +++++ .../customview/PermissionDialogFragment.kt | 81 +++++++++++++++++++ .../archive/presentation/ui/MainActivity.kt | 41 ++++++---- 4 files changed, 142 insertions(+), 16 deletions(-) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/PermissionDialogFragment.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseActivity.kt index 377078874..5e5965ef6 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseActivity.kt @@ -3,9 +3,12 @@ package com.droidblossom.archive.presentation.base import android.content.ClipData import android.content.ClipboardManager import android.content.Context +import android.content.Intent import android.graphics.Color +import android.net.Uri import android.os.Build import android.os.Bundle +import android.provider.Settings import android.util.Log import android.view.View import android.view.WindowManager @@ -16,6 +19,7 @@ import androidx.databinding.DataBindingUtil import androidx.databinding.ViewDataBinding import com.droidblossom.archive.presentation.customview.HomeSnackBarSmall import com.droidblossom.archive.presentation.customview.LoadingDialog +import com.droidblossom.archive.presentation.customview.PermissionDialogFragment import com.droidblossom.archive.presentation.model.AppEvent import com.droidblossom.archive.util.ClipboardUtil import kotlinx.coroutines.Job @@ -114,4 +118,18 @@ abstract class BaseActivity(@LayoutRes v val toast = Toast.makeText(this, message, Toast.LENGTH_SHORT) toast.show() } + + fun showSettingsDialog(permissionType : PermissionDialogFragment.PermissionType) { + + val sheet = PermissionDialogFragment.newIntent( + permissionType.name + ) { + val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply { + data = Uri.fromParts("package", packageName, null) + } + startActivity(intent) + } + sheet.show(supportFragmentManager, "PermissionDialog") + + } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseFragment.kt index 2660156fb..e667e6a01 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseFragment.kt @@ -1,8 +1,11 @@ package com.droidblossom.archive.presentation.base import android.content.Context +import android.content.Intent +import android.net.Uri import android.os.Build import android.os.Bundle +import android.provider.Settings import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -12,6 +15,7 @@ import androidx.databinding.DataBindingUtil import androidx.databinding.ViewDataBinding import androidx.fragment.app.Fragment import com.droidblossom.archive.presentation.customview.LoadingDialog +import com.droidblossom.archive.presentation.customview.PermissionDialogFragment import com.droidblossom.archive.util.ClipboardUtil import kotlinx.coroutines.Job @@ -67,6 +71,20 @@ abstract class BaseFragment(@LayoutRes va toast.show() } + fun showSettingsDialog(permissionType : PermissionDialogFragment.PermissionType) { + + val sheet = PermissionDialogFragment.newIntent( + permissionType.name + ) { + val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply { + data = Uri.fromParts("package", requireContext().packageName, null) + } + startActivity(intent) + } + sheet.show(parentFragmentManager, "PermissionDialog") + + } + fun showLoading(context: Context) { if (!loadingState) { loadingDialog = LoadingDialog(context) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/PermissionDialogFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/PermissionDialogFragment.kt new file mode 100644 index 000000000..9c1aecb77 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/PermissionDialogFragment.kt @@ -0,0 +1,81 @@ +package com.droidblossom.archive.presentation.customview + +import android.os.Bundle +import android.view.View +import android.view.ViewGroup +import com.droidblossom.archive.R +import com.droidblossom.archive.databinding.DialogPermissionBinding +import com.droidblossom.archive.presentation.base.BaseDialogFragment + +class PermissionDialogFragment( + private val onClick: () -> Unit +) : BaseDialogFragment(R.layout.dialog_permission) { + + override fun onStart() { + super.onStart() + val dialog = dialog + if (dialog != null) { + val width = ViewGroup.LayoutParams.MATCH_PARENT + val height = ViewGroup.LayoutParams.WRAP_CONTENT + dialog.window?.setLayout(width, height) + } + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + val permissionType = arguments?.getString("permission") + + when(permissionType){ + PermissionType.LOCATION.name -> { + binding.messageT.text = "ARchive 은 위치 권한이 필수입니다. 앱을 사용하려면 위치 권한을 허용해 주세요." + binding.permissionImg.setImageResource(R.drawable.ic_location_outline) + binding.leftBtn.text = "앱 종료" + } + PermissionType.CAMERA.name -> { + binding.messageT.text = "AR 기능을 사용하려면 카메라 권한이 필요합니다. '권한'에서 카메라 권한을 허용해 주세요." + binding.permissionImg.setImageResource(R.drawable.ic_camera_outline) + } + PermissionType.CONTACTS.name -> { + binding.messageT.text = "연락처를 통해 앱 내에서 친구를 찾아보세요. 권한을 허용하면 친구 찾기가 가능합니다." + binding.permissionImg.setImageResource(R.drawable.ic_concats_outline) + } + PermissionType.NOTIFICATIONS.name -> { + binding.messageT.text = "ARchive 앱의 알림을 받기 위해서는 알림 권한이 필요합니다. 알림을 통해 중요한 정보와 업데이트를 놓치지 마세요." + binding.permissionImg.setImageResource(R.drawable.ic_alarm_24) + } + } + + + binding.leftBtn.setOnClickListener { + this.dismiss() + } + + binding.rightBtn.setOnClickListener { + onClick() + this.dismiss() + } + } + + companion object { + + fun newIntent( + permission: String, + onRightClick: () -> Unit, + ): PermissionDialogFragment { + val args = Bundle().apply { + putString("permission", permission) + } + return PermissionDialogFragment(onRightClick).apply { + arguments = args + } + } + } + + enum class PermissionType(val description: String){ + LOCATION("위치"), + CAMERA("카메라"), + CONTACTS("연락처"), + NOTIFICATIONS("알람") + } +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt index 0370aa2aa..b3c101438 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt @@ -1,16 +1,24 @@ package com.droidblossom.archive.presentation.ui import android.Manifest +import android.app.AlertDialog import android.content.Context import android.content.Intent +import android.net.Uri import android.os.Bundle +import android.provider.Settings import android.util.Log +import androidx.activity.result.contract.ActivityResultContracts import androidx.fragment.app.Fragment import com.droidblossom.archive.R import com.droidblossom.archive.data.dto.member.request.FcmTokenRequsetDto import com.droidblossom.archive.databinding.ActivityMainBinding +import com.droidblossom.archive.databinding.DialogPermissionBinding import com.droidblossom.archive.domain.usecase.member.FcmTokenUseCase import com.droidblossom.archive.presentation.base.BaseActivity +import com.droidblossom.archive.presentation.customview.CommonDialogFragment +import com.droidblossom.archive.presentation.customview.PermissionDialogFragment +import com.droidblossom.archive.presentation.ui.auth.AuthActivity import com.droidblossom.archive.presentation.ui.camera.CameraFragment import com.droidblossom.archive.presentation.ui.home.HomeFragment import com.droidblossom.archive.presentation.ui.mypage.MyPageFragment @@ -20,7 +28,6 @@ import com.droidblossom.archive.presentation.ui.skin.SkinFragment import com.droidblossom.archive.presentation.ui.social.SocialFragment import com.droidblossom.archive.util.DataStoreUtils import com.droidblossom.archive.util.MyFirebaseMessagingService -import com.droidblossom.archive.util.PermissionsUtil import com.droidblossom.archive.util.onFail import com.droidblossom.archive.util.onSuccess import com.gun0912.tedpermission.PermissionListener @@ -42,7 +49,22 @@ class MainActivity : BaseActivity(R.layout.activi override val viewModel: Nothing? = null lateinit var viewBinding: ActivityMainBinding - + + private val requestCameraPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted -> + if (isGranted) { + showFragment(CameraFragment.newIntent(), CameraFragment.TAG) + binding.bottomNavigation.selectedItemId = R.id.menuCamera + } else { + if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) { + showToastMessage("AR 기능을 사용하려면 카메라 권한이 필요합니다.") + } else { + showSettingsDialog(PermissionDialogFragment.PermissionType.CAMERA) + } + } + } + + + override fun observeData() {} override fun onCreate(savedInstanceState: Bundle?) { @@ -80,20 +102,7 @@ class MainActivity : BaseActivity(R.layout.activi private fun initBottomNav(){ binding.fab.setOnClickListener { - - TedPermission.create() - .setPermissionListener(object : PermissionListener{ - override fun onPermissionGranted() { - showFragment(CameraFragment.newIntent(), CameraFragment.TAG) - binding.bottomNavigation.selectedItemId = R.id.menuCamera - } - override fun onPermissionDenied(p0: MutableList?) { - showToastMessage("카메라 권한이 없으면 AR 기능을 사용할 수 없습니다.") - } - }) - .setDeniedMessage("카메라 권한이 필요해요. '설정'에서 권한을 허용하면 AR 기능을 이용할 수 있습니다.") - .setPermissions(Manifest.permission.CAMERA) - .check() + requestCameraPermissionLauncher.launch(Manifest.permission.CAMERA) } binding.bottomNavigation.setOnItemSelectedListener { From 795f007dcb31921b0813574e952649b34f2582b5 Mon Sep 17 00:00:00 2001 From: comst19 Date: Sat, 11 May 2024 00:31:14 +0900 Subject: [PATCH 264/301] =?UTF-8?q?feat:=20=EC=A3=BC=EC=86=8C=EB=A1=9D=20?= =?UTF-8?q?=EC=B9=9C=EA=B5=AC=20=EC=B6=94=EA=B0=80=ED=95=A0=20=EB=95=8C=20?= =?UTF-8?q?=EC=A3=BC=EC=86=8C=EB=A1=9D=20=EC=9D=BD=EA=B8=B0=20=EA=B6=8C?= =?UTF-8?q?=ED=95=9C=20=ED=99=95=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../friend/addfriend/AddFriendActivity.kt | 2 +- .../addfriend/SearchFriendNicknameFragment.kt | 26 ++++++++++++++++--- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendActivity.kt index 3f9cf3db0..f1f1bb1a4 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendActivity.kt @@ -28,7 +28,7 @@ class AddFriendActivity : BaseActivity + AddFriendRVA { position -> viewModel.checkAddFriendList(position) } } + + private val requestContactsPermissionLauncher = + registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted -> + if (isGranted) { + navController.navigate(R.id.action_searchFriendNicknameFragment_to_searchFriendNumberFragment) + } else { + if (shouldShowRequestPermissionRationale(Manifest.permission.READ_CONTACTS)) { + showToastMessage("앱에서 친구를 찾기 위해 연락처 접근 권한이 필요합니다. 권한을 허용해 주세요.") + } else { + showSettingsDialog(PermissionDialogFragment.PermissionType.CONTACTS) + } + } + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding.vm = viewModel @@ -68,8 +87,7 @@ class SearchFriendNicknameFragment : } binding.addCV.setOnClickListener { - //viewModel.resetList() - navController.navigate(R.id.action_searchFriendNicknameFragment_to_searchFriendNumberFragment) + requestContactsPermissionLauncher.launch(Manifest.permission.READ_CONTACTS) } binding.searchOpenBtnT.setOnClickListener { @@ -100,7 +118,7 @@ class SearchFriendNicknameFragment : viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { - viewModel.addFriendListUI.collect{ friends -> + viewModel.addFriendListUI.collect { friends -> addFriendRVA.submitList(friends) } } From 1d5e8467bd0108a1fe31905aa7a7edd79ec13432 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Sat, 11 May 2024 02:41:18 +0900 Subject: [PATCH 265/301] =?UTF-8?q?feat:=20=EB=82=B4=20=EC=A0=84=ED=99=94?= =?UTF-8?q?=EB=B2=88=ED=98=B8=20=EC=B9=9C=EC=B6=94=20=EB=AA=BB=EB=B3=B4?= =?UTF-8?q?=EB=82=B4=EA=B2=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/ARchive/app/src/main/AndroidManifest.xml | 1 + .../java/com/droidblossom/archive/util/ContactsUtils.kt | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/frontend/ARchive/app/src/main/AndroidManifest.xml b/frontend/ARchive/app/src/main/AndroidManifest.xml index 28bc1c0fb..7e8369bcb 100644 --- a/frontend/ARchive/app/src/main/AndroidManifest.xml +++ b/frontend/ARchive/app/src/main/AndroidManifest.xml @@ -7,6 +7,7 @@ + diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/ContactsUtils.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/ContactsUtils.kt index 24ba0c25e..5ed9de50f 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/ContactsUtils.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/ContactsUtils.kt @@ -1,17 +1,22 @@ package com.droidblossom.archive.util +import android.annotation.SuppressLint import android.content.ContentResolver import android.content.Context import android.provider.ContactsContract +import android.telephony.TelephonyManager import android.util.Log import com.droidblossom.archive.data.dto.friend.request.PhoneBooks object ContactsUtils { + @SuppressLint("MissingPermission") fun getContacts(context : Context) : List { val resolver: ContentResolver = context.contentResolver val phoneUri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI + + val tm = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager val projection = arrayOf( ContactsContract.CommonDataKinds.Phone.CONTACT_ID, ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, @@ -30,6 +35,9 @@ object ContactsUtils { Log.d("GetContact", "이름 : $name 번호 : $number") } } + numberList.removeIf { + it.originPhone == tm.line1Number.replace("+82","0") + } cursor!!.close() return numberList.toList() } From 0c0d6b5adf63df9cdf1f454b7b36f99bb2e364f0 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Sat, 11 May 2024 03:41:23 +0900 Subject: [PATCH 266/301] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EA=B6=8C=ED=95=9C=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../customview/PermissionDialogFragment.kt | 20 ++++++++++---- .../archive/presentation/ui/MainActivity.kt | 8 ------ .../friend/addfriend/AddFriendActivity.kt | 26 +++++++++---------- .../addfriend/SearchFriendNicknameFragment.kt | 18 +++++++++---- .../addfriend/SearchFriendNumberFragment.kt | 20 +++++++++++--- .../app/src/main/res/drawable/ic_call_24.xml | 5 ++++ 6 files changed, 62 insertions(+), 35 deletions(-) create mode 100644 frontend/ARchive/app/src/main/res/drawable/ic_call_24.xml diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/PermissionDialogFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/PermissionDialogFragment.kt index 9c1aecb77..be9c036e0 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/PermissionDialogFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/PermissionDialogFragment.kt @@ -26,24 +26,33 @@ class PermissionDialogFragment( val permissionType = arguments?.getString("permission") - when(permissionType){ + when (permissionType) { PermissionType.LOCATION.name -> { binding.messageT.text = "ARchive 은 위치 권한이 필수입니다. 앱을 사용하려면 위치 권한을 허용해 주세요." binding.permissionImg.setImageResource(R.drawable.ic_location_outline) binding.leftBtn.text = "앱 종료" } + PermissionType.CAMERA.name -> { binding.messageT.text = "AR 기능을 사용하려면 카메라 권한이 필요합니다. '권한'에서 카메라 권한을 허용해 주세요." binding.permissionImg.setImageResource(R.drawable.ic_camera_outline) } + PermissionType.CONTACTS.name -> { - binding.messageT.text = "연락처를 통해 앱 내에서 친구를 찾아보세요. 권한을 허용하면 친구 찾기가 가능합니다." + binding.messageT.text = "연락처를 통해 앱 내에서 친구를 찾아보세요. 연락처와 전화 권한을 허용하면 친구 찾기가 가능합니다." binding.permissionImg.setImageResource(R.drawable.ic_concats_outline) } + PermissionType.NOTIFICATIONS.name -> { - binding.messageT.text = "ARchive 앱의 알림을 받기 위해서는 알림 권한이 필요합니다. 알림을 통해 중요한 정보와 업데이트를 놓치지 마세요." + binding.messageT.text = + "ARchive 앱의 알림을 받기 위해서는 알림 권한이 필요합니다. 알림을 통해 중요한 정보와 업데이트를 놓치지 마세요." binding.permissionImg.setImageResource(R.drawable.ic_alarm_24) } + + PermissionType.CALL.name -> { + binding.messageT.text = "연락처를 통해 앱 내에서 친구를 찾아보세요. 전화 권한을 허용하면 친구 찾기가 가능합니다." + binding.permissionImg.setImageResource(R.drawable.ic_call_24) + } } @@ -72,10 +81,11 @@ class PermissionDialogFragment( } } - enum class PermissionType(val description: String){ + enum class PermissionType(val description: String) { LOCATION("위치"), CAMERA("카메라"), CONTACTS("연락처"), - NOTIFICATIONS("알람") + NOTIFICATIONS("알람"), + CALL("전화"), } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt index b3c101438..b2125501b 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt @@ -1,24 +1,18 @@ package com.droidblossom.archive.presentation.ui import android.Manifest -import android.app.AlertDialog import android.content.Context import android.content.Intent -import android.net.Uri import android.os.Bundle -import android.provider.Settings import android.util.Log import androidx.activity.result.contract.ActivityResultContracts import androidx.fragment.app.Fragment import com.droidblossom.archive.R import com.droidblossom.archive.data.dto.member.request.FcmTokenRequsetDto import com.droidblossom.archive.databinding.ActivityMainBinding -import com.droidblossom.archive.databinding.DialogPermissionBinding import com.droidblossom.archive.domain.usecase.member.FcmTokenUseCase import com.droidblossom.archive.presentation.base.BaseActivity -import com.droidblossom.archive.presentation.customview.CommonDialogFragment import com.droidblossom.archive.presentation.customview.PermissionDialogFragment -import com.droidblossom.archive.presentation.ui.auth.AuthActivity import com.droidblossom.archive.presentation.ui.camera.CameraFragment import com.droidblossom.archive.presentation.ui.home.HomeFragment import com.droidblossom.archive.presentation.ui.mypage.MyPageFragment @@ -30,8 +24,6 @@ import com.droidblossom.archive.util.DataStoreUtils import com.droidblossom.archive.util.MyFirebaseMessagingService import com.droidblossom.archive.util.onFail import com.droidblossom.archive.util.onSuccess -import com.gun0912.tedpermission.PermissionListener -import com.gun0912.tedpermission.normal.TedPermission import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendActivity.kt index f1f1bb1a4..38302b76c 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/AddFriendActivity.kt @@ -18,19 +18,19 @@ class AddFriendActivity : BaseActivity, - grantResults: IntArray - ) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults) - - if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { - viewModel.contactsSearch(ContactsUtils.getContacts(this)) - } else { - //showToastMessage("권한이 필요합니다.") - } - } +// override fun onRequestPermissionsResult( +// requestCode: Int, +// permissions: Array, +// grantResults: IntArray +// ) { +// super.onRequestPermissionsResult(requestCode, permissions, grantResults) +// +// if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { +// //viewModel.contactsSearch(ContactsUtils.getContacts(this)) +// } else { +// //showToastMessage("권한이 필요합니다.") +// } +// } companion object { const val ADDFRIEND = "add_friend" diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNicknameFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNicknameFragment.kt index f7b1cbbcd..9e56eb83e 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNicknameFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNicknameFragment.kt @@ -2,6 +2,7 @@ package com.droidblossom.archive.presentation.ui.mypage.friend.addfriend import android.Manifest import android.os.Bundle +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -40,12 +41,14 @@ class SearchFriendNicknameFragment : } private val requestContactsPermissionLauncher = - registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted -> - if (isGranted) { + registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions -> + if (permissions.all { it.value }) { navController.navigate(R.id.action_searchFriendNicknameFragment_to_searchFriendNumberFragment) } else { - if (shouldShowRequestPermissionRationale(Manifest.permission.READ_CONTACTS)) { - showToastMessage("앱에서 친구를 찾기 위해 연락처 접근 권한이 필요합니다. 권한을 허용해 주세요.") + if (shouldShowRequestPermissionRationale(Manifest.permission.READ_CONTACTS) && + shouldShowRequestPermissionRationale(Manifest.permission.READ_PHONE_NUMBERS) + ) { + showToastMessage("앱에서 친구를 찾기 위해 연락처와 전화 접근 권한이 필요합니다. 권한을 허용해 주세요.") } else { showSettingsDialog(PermissionDialogFragment.PermissionType.CONTACTS) } @@ -87,7 +90,12 @@ class SearchFriendNicknameFragment : } binding.addCV.setOnClickListener { - requestContactsPermissionLauncher.launch(Manifest.permission.READ_CONTACTS) + requestContactsPermissionLauncher.launch( + arrayOf( + Manifest.permission.READ_CONTACTS, + Manifest.permission.READ_PHONE_NUMBERS + ) + ) } binding.searchOpenBtnT.setOnClickListener { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNumberFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNumberFragment.kt index 8d743b282..b13abb7a0 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNumberFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNumberFragment.kt @@ -1,5 +1,6 @@ package com.droidblossom.archive.presentation.ui.mypage.friend.addfriend +import android.Manifest import android.annotation.SuppressLint import android.content.pm.PackageManager import android.graphics.Rect @@ -48,7 +49,8 @@ class SearchFriendNumberFragment : navController = Navigation.findNavController(view) initView() - permissionCheck() + viewModel.contactsSearch(ContactsUtils.getContacts(requireContext())) + //permissionCheck() } @SuppressLint("ClickableViewAccessibility", "NotifyDataSetChanged") @@ -153,18 +155,28 @@ class SearchFriendNumberFragment : } private fun permissionCheck() { - val status = ContextCompat.checkSelfPermission( + val contactStatus = ContextCompat.checkSelfPermission( requireContext(), "android.permission.READ_CONTACTS" ) - if (status == PackageManager.PERMISSION_GRANTED) { + val numberStatus = ContextCompat.checkSelfPermission( + requireContext(), + "android.permission.READ_PHONE_NUMBERS" + ) + if (contactStatus == PackageManager.PERMISSION_GRANTED + && numberStatus == PackageManager.PERMISSION_GRANTED + ) { viewModel.contactsSearch(ContactsUtils.getContacts(requireContext())) } else { ActivityCompat.requestPermissions( requireActivity(), - arrayOf("android.permission.READ_CONTACTS"), + arrayOf( + "android.permission.READ_PHONE_NUMBERS", + "android.permission.READ_CONTACTS", + ), 100 ) + } } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/drawable/ic_call_24.xml b/frontend/ARchive/app/src/main/res/drawable/ic_call_24.xml new file mode 100644 index 000000000..f56f10fd4 --- /dev/null +++ b/frontend/ARchive/app/src/main/res/drawable/ic_call_24.xml @@ -0,0 +1,5 @@ + + + From 5f15bf1676bef3f47eae112065c03324cc9a5cea Mon Sep 17 00:00:00 2001 From: comst19 Date: Sat, 11 May 2024 16:31:59 +0900 Subject: [PATCH 267/301] =?UTF-8?q?refact:=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20=EB=B3=B5=EA=B7=80=20=EC=8B=9C=20=EC=BD=9C?= =?UTF-8?q?=EB=B0=B1=20=EC=8B=A4=ED=96=89=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/presentation/base/BaseActivity.kt | 32 ++++++++++++------- .../archive/presentation/base/BaseFragment.kt | 30 +++++++++++------ 2 files changed, 41 insertions(+), 21 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseActivity.kt index 5e5965ef6..51523bb53 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseActivity.kt @@ -1,5 +1,6 @@ package com.droidblossom.archive.presentation.base +import android.app.Activity import android.content.ClipData import android.content.ClipboardManager import android.content.Context @@ -13,12 +14,15 @@ import android.util.Log import android.view.View import android.view.WindowManager import android.widget.Toast +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.LayoutRes import androidx.appcompat.app.AppCompatActivity import androidx.databinding.DataBindingUtil import androidx.databinding.ViewDataBinding import com.droidblossom.archive.presentation.customview.HomeSnackBarSmall import com.droidblossom.archive.presentation.customview.LoadingDialog +import com.droidblossom.archive.presentation.customview.PermissionDialogButtonClickListener import com.droidblossom.archive.presentation.customview.PermissionDialogFragment import com.droidblossom.archive.presentation.model.AppEvent import com.droidblossom.archive.util.ClipboardUtil @@ -38,6 +42,8 @@ abstract class BaseActivity(@LayoutRes v private lateinit var loadingDialog: LoadingDialog private var loadingState = false + private lateinit var resultLauncher: ActivityResultLauncher + private lateinit var onSettingsResult: () -> Unit abstract fun observeData() protected fun getStatusBarHeight(): Int { @@ -72,6 +78,11 @@ abstract class BaseActivity(@LayoutRes v setContentView(binding.root) fetchJob = viewModel?.fetchData() observeData() + + resultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { + onSettingsResult() + } + window.apply { decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_STABLE decorView.systemUiVisibility = decorView.systemUiVisibility or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR @@ -119,17 +130,16 @@ abstract class BaseActivity(@LayoutRes v toast.show() } - fun showSettingsDialog(permissionType : PermissionDialogFragment.PermissionType) { - - val sheet = PermissionDialogFragment.newIntent( - permissionType.name - ) { - val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply { - data = Uri.fromParts("package", packageName, null) - } - startActivity(intent) + fun showSettingsDialog(permissionType: PermissionDialogFragment.PermissionType, listener: PermissionDialogButtonClickListener) { + val dialog = PermissionDialogFragment.newInstance(permissionType.name, listener) + dialog.show(supportFragmentManager, "PermissionDialog") + } + fun navigateToAppSettings(onComplete: () -> Unit) { + onSettingsResult = onComplete // 저장된 콜백 설정 + val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply { + val uri = Uri.fromParts("package", packageName, null) + data = uri } - sheet.show(supportFragmentManager, "PermissionDialog") - + resultLauncher.launch(intent) // 설정 화면으로 이동 } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseFragment.kt index e667e6a01..354745c2f 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseFragment.kt @@ -1,5 +1,6 @@ package com.droidblossom.archive.presentation.base +import android.app.Activity import android.content.Context import android.content.Intent import android.net.Uri @@ -10,11 +11,14 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.Toast +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.LayoutRes import androidx.databinding.DataBindingUtil import androidx.databinding.ViewDataBinding import androidx.fragment.app.Fragment import com.droidblossom.archive.presentation.customview.LoadingDialog +import com.droidblossom.archive.presentation.customview.PermissionDialogButtonClickListener import com.droidblossom.archive.presentation.customview.PermissionDialogFragment import com.droidblossom.archive.util.ClipboardUtil import kotlinx.coroutines.Job @@ -30,11 +34,14 @@ abstract class BaseFragment(@LayoutRes va private lateinit var loadingDialog: LoadingDialog private var loadingState = false + private lateinit var resultLauncher: ActivityResultLauncher + protected fun getStatusBarHeight(): Int { val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android") return if (resourceId > 0) resources.getDimensionPixelSize(resourceId) else 0 } + private lateinit var onSettingsResult: () -> Unit abstract fun observeData() override fun onCreateView( @@ -56,6 +63,9 @@ abstract class BaseFragment(@LayoutRes va override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) fetchJob = viewModel.fetchData() + resultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { + onSettingsResult() + } observeData() } @@ -71,18 +81,18 @@ abstract class BaseFragment(@LayoutRes va toast.show() } - fun showSettingsDialog(permissionType : PermissionDialogFragment.PermissionType) { + fun showSettingsDialog(permissionType: PermissionDialogFragment.PermissionType, listener: PermissionDialogButtonClickListener) { + val dialog = PermissionDialogFragment.newInstance(permissionType.name, listener) + dialog.show(parentFragmentManager, "PermissionDialog") + } - val sheet = PermissionDialogFragment.newIntent( - permissionType.name - ) { - val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply { - data = Uri.fromParts("package", requireContext().packageName, null) - } - startActivity(intent) + fun navigateToAppSettings(onComplete: () -> Unit) { + onSettingsResult = onComplete // 저장된 콜백 설정 + val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply { + val uri = Uri.fromParts("package", requireContext().packageName, null) + data = uri } - sheet.show(parentFragmentManager, "PermissionDialog") - + resultLauncher.launch(intent) // 설정 화면으로 이동 } fun showLoading(context: Context) { From 1ac411878b60716bbf694eab41b9f421015e9513 Mon Sep 17 00:00:00 2001 From: comst19 Date: Sat, 11 May 2024 16:34:39 +0900 Subject: [PATCH 268/301] =?UTF-8?q?feat:=20PermissionDialogFragment=20?= =?UTF-8?q?=ED=81=B4=EB=A6=AD=20=EB=A6=AC=EC=8A=A4=EB=84=88=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../customview/PermissionDialogFragment.kt | 87 +++++++++++-------- .../res/drawable/ic_contact_phone_outline.xml | 5 ++ ...ts_outline.xml => ic_contacts_outline.xml} | 0 .../main/res/drawable/ic_default_outline.xml | 5 ++ 4 files changed, 61 insertions(+), 36 deletions(-) create mode 100644 frontend/ARchive/app/src/main/res/drawable/ic_contact_phone_outline.xml rename frontend/ARchive/app/src/main/res/drawable/{ic_concats_outline.xml => ic_contacts_outline.xml} (100%) create mode 100644 frontend/ARchive/app/src/main/res/drawable/ic_default_outline.xml diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/PermissionDialogFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/PermissionDialogFragment.kt index be9c036e0..69789b106 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/PermissionDialogFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/PermissionDialogFragment.kt @@ -7,8 +7,13 @@ import com.droidblossom.archive.R import com.droidblossom.archive.databinding.DialogPermissionBinding import com.droidblossom.archive.presentation.base.BaseDialogFragment +interface PermissionDialogButtonClickListener { + fun onLeftButtonClicked() + fun onRightButtonClicked() +} + class PermissionDialogFragment( - private val onClick: () -> Unit + private val listener: PermissionDialogButtonClickListener, ) : BaseDialogFragment(R.layout.dialog_permission) { override fun onStart() { @@ -18,64 +23,73 @@ class PermissionDialogFragment( val width = ViewGroup.LayoutParams.MATCH_PARENT val height = ViewGroup.LayoutParams.WRAP_CONTENT dialog.window?.setLayout(width, height) + dialog.setCancelable(false) } } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - val permissionType = arguments?.getString("permission") - - when (permissionType) { - PermissionType.LOCATION.name -> { - binding.messageT.text = "ARchive 은 위치 권한이 필수입니다. 앱을 사용하려면 위치 권한을 허용해 주세요." - binding.permissionImg.setImageResource(R.drawable.ic_location_outline) - binding.leftBtn.text = "앱 종료" - } - - PermissionType.CAMERA.name -> { - binding.messageT.text = "AR 기능을 사용하려면 카메라 권한이 필요합니다. '권한'에서 카메라 권한을 허용해 주세요." - binding.permissionImg.setImageResource(R.drawable.ic_camera_outline) - } - - PermissionType.CONTACTS.name -> { - binding.messageT.text = "연락처를 통해 앱 내에서 친구를 찾아보세요. 연락처와 전화 권한을 허용하면 친구 찾기가 가능합니다." - binding.permissionImg.setImageResource(R.drawable.ic_concats_outline) - } - - PermissionType.NOTIFICATIONS.name -> { - binding.messageT.text = - "ARchive 앱의 알림을 받기 위해서는 알림 권한이 필요합니다. 알림을 통해 중요한 정보와 업데이트를 놓치지 마세요." - binding.permissionImg.setImageResource(R.drawable.ic_alarm_24) - } - - PermissionType.CALL.name -> { - binding.messageT.text = "연락처를 통해 앱 내에서 친구를 찾아보세요. 전화 권한을 허용하면 친구 찾기가 가능합니다." - binding.permissionImg.setImageResource(R.drawable.ic_call_24) - } - } + val permissionType = arguments?.getString("permission") ?: "" + setupPermissionInfo(permissionType) + binding.leftBtn.text = if (permissionType == PermissionType.LOCATION.name) "앱 종료" else "취소" binding.leftBtn.setOnClickListener { + listener.onLeftButtonClicked() this.dismiss() } binding.rightBtn.setOnClickListener { - onClick() + listener.onRightButtonClicked() this.dismiss() } } - companion object { + private fun setupPermissionInfo(permissionType: String) { + val info = when (permissionType) { + PermissionType.LOCATION.name -> Pair( + "ARchive 은 위치 권한이 필수입니다. 앱을 사용하려면 위치 권한을 허용해 주세요.", + R.drawable.ic_location_outline + ) + PermissionType.CAMERA.name -> Pair( + "AR 기능을 사용하려면 카메라 권한이 필요합니다. '권한'에서 카메라 권한을 허용해 주세요.", + R.drawable.ic_camera_outline + ) + PermissionType.CONTACTS.name -> Pair( + "연락처를 통해 앱 내에서 친구를 찾아보세요. 연락처와 전화 권한을 허용하면 친구 찾기가 가능합니다.", + R.drawable.ic_contacts_outline + ) + PermissionType.NOTIFICATIONS.name -> Pair( + "ARchive 앱의 알림을 받기 위해서는 알림 권한이 필요합니다. 알림을 통해 중요한 정보와 업데이트를 놓치지 마세요.", + R.drawable.ic_alarm_24 + ) + PermissionType.CALL.name -> Pair( + "연락처를 통해 앱 내에서 친구를 찾아보세요. 전화 권한을 허용하면 친구 찾기가 가능합니다.", + R.drawable.ic_call_24 + ) + PermissionType.CONTACTS_AND_CALL.name -> Pair( + "연락처를 통해 앱 내에서 친구를 찾아보세요. 전화, 연락처 권한을 허용하면 친구 찾기가 가능합니다.", + R.drawable.ic_contact_phone_outline + ) + else -> Pair("", R.drawable.ic_default_outline) + } - fun newIntent( + binding.messageT.text = info.first + if (info.second != 0) { + binding.permissionImg.setImageResource(info.second) + } + } + + companion object { + fun newInstance( permission: String, - onRightClick: () -> Unit, + listener: PermissionDialogButtonClickListener ): PermissionDialogFragment { val args = Bundle().apply { putString("permission", permission) } - return PermissionDialogFragment(onRightClick).apply { + return PermissionDialogFragment(listener).apply { arguments = args } } @@ -87,5 +101,6 @@ class PermissionDialogFragment( CONTACTS("연락처"), NOTIFICATIONS("알람"), CALL("전화"), + CONTACTS_AND_CALL("연락처, 전화"), } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/drawable/ic_contact_phone_outline.xml b/frontend/ARchive/app/src/main/res/drawable/ic_contact_phone_outline.xml new file mode 100644 index 000000000..71a115913 --- /dev/null +++ b/frontend/ARchive/app/src/main/res/drawable/ic_contact_phone_outline.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/ARchive/app/src/main/res/drawable/ic_concats_outline.xml b/frontend/ARchive/app/src/main/res/drawable/ic_contacts_outline.xml similarity index 100% rename from frontend/ARchive/app/src/main/res/drawable/ic_concats_outline.xml rename to frontend/ARchive/app/src/main/res/drawable/ic_contacts_outline.xml diff --git a/frontend/ARchive/app/src/main/res/drawable/ic_default_outline.xml b/frontend/ARchive/app/src/main/res/drawable/ic_default_outline.xml new file mode 100644 index 000000000..6d629d60c --- /dev/null +++ b/frontend/ARchive/app/src/main/res/drawable/ic_default_outline.xml @@ -0,0 +1,5 @@ + + + + + From 9e7e64cd01d6b1d5548a0a38336be74603e658d3 Mon Sep 17 00:00:00 2001 From: comst19 Date: Sat, 11 May 2024 16:41:32 +0900 Subject: [PATCH 269/301] =?UTF-8?q?feat:=20=EC=82=AC=EC=9A=A9=EC=9E=90?= =?UTF-8?q?=EA=B0=80=20=EA=B6=8C=ED=95=9C=EC=9D=84=20=ED=92=80=EA=B3=A0=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=EC=97=90=20=EB=93=A4=EC=96=B4=EC=99=94?= =?UTF-8?q?=EC=9D=84=20=EB=95=8C=20=EA=B6=8C=ED=95=9C=20=EA=B2=80=EC=82=AC?= =?UTF-8?q?=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../addfriend/SearchFriendNicknameFragment.kt | 110 +++++++++++++-- .../addfriend/SearchFriendNumberFragment.kt | 132 ++++++++++++++---- 2 files changed, 199 insertions(+), 43 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNicknameFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNicknameFragment.kt index 9e56eb83e..9571810c8 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNicknameFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNicknameFragment.kt @@ -1,6 +1,7 @@ package com.droidblossom.archive.presentation.ui.mypage.friend.addfriend import android.Manifest +import android.os.Build import android.os.Bundle import android.util.Log import android.view.LayoutInflater @@ -20,6 +21,7 @@ import androidx.navigation.Navigation import com.droidblossom.archive.R import com.droidblossom.archive.databinding.FragmentFriendSearchNicknameBinding import com.droidblossom.archive.presentation.base.BaseFragment +import com.droidblossom.archive.presentation.customview.PermissionDialogButtonClickListener import com.droidblossom.archive.presentation.customview.PermissionDialogFragment import com.droidblossom.archive.presentation.ui.camera.CameraFragment import com.droidblossom.archive.presentation.ui.mypage.friend.addfriend.adapter.AddFriendRVA @@ -40,20 +42,103 @@ class SearchFriendNicknameFragment : } } - private val requestContactsPermissionLauncher = + private val permissionsToRequest: Array by lazy { + val permissionsList = mutableListOf() + permissionsList.add(Manifest.permission.READ_CONTACTS) + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + permissionsList.add(Manifest.permission.READ_PHONE_NUMBERS) + } + + permissionsList.toTypedArray() + } + + private val requestContactsCallPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions -> - if (permissions.all { it.value }) { - navController.navigate(R.id.action_searchFriendNicknameFragment_to_searchFriendNumberFragment) + when { + permissions.all { it.value } -> { + navController.navigate(R.id.action_searchFriendNicknameFragment_to_searchFriendNumberFragment) + } + permissions.none { it.value } -> { + handleAllPermissionsDenied() + } + else -> { + handlePartialPermissionsDenied(permissions) + } + } + } + + private fun handleAllPermissionsDenied() { + if (permissionsToRequest.size > 1) { + if (shouldShowRequestPermissionRationale(Manifest.permission.READ_CONTACTS) || + shouldShowRequestPermissionRationale(Manifest.permission.READ_PHONE_NUMBERS)) { + showToastMessage("앱에서 친구를 찾기 위해 연락처, 전화 접근 권한이 필요합니다.") + } else { + showSettingsDialog(PermissionDialogFragment.PermissionType.CONTACTS_AND_CALL, object : + PermissionDialogButtonClickListener{ + override fun onLeftButtonClicked() { + showToastMessage("앱에서 친구를 찾기 위해 연락처, 전화 접근 권한이 필요합니다.") + } + + override fun onRightButtonClicked() { + navigateToAppSettings{ + requestContactsCallPermissionLauncher.launch(permissionsToRequest) + } + } + + }) + } + } else { + if (shouldShowRequestPermissionRationale(Manifest.permission.READ_CONTACTS)) { + showToastMessage("앱에서 친구를 찾기 위해 연락처 접근 권한이 필요합니다.") } else { - if (shouldShowRequestPermissionRationale(Manifest.permission.READ_CONTACTS) && - shouldShowRequestPermissionRationale(Manifest.permission.READ_PHONE_NUMBERS) - ) { - showToastMessage("앱에서 친구를 찾기 위해 연락처와 전화 접근 권한이 필요합니다. 권한을 허용해 주세요.") - } else { - showSettingsDialog(PermissionDialogFragment.PermissionType.CONTACTS) + showSettingsDialog(PermissionDialogFragment.PermissionType.CONTACTS, object : PermissionDialogButtonClickListener{ + override fun onLeftButtonClicked() { + showToastMessage("앱에서 친구를 찾기 위해 연락처 접근 권한이 필요합니다.") + } + + override fun onRightButtonClicked() { + navigateToAppSettings{ + requestContactsCallPermissionLauncher.launch(permissionsToRequest) + } + } + + }) + } + } + } + + private fun handlePartialPermissionsDenied(permissions: Map) { + permissions.forEach { (perm, granted) -> + if (!granted) { + when (perm) { + Manifest.permission.READ_CONTACTS -> showPermissionDialog( + PermissionDialogFragment.PermissionType.CONTACTS) + Manifest.permission.READ_PHONE_NUMBERS -> showPermissionDialog( + PermissionDialogFragment.PermissionType.CALL) } } } + } + + private fun showPermissionDialog(permissionType: PermissionDialogFragment.PermissionType) { + if (shouldShowRequestPermissionRationale(permissionType.toString())) { + showToastMessage("앱에서 친구를 찾기 위해 ${permissionType.description} 권한이 필요합니다.") + } else { + showSettingsDialog(permissionType, object : PermissionDialogButtonClickListener{ + override fun onLeftButtonClicked() { + showToastMessage("앱에서 친구를 찾기 위해 ${permissionType.description} 권한이 필요합니다.") + } + + override fun onRightButtonClicked() { + navigateToAppSettings{ + requestContactsCallPermissionLauncher.launch(permissionsToRequest) + } + } + + }) + } + } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -90,12 +175,7 @@ class SearchFriendNicknameFragment : } binding.addCV.setOnClickListener { - requestContactsPermissionLauncher.launch( - arrayOf( - Manifest.permission.READ_CONTACTS, - Manifest.permission.READ_PHONE_NUMBERS - ) - ) + requestContactsCallPermissionLauncher.launch(permissionsToRequest) } binding.searchOpenBtnT.setOnClickListener { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNumberFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNumberFragment.kt index b13abb7a0..1265e011f 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNumberFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNumberFragment.kt @@ -4,12 +4,15 @@ import android.Manifest import android.annotation.SuppressLint import android.content.pm.PackageManager import android.graphics.Rect +import android.os.Build import android.os.Bundle +import android.util.Log import android.view.MotionEvent import android.view.View import android.view.ViewGroup import android.view.inputmethod.EditorInfo import android.view.inputmethod.InputMethodManager +import androidx.activity.result.contract.ActivityResultContracts import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import androidx.core.widget.addTextChangedListener @@ -19,8 +22,11 @@ import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.NavController import androidx.navigation.Navigation +import com.droidblossom.archive.R import com.droidblossom.archive.databinding.FragmentFriendSearchNumberBinding import com.droidblossom.archive.presentation.base.BaseFragment +import com.droidblossom.archive.presentation.customview.PermissionDialogButtonClickListener +import com.droidblossom.archive.presentation.customview.PermissionDialogFragment import com.droidblossom.archive.presentation.ui.mypage.friend.addfriend.adapter.AddFriendRVA import com.droidblossom.archive.util.ContactsUtils import dagger.hilt.android.AndroidEntryPoint @@ -31,7 +37,7 @@ import okhttp3.internal.notify @AndroidEntryPoint class SearchFriendNumberFragment : - BaseFragment(com.droidblossom.archive.R.layout.fragment_friend_search_number) { + BaseFragment(R.layout.fragment_friend_search_number) { override val viewModel: AddFriendViewModelImpl by viewModels() @@ -43,14 +49,109 @@ class SearchFriendNumberFragment : } } + + private val permissionsToRequest: Array by lazy { + val permissionsList = mutableListOf() + permissionsList.add(Manifest.permission.READ_CONTACTS) + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + permissionsList.add(Manifest.permission.READ_PHONE_NUMBERS) + } + + permissionsList.toTypedArray() + } + + private val requestContactsCallPermissionLauncher = + registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions -> + when { + permissions.all { it.value } -> { + viewModel.contactsSearch(ContactsUtils.getContacts(requireContext())) + } + permissions.none { it.value } -> { + handleAllPermissionsDenied() + } + else -> { + handlePartialPermissionsDenied(permissions) + } + } + } + + private fun handleAllPermissionsDenied() { + if (permissionsToRequest.size > 1) { + if (shouldShowRequestPermissionRationale(Manifest.permission.READ_CONTACTS) || + shouldShowRequestPermissionRationale(Manifest.permission.READ_PHONE_NUMBERS)) { + showToastMessage("앱에서 친구를 찾기 위해 연락처, 전화 접근 권한이 필요합니다.") + } else { + showSettingsDialog(PermissionDialogFragment.PermissionType.CONTACTS_AND_CALL, object : PermissionDialogButtonClickListener{ + override fun onLeftButtonClicked() { + showToastMessage("앱에서 친구를 찾기 위해 연락처, 전화 접근 권한이 필요합니다.") + requireActivity().finish() + } + + override fun onRightButtonClicked() { + navigateToAppSettings{requestContactsCallPermissionLauncher.launch(permissionsToRequest)} + } + + }) + } + } else { + if (shouldShowRequestPermissionRationale(Manifest.permission.READ_CONTACTS)) { + showToastMessage("앱에서 친구를 찾기 위해 연락처 접근 권한이 필요합니다.") + } else { + showSettingsDialog(PermissionDialogFragment.PermissionType.CONTACTS, object : PermissionDialogButtonClickListener{ + override fun onLeftButtonClicked() { + showToastMessage("앱에서 친구를 찾기 위해 연락처 접근 권한이 필요합니다.") + requireActivity().finish() + } + + override fun onRightButtonClicked() { + navigateToAppSettings{requestContactsCallPermissionLauncher.launch(permissionsToRequest)} + } + + }) + } + } + } + + private fun handlePartialPermissionsDenied(permissions: Map) { + permissions.forEach { (perm, granted) -> + if (!granted) { + when (perm) { + Manifest.permission.READ_CONTACTS -> showPermissionDialog( + PermissionDialogFragment.PermissionType.CONTACTS) + Manifest.permission.READ_PHONE_NUMBERS -> showPermissionDialog( + PermissionDialogFragment.PermissionType.CALL) + } + } + } + } + + private fun showPermissionDialog(permissionType: PermissionDialogFragment.PermissionType) { + if (shouldShowRequestPermissionRationale(permissionType.toString())) { + showToastMessage("앱에서 친구를 찾기 위해 ${permissionType.description} 권한이 필요합니다.") + } else { + showSettingsDialog(permissionType, object : PermissionDialogButtonClickListener{ + override fun onLeftButtonClicked() { + showToastMessage("앱에서 친구를 찾기 위해 ${permissionType.description} 권한이 필요합니다.") + requireActivity().finish() + } + + override fun onRightButtonClicked() { + navigateToAppSettings{requestContactsCallPermissionLauncher.launch(permissionsToRequest)} + } + + }) + } + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding.vm = viewModel navController = Navigation.findNavController(view) initView() - viewModel.contactsSearch(ContactsUtils.getContacts(requireContext())) - //permissionCheck() + + requestContactsCallPermissionLauncher.launch(permissionsToRequest) } @SuppressLint("ClickableViewAccessibility", "NotifyDataSetChanged") @@ -154,29 +255,4 @@ class SearchFriendNumberFragment : } } - private fun permissionCheck() { - val contactStatus = ContextCompat.checkSelfPermission( - requireContext(), - "android.permission.READ_CONTACTS" - ) - val numberStatus = ContextCompat.checkSelfPermission( - requireContext(), - "android.permission.READ_PHONE_NUMBERS" - ) - if (contactStatus == PackageManager.PERMISSION_GRANTED - && numberStatus == PackageManager.PERMISSION_GRANTED - ) { - viewModel.contactsSearch(ContactsUtils.getContacts(requireContext())) - } else { - ActivityCompat.requestPermissions( - requireActivity(), - arrayOf( - "android.permission.READ_PHONE_NUMBERS", - "android.permission.READ_CONTACTS", - ), - 100 - ) - - } - } } \ No newline at end of file From 63dcc645dec32ba8ea96dfee6c91a9f0bb9aeca2 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Sat, 11 May 2024 18:56:01 +0900 Subject: [PATCH 270/301] =?UTF-8?q?feat:=20=EC=B9=B4=EB=A9=94=EB=9D=BC=20?= =?UTF-8?q?=EA=B6=8C=ED=95=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/presentation/ui/MainActivity.kt | 41 ++++++++++++++----- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt index b2125501b..7d54d10db 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt @@ -6,12 +6,14 @@ import android.content.Intent import android.os.Bundle import android.util.Log import androidx.activity.result.contract.ActivityResultContracts +import androidx.core.content.ContextCompat.startActivity import androidx.fragment.app.Fragment import com.droidblossom.archive.R import com.droidblossom.archive.data.dto.member.request.FcmTokenRequsetDto import com.droidblossom.archive.databinding.ActivityMainBinding import com.droidblossom.archive.domain.usecase.member.FcmTokenUseCase import com.droidblossom.archive.presentation.base.BaseActivity +import com.droidblossom.archive.presentation.customview.PermissionDialogButtonClickListener import com.droidblossom.archive.presentation.customview.PermissionDialogFragment import com.droidblossom.archive.presentation.ui.camera.CameraFragment import com.droidblossom.archive.presentation.ui.home.HomeFragment @@ -42,19 +44,35 @@ class MainActivity : BaseActivity(R.layout.activi override val viewModel: Nothing? = null lateinit var viewBinding: ActivityMainBinding - private val requestCameraPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted -> - if (isGranted) { - showFragment(CameraFragment.newIntent(), CameraFragment.TAG) - binding.bottomNavigation.selectedItemId = R.id.menuCamera - } else { - if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) { - showToastMessage("AR 기능을 사용하려면 카메라 권한이 필요합니다.") + private val requestCameraPermissionLauncher = + registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted -> + if (isGranted) { + showFragment(CameraFragment.newIntent(), CameraFragment.TAG) + binding.bottomNavigation.selectedItemId = R.id.menuCamera } else { - showSettingsDialog(PermissionDialogFragment.PermissionType.CAMERA) + handlePermissionsDenied() } } - } + private fun handlePermissionsDenied() { + if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) { + showToastMessage("AR 기능을 사용하려면 카메라 권한이 필요합니다. '권한'에서 카메라 권한을 허용해 주세요.") + } else { + showSettingsDialog(PermissionDialogFragment.PermissionType.CAMERA, object : + PermissionDialogButtonClickListener { + override fun onLeftButtonClicked() { + showToastMessage("AR 기능을 사용하려면 카메라 권한이 필요합니다. '권한'에서 카메라 권한을 허용해 주세요.") + } + + override fun onRightButtonClicked() { + navigateToAppSettings { + requestCameraPermissionLauncher.launch(Manifest.permission.CAMERA) + } + } + + }) + } + } override fun observeData() {} @@ -78,7 +96,7 @@ class MainActivity : BaseActivity(R.layout.activi } } - private fun initFCM(){ + private fun initFCM() { CoroutineScope(Dispatchers.IO).launch { fcmTokenUseCase( FcmTokenRequsetDto(MyFirebaseMessagingService().getFirebaseToken()) @@ -92,7 +110,7 @@ class MainActivity : BaseActivity(R.layout.activi } } - private fun initBottomNav(){ + private fun initBottomNav() { binding.fab.setOnClickListener { requestCameraPermissionLauncher.launch(Manifest.permission.CAMERA) } @@ -164,6 +182,7 @@ class MainActivity : BaseActivity(R.layout.activi } } } + companion object { fun goMain(context: Context) { val intent = Intent(context, MainActivity::class.java) From 975c7b59a9d0b4efe9ca84197d0b0aa77418bf5f Mon Sep 17 00:00:00 2001 From: comst19 Date: Sat, 11 May 2024 19:33:06 +0900 Subject: [PATCH 271/301] =?UTF-8?q?feat:=20CameraFragment=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99=20=EC=A0=84=20=EC=9C=84=EC=B9=98=20=EA=B6=8C=ED=95=9C?= =?UTF-8?q?=EB=8F=84=20=ED=99=95=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../customview/PermissionDialogFragment.kt | 5 ++ .../archive/presentation/ui/MainActivity.kt | 74 +++++++++++++++---- .../src/main/res/drawable/ic_ar_outline.xml | 15 ++++ 3 files changed, 79 insertions(+), 15 deletions(-) create mode 100644 frontend/ARchive/app/src/main/res/drawable/ic_ar_outline.xml diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/PermissionDialogFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/PermissionDialogFragment.kt index 69789b106..b01466834 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/PermissionDialogFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/PermissionDialogFragment.kt @@ -72,6 +72,10 @@ class PermissionDialogFragment( "연락처를 통해 앱 내에서 친구를 찾아보세요. 전화, 연락처 권한을 허용하면 친구 찾기가 가능합니다.", R.drawable.ic_contact_phone_outline ) + PermissionType.AR.name -> Pair( + "AR 기능을 사용하려면 카메라, 위치 권한이 필요합니다. '권한'에서 카메라, 위치 권한을 허용해 주세요.", + R.drawable.ic_ar_outline + ) else -> Pair("", R.drawable.ic_default_outline) } @@ -102,5 +106,6 @@ class PermissionDialogFragment( NOTIFICATIONS("알람"), CALL("전화"), CONTACTS_AND_CALL("연락처, 전화"), + AR("카메라, 위치") } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt index 7d54d10db..4f5695643 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt @@ -44,34 +44,78 @@ class MainActivity : BaseActivity(R.layout.activi override val viewModel: Nothing? = null lateinit var viewBinding: ActivityMainBinding - private val requestCameraPermissionLauncher = - registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted -> - if (isGranted) { + private val arPermissionList = arrayOf( + Manifest.permission.CAMERA, + Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.ACCESS_COARSE_LOCATION + ) + + private val requestPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions -> + when { + permissions.all { it.value } -> { showFragment(CameraFragment.newIntent(), CameraFragment.TAG) binding.bottomNavigation.selectedItemId = R.id.menuCamera - } else { - handlePermissionsDenied() + } + permissions.none { it.value } -> { + handleAllPermissionsDenied() + } + else -> { + handlePartialPermissionsDenied(permissions) } } + } - private fun handlePermissionsDenied() { - if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) { - showToastMessage("AR 기능을 사용하려면 카메라 권한이 필요합니다. '권한'에서 카메라 권한을 허용해 주세요.") - } else { - showSettingsDialog(PermissionDialogFragment.PermissionType.CAMERA, object : - PermissionDialogButtonClickListener { + private fun handleAllPermissionsDenied() { + if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA) || shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_COARSE_LOCATION) || shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)){ + showToastMessage("AR 기능을 사용하려면 카메라, 위치 권한이 필요합니다.") + }else{ + showSettingsDialog(PermissionDialogFragment.PermissionType.AR, object : PermissionDialogButtonClickListener{ override fun onLeftButtonClicked() { - showToastMessage("AR 기능을 사용하려면 카메라 권한이 필요합니다. '권한'에서 카메라 권한을 허용해 주세요.") + showToastMessage("AR 기능을 사용하려면 카메라, 위치 권한이 필요합니다.") } override fun onRightButtonClicked() { - navigateToAppSettings { - requestCameraPermissionLauncher.launch(Manifest.permission.CAMERA) + navigateToAppSettings{requestPermissionLauncher.launch(arPermissionList)} + } + + }) + } + } + + private fun handlePartialPermissionsDenied(permissions: Map) { + permissions.forEach { (permission, granted) -> + if (!granted) { + when (permission) { + Manifest.permission.CAMERA -> showPermissionDialog(PermissionDialogFragment.PermissionType.CAMERA) + Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.ACCESS_COARSE_LOCATION -> { + if (!permissions.getValue(Manifest.permission.ACCESS_FINE_LOCATION) || + !permissions.getValue(Manifest.permission.ACCESS_COARSE_LOCATION)) { + showPermissionDialog(PermissionDialogFragment.PermissionType.LOCATION) + } } } + } + } + } + + private fun showPermissionDialog(permissionType: PermissionDialogFragment.PermissionType) { + + if (shouldShowRequestPermissionRationale(permissionType.toString())) { + showToastMessage("AR 기능을 사용하려면 ${permissionType.description} 권한이 필요합니다.") + } else { + showSettingsDialog(permissionType, object : PermissionDialogButtonClickListener{ + override fun onLeftButtonClicked() { + showToastMessage("AR 기능을 사용하려면 ${permissionType.description} 권한이 필요합니다.") + } + + override fun onRightButtonClicked() { + navigateToAppSettings{requestPermissionLauncher.launch(arPermissionList)} + } }) } + } @@ -112,7 +156,7 @@ class MainActivity : BaseActivity(R.layout.activi private fun initBottomNav() { binding.fab.setOnClickListener { - requestCameraPermissionLauncher.launch(Manifest.permission.CAMERA) + requestPermissionLauncher.launch(arPermissionList) } binding.bottomNavigation.setOnItemSelectedListener { diff --git a/frontend/ARchive/app/src/main/res/drawable/ic_ar_outline.xml b/frontend/ARchive/app/src/main/res/drawable/ic_ar_outline.xml new file mode 100644 index 000000000..868c54f63 --- /dev/null +++ b/frontend/ARchive/app/src/main/res/drawable/ic_ar_outline.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + From 41d9146e95f67bb7b0f3714f90515daa590f9429 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Sat, 11 May 2024 22:53:04 +0900 Subject: [PATCH 272/301] =?UTF-8?q?feat:=20=EA=B7=B8=EB=A3=B9=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20init?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ARchive/app/src/main/AndroidManifest.xml | 5 +- .../archive/presentation/ui/MainActivity.kt | 59 ++++++++++------- .../ui/mypage/friend/FriendActivity.kt | 8 ++- .../addfriend/SearchFriendNicknameFragment.kt | 37 ++++++----- .../addfriend/SearchFriendNumberFragment.kt | 13 ++-- .../friend/addgroup/AddGroupActivity.kt | 41 ++++++++++++ .../friend/addgroup/AddGroupFragment.kt | 28 ++++++++ .../friend/addgroup/AddGroupViewModel.kt | 4 ++ .../friend/addgroup/AddGroupViewModelImpl.kt | 10 +++ .../friend/addgroup/adapter/AddGroupVPA.kt | 19 ++++++ .../main/res/layout/activity_add_group.xml | 66 +++++++++++++++++++ .../main/res/layout/fragment_add_group.xml | 17 +++++ 12 files changed, 256 insertions(+), 51 deletions(-) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addgroup/AddGroupActivity.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addgroup/AddGroupFragment.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addgroup/AddGroupViewModel.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addgroup/AddGroupViewModelImpl.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addgroup/adapter/AddGroupVPA.kt create mode 100644 frontend/ARchive/app/src/main/res/layout/activity_add_group.xml create mode 100644 frontend/ARchive/app/src/main/res/layout/fragment_add_group.xml diff --git a/frontend/ARchive/app/src/main/AndroidManifest.xml b/frontend/ARchive/app/src/main/AndroidManifest.xml index 7e8369bcb..57e081f53 100644 --- a/frontend/ARchive/app/src/main/AndroidManifest.xml +++ b/frontend/ARchive/app/src/main/AndroidManifest.xml @@ -7,7 +7,7 @@ - + @@ -33,6 +33,9 @@ android:theme="@style/Theme.ARchive" android:usesCleartextTraffic="true" tools:targetApi="31"> + diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt index 4f5695643..4336941fb 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt @@ -50,35 +50,43 @@ class MainActivity : BaseActivity(R.layout.activi Manifest.permission.ACCESS_COARSE_LOCATION ) - private val requestPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions -> - when { - permissions.all { it.value } -> { - showFragment(CameraFragment.newIntent(), CameraFragment.TAG) - binding.bottomNavigation.selectedItemId = R.id.menuCamera - } - permissions.none { it.value } -> { - handleAllPermissionsDenied() - } - else -> { - handlePartialPermissionsDenied(permissions) + private val requestPermissionLauncher = + registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions -> + when { + permissions.all { it.value } -> { + showFragment(CameraFragment.newIntent(), CameraFragment.TAG) + binding.bottomNavigation.selectedItemId = R.id.menuCamera + } + + permissions.none { it.value } -> { + handleAllPermissionsDenied() + } + + else -> { + handlePartialPermissionsDenied(permissions) + } } } - } private fun handleAllPermissionsDenied() { - if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA) || shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_COARSE_LOCATION) || shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)){ + if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA) || + shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_COARSE_LOCATION) || + shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION) + ) { showToastMessage("AR 기능을 사용하려면 카메라, 위치 권한이 필요합니다.") - }else{ - showSettingsDialog(PermissionDialogFragment.PermissionType.AR, object : PermissionDialogButtonClickListener{ - override fun onLeftButtonClicked() { - showToastMessage("AR 기능을 사용하려면 카메라, 위치 권한이 필요합니다.") - } + } else { + showSettingsDialog( + PermissionDialogFragment.PermissionType.AR, + object : PermissionDialogButtonClickListener { + override fun onLeftButtonClicked() { + showToastMessage("AR 기능을 사용하려면 카메라, 위치 권한이 필요합니다.") + } - override fun onRightButtonClicked() { - navigateToAppSettings{requestPermissionLauncher.launch(arPermissionList)} - } + override fun onRightButtonClicked() { + navigateToAppSettings { requestPermissionLauncher.launch(arPermissionList) } + } - }) + }) } } @@ -90,7 +98,8 @@ class MainActivity : BaseActivity(R.layout.activi Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION -> { if (!permissions.getValue(Manifest.permission.ACCESS_FINE_LOCATION) || - !permissions.getValue(Manifest.permission.ACCESS_COARSE_LOCATION)) { + !permissions.getValue(Manifest.permission.ACCESS_COARSE_LOCATION) + ) { showPermissionDialog(PermissionDialogFragment.PermissionType.LOCATION) } } @@ -104,13 +113,13 @@ class MainActivity : BaseActivity(R.layout.activi if (shouldShowRequestPermissionRationale(permissionType.toString())) { showToastMessage("AR 기능을 사용하려면 ${permissionType.description} 권한이 필요합니다.") } else { - showSettingsDialog(permissionType, object : PermissionDialogButtonClickListener{ + showSettingsDialog(permissionType, object : PermissionDialogButtonClickListener { override fun onLeftButtonClicked() { showToastMessage("AR 기능을 사용하려면 ${permissionType.description} 권한이 필요합니다.") } override fun onRightButtonClicked() { - navigateToAppSettings{requestPermissionLauncher.launch(arPermissionList)} + navigateToAppSettings { requestPermissionLauncher.launch(arPermissionList) } } }) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendActivity.kt index 55192e4e5..0955a631b 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/FriendActivity.kt @@ -10,6 +10,8 @@ import com.droidblossom.archive.databinding.ActivityFriendBinding import com.droidblossom.archive.presentation.base.BaseActivity import com.droidblossom.archive.presentation.ui.mypage.friend.adapter.FriendVPA import com.droidblossom.archive.presentation.ui.mypage.friend.addfriend.AddFriendActivity +import com.droidblossom.archive.presentation.ui.mypage.friend.addgroup.AddGroupActivity +import com.droidblossom.archive.presentation.ui.mypage.friend.addgroup.AddGroupViewModelImpl import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayout.OnTabSelectedListener import com.google.android.material.tabs.TabLayoutMediator @@ -41,6 +43,10 @@ class FriendActivity : binding.vp.adapter = friendVPA + binding.addCV.setOnClickListener { + startActivity(AddGroupActivity.newIntent(this@FriendActivity)) + } + binding.closeBtn.setOnClickListener { finish() } @@ -58,7 +64,7 @@ class FriendActivity : if (tab.position == 0) { binding.addT.text = "그룹 추가" binding.addCV.setOnClickListener { - + startActivity(AddGroupActivity.newIntent(this@FriendActivity)) } } else { binding.addT.text = "친구 추가" diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNicknameFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNicknameFragment.kt index 9571810c8..7fa38b5d9 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNicknameFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNicknameFragment.kt @@ -71,22 +71,23 @@ class SearchFriendNicknameFragment : private fun handleAllPermissionsDenied() { if (permissionsToRequest.size > 1) { if (shouldShowRequestPermissionRationale(Manifest.permission.READ_CONTACTS) || - shouldShowRequestPermissionRationale(Manifest.permission.READ_PHONE_NUMBERS)) { + shouldShowRequestPermissionRationale(Manifest.permission.READ_PHONE_NUMBERS) + ) { showToastMessage("앱에서 친구를 찾기 위해 연락처, 전화 접근 권한이 필요합니다.") } else { - showSettingsDialog(PermissionDialogFragment.PermissionType.CONTACTS_AND_CALL, object : - PermissionDialogButtonClickListener{ - override fun onLeftButtonClicked() { - showToastMessage("앱에서 친구를 찾기 위해 연락처, 전화 접근 권한이 필요합니다.") - } + showSettingsDialog( + PermissionDialogFragment.PermissionType.CONTACTS_AND_CALL, object : PermissionDialogButtonClickListener { + override fun onLeftButtonClicked() { + showToastMessage("앱에서 친구를 찾기 위해 연락처, 전화 접근 권한이 필요합니다.") + } - override fun onRightButtonClicked() { - navigateToAppSettings{ - requestContactsCallPermissionLauncher.launch(permissionsToRequest) + override fun onRightButtonClicked() { + navigateToAppSettings { + requestContactsCallPermissionLauncher.launch(permissionsToRequest) + } } } - - }) + ) } } else { if (shouldShowRequestPermissionRationale(Manifest.permission.READ_CONTACTS)) { @@ -102,7 +103,6 @@ class SearchFriendNicknameFragment : requestContactsCallPermissionLauncher.launch(permissionsToRequest) } } - }) } } @@ -112,10 +112,11 @@ class SearchFriendNicknameFragment : permissions.forEach { (perm, granted) -> if (!granted) { when (perm) { - Manifest.permission.READ_CONTACTS -> showPermissionDialog( - PermissionDialogFragment.PermissionType.CONTACTS) - Manifest.permission.READ_PHONE_NUMBERS -> showPermissionDialog( - PermissionDialogFragment.PermissionType.CALL) + Manifest.permission.READ_CONTACTS -> + showPermissionDialog(PermissionDialogFragment.PermissionType.CONTACTS) + + Manifest.permission.READ_PHONE_NUMBERS -> + showPermissionDialog(PermissionDialogFragment.PermissionType.CALL) } } } @@ -125,13 +126,13 @@ class SearchFriendNicknameFragment : if (shouldShowRequestPermissionRationale(permissionType.toString())) { showToastMessage("앱에서 친구를 찾기 위해 ${permissionType.description} 권한이 필요합니다.") } else { - showSettingsDialog(permissionType, object : PermissionDialogButtonClickListener{ + showSettingsDialog(permissionType, object : PermissionDialogButtonClickListener { override fun onLeftButtonClicked() { showToastMessage("앱에서 친구를 찾기 위해 ${permissionType.description} 권한이 필요합니다.") } override fun onRightButtonClicked() { - navigateToAppSettings{ + navigateToAppSettings { requestContactsCallPermissionLauncher.launch(permissionsToRequest) } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNumberFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNumberFragment.kt index 1265e011f..a90039278 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNumberFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNumberFragment.kt @@ -79,7 +79,8 @@ class SearchFriendNumberFragment : private fun handleAllPermissionsDenied() { if (permissionsToRequest.size > 1) { if (shouldShowRequestPermissionRationale(Manifest.permission.READ_CONTACTS) || - shouldShowRequestPermissionRationale(Manifest.permission.READ_PHONE_NUMBERS)) { + shouldShowRequestPermissionRationale(Manifest.permission.READ_PHONE_NUMBERS) + ) { showToastMessage("앱에서 친구를 찾기 위해 연락처, 전화 접근 권한이 필요합니다.") } else { showSettingsDialog(PermissionDialogFragment.PermissionType.CONTACTS_AND_CALL, object : PermissionDialogButtonClickListener{ @@ -91,7 +92,6 @@ class SearchFriendNumberFragment : override fun onRightButtonClicked() { navigateToAppSettings{requestContactsCallPermissionLauncher.launch(permissionsToRequest)} } - }) } } else { @@ -117,10 +117,11 @@ class SearchFriendNumberFragment : permissions.forEach { (perm, granted) -> if (!granted) { when (perm) { - Manifest.permission.READ_CONTACTS -> showPermissionDialog( - PermissionDialogFragment.PermissionType.CONTACTS) - Manifest.permission.READ_PHONE_NUMBERS -> showPermissionDialog( - PermissionDialogFragment.PermissionType.CALL) + Manifest.permission.READ_CONTACTS -> + showPermissionDialog( PermissionDialogFragment.PermissionType.CONTACTS ) + + Manifest.permission.READ_PHONE_NUMBERS -> + showPermissionDialog( PermissionDialogFragment.PermissionType.CALL ) } } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addgroup/AddGroupActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addgroup/AddGroupActivity.kt new file mode 100644 index 000000000..2d1f5c533 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addgroup/AddGroupActivity.kt @@ -0,0 +1,41 @@ +package com.droidblossom.archive.presentation.ui.mypage.friend.addgroup + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import androidx.activity.viewModels +import com.droidblossom.archive.R +import com.droidblossom.archive.databinding.ActivityAddGroupBinding +import com.droidblossom.archive.presentation.base.BaseActivity +import com.droidblossom.archive.presentation.ui.mypage.friend.addgroup.adapter.AddGroupVPA + +class AddGroupActivity : + BaseActivity(R.layout.activity_add_group) { + override val viewModel: AddGroupViewModelImpl by viewModels() + + private val addGroupVPA by lazy { + AddGroupVPA(this) + } + + override fun observeData() { + + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding.vm = viewModel + initView() + } + + private fun initView() { + binding.vp.adapter = addGroupVPA + binding.vp. currentItem = 0 + } + + companion object { + const val ADD_GROUP = "add_group" + + fun newIntent(context: Context) = + Intent(context, AddGroupActivity::class.java) + } +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addgroup/AddGroupFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addgroup/AddGroupFragment.kt new file mode 100644 index 000000000..00c3650d1 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addgroup/AddGroupFragment.kt @@ -0,0 +1,28 @@ +package com.droidblossom.archive.presentation.ui.mypage.friend.addgroup + +import android.os.Bundle +import android.view.View +import androidx.fragment.app.activityViewModels +import com.droidblossom.archive.R +import com.droidblossom.archive.databinding.FragmentAddGroupBinding +import com.droidblossom.archive.presentation.base.BaseFragment + +class AddGroupFragment : + BaseFragment(R.layout.fragment_add_group) { + + override val viewModel: AddGroupViewModelImpl by activityViewModels() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.vm = viewModel + initView() + } + + private fun initView() { + + } + + override fun observeData() { + + } +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addgroup/AddGroupViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addgroup/AddGroupViewModel.kt new file mode 100644 index 000000000..c2caaeb44 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addgroup/AddGroupViewModel.kt @@ -0,0 +1,4 @@ +package com.droidblossom.archive.presentation.ui.mypage.friend.addgroup + +interface AddGroupViewModel { +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addgroup/AddGroupViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addgroup/AddGroupViewModelImpl.kt new file mode 100644 index 000000000..a3ece0fc4 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addgroup/AddGroupViewModelImpl.kt @@ -0,0 +1,10 @@ +package com.droidblossom.archive.presentation.ui.mypage.friend.addgroup + +import com.droidblossom.archive.presentation.base.BaseViewModel +import javax.inject.Inject + +class AddGroupViewModelImpl @Inject constructor( + +) : BaseViewModel(), AddGroupViewModel { + +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addgroup/adapter/AddGroupVPA.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addgroup/adapter/AddGroupVPA.kt new file mode 100644 index 000000000..a29a020a3 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addgroup/adapter/AddGroupVPA.kt @@ -0,0 +1,19 @@ +package com.droidblossom.archive.presentation.ui.mypage.friend.addgroup.adapter + +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentActivity +import androidx.viewpager2.adapter.FragmentStateAdapter +import com.droidblossom.archive.presentation.ui.mypage.friend.addgroup.AddGroupFragment + +class AddGroupVPA(activity: FragmentActivity) : FragmentStateAdapter(activity){ + + private val fragmentList = listOf( + AddGroupFragment() + ) + + override fun getItemCount(): Int = fragmentList.size + + override fun createFragment(position: Int): Fragment { + return fragmentList[position] + } +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/activity_add_group.xml b/frontend/ARchive/app/src/main/res/layout/activity_add_group.xml new file mode 100644 index 000000000..723af8ade --- /dev/null +++ b/frontend/ARchive/app/src/main/res/layout/activity_add_group.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_add_group.xml b/frontend/ARchive/app/src/main/res/layout/fragment_add_group.xml new file mode 100644 index 000000000..73f376768 --- /dev/null +++ b/frontend/ARchive/app/src/main/res/layout/fragment_add_group.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + \ No newline at end of file From 389a1d3e2d765e99b7db85b5121114d6ef9cb523 Mon Sep 17 00:00:00 2001 From: comst19 Date: Sat, 11 May 2024 23:08:06 +0900 Subject: [PATCH 273/301] =?UTF-8?q?feat:=20=EC=9C=84=EC=B9=98,=20=EC=B9=B4?= =?UTF-8?q?=EB=A9=94=EB=9D=BC=20=EA=B6=8C=ED=95=9C=20=ED=99=95=EC=9D=B8=20?= =?UTF-8?q?=EB=B0=8F=20=EC=9A=94=EC=B2=AD=20=ED=9B=84=20=EB=B6=84=EA=B8=B0?= =?UTF-8?q?=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../customview/PermissionDialogFragment.kt | 9 +- .../archive/presentation/ui/MainActivity.kt | 142 ++++++++++++---- .../presentation/ui/camera/CameraFragment.kt | 160 +++++++++++++++--- .../res/drawable/ic_essential_outline.xml | 29 ++++ 4 files changed, 289 insertions(+), 51 deletions(-) create mode 100644 frontend/ARchive/app/src/main/res/drawable/ic_essential_outline.xml diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/PermissionDialogFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/PermissionDialogFragment.kt index b01466834..f57060191 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/PermissionDialogFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/PermissionDialogFragment.kt @@ -33,7 +33,7 @@ class PermissionDialogFragment( val permissionType = arguments?.getString("permission") ?: "" setupPermissionInfo(permissionType) - binding.leftBtn.text = if (permissionType == PermissionType.LOCATION.name) "앱 종료" else "취소" + binding.leftBtn.text = if (permissionType == PermissionType.ESSENTIAL.name) "앱 종료" else "취소" binding.leftBtn.setOnClickListener { listener.onLeftButtonClicked() @@ -76,6 +76,10 @@ class PermissionDialogFragment( "AR 기능을 사용하려면 카메라, 위치 권한이 필요합니다. '권한'에서 카메라, 위치 권한을 허용해 주세요.", R.drawable.ic_ar_outline ) + PermissionType.ESSENTIAL.name -> Pair( + "ARchive 앱을 사용하려면 카메라, 위치 권한이 필수입니다. '권한'에서 카메라, 위치 권한을 허용해 주세요.", + R.drawable.ic_essential_outline + ) else -> Pair("", R.drawable.ic_default_outline) } @@ -106,6 +110,7 @@ class PermissionDialogFragment( NOTIFICATIONS("알람"), CALL("전화"), CONTACTS_AND_CALL("연락처, 전화"), - AR("카메라, 위치") + AR("카메라, 위치"), + ESSENTIAL("필수") } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt index 4f5695643..cd0e075de 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt @@ -3,9 +3,11 @@ package com.droidblossom.archive.presentation.ui import android.Manifest import android.content.Context import android.content.Intent +import android.content.pm.PackageManager import android.os.Bundle import android.util.Log import androidx.activity.result.contract.ActivityResultContracts +import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat.startActivity import androidx.fragment.app.Fragment import com.droidblossom.archive.R @@ -44,41 +46,105 @@ class MainActivity : BaseActivity(R.layout.activi override val viewModel: Nothing? = null lateinit var viewBinding: ActivityMainBinding + private var isPermissionRequested = false + private val arPermissionList = arrayOf( Manifest.permission.CAMERA, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION ) - private val requestPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions -> - when { - permissions.all { it.value } -> { - showFragment(CameraFragment.newIntent(), CameraFragment.TAG) - binding.bottomNavigation.selectedItemId = R.id.menuCamera - } - permissions.none { it.value } -> { - handleAllPermissionsDenied() + private val essentialPermissionList = arrayOf( + Manifest.permission.CAMERA, + Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.ACCESS_COARSE_LOCATION + ) + + private val arPermissionLauncher = + registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions -> + when { + permissions.all { it.value } -> { + showFragment(CameraFragment.newIntent(), CameraFragment.TAG) + binding.bottomNavigation.selectedItemId = R.id.menuCamera + } + + permissions.none { it.value } -> { + handleAllPermissionsDenied() + } + + else -> { + handlePartialPermissionsDenied(permissions) + } } - else -> { - handlePartialPermissionsDenied(permissions) + } + + private val essentialPermissionLauncher = + registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions -> + when { + permissions.all { it.value } -> { + + } + + else -> { + handleEssentialPermissionsDenied() + } } + + } + + private fun handleEssentialPermissionsDenied() { + if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA) || shouldShowRequestPermissionRationale( + Manifest.permission.ACCESS_COARSE_LOCATION + ) || shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION) + ) { + showToastMessage("ARchive 앱을 사용하려면 카메라, 위치 권한은 필수입니다.") + } else { + showSettingsDialog( + PermissionDialogFragment.PermissionType.ESSENTIAL, + object : PermissionDialogButtonClickListener { + override fun onLeftButtonClicked() { + showToastMessage("ARchive 앱을 사용하려면 카메라, 위치 권한은 필수입니다.") + finish() + } + + override fun onRightButtonClicked() { + isPermissionRequested = false + navigateToAppSettings { + if (essentialPermissionList.any { + ActivityCompat.checkSelfPermission( + this@MainActivity, + it + ) != PackageManager.PERMISSION_GRANTED + }) { + showToastMessage("ARchive 앱을 사용하려면 카메라, 위치 권한은 필수입니다.") + goMain(this@MainActivity) + } + } + } + + }) } } private fun handleAllPermissionsDenied() { - if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA) || shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_COARSE_LOCATION) || shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)){ + if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA) || shouldShowRequestPermissionRationale( + Manifest.permission.ACCESS_COARSE_LOCATION + ) || shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION) + ) { showToastMessage("AR 기능을 사용하려면 카메라, 위치 권한이 필요합니다.") - }else{ - showSettingsDialog(PermissionDialogFragment.PermissionType.AR, object : PermissionDialogButtonClickListener{ - override fun onLeftButtonClicked() { - showToastMessage("AR 기능을 사용하려면 카메라, 위치 권한이 필요합니다.") - } + } else { + showSettingsDialog( + PermissionDialogFragment.PermissionType.AR, + object : PermissionDialogButtonClickListener { + override fun onLeftButtonClicked() { + showToastMessage("AR 기능을 사용하려면 카메라, 위치 권한이 필요합니다.") + } - override fun onRightButtonClicked() { - navigateToAppSettings{requestPermissionLauncher.launch(arPermissionList)} - } + override fun onRightButtonClicked() { + navigateToAppSettings { arPermissionLauncher.launch(arPermissionList) } + } - }) + }) } } @@ -86,12 +152,13 @@ class MainActivity : BaseActivity(R.layout.activi permissions.forEach { (permission, granted) -> if (!granted) { when (permission) { - Manifest.permission.CAMERA -> showPermissionDialog(PermissionDialogFragment.PermissionType.CAMERA) + Manifest.permission.CAMERA -> showARPermissionDialog(PermissionDialogFragment.PermissionType.CAMERA) Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION -> { if (!permissions.getValue(Manifest.permission.ACCESS_FINE_LOCATION) || - !permissions.getValue(Manifest.permission.ACCESS_COARSE_LOCATION)) { - showPermissionDialog(PermissionDialogFragment.PermissionType.LOCATION) + !permissions.getValue(Manifest.permission.ACCESS_COARSE_LOCATION) + ) { + showARPermissionDialog(PermissionDialogFragment.PermissionType.LOCATION) } } } @@ -99,23 +166,21 @@ class MainActivity : BaseActivity(R.layout.activi } } - private fun showPermissionDialog(permissionType: PermissionDialogFragment.PermissionType) { - + private fun showARPermissionDialog(permissionType: PermissionDialogFragment.PermissionType) { if (shouldShowRequestPermissionRationale(permissionType.toString())) { showToastMessage("AR 기능을 사용하려면 ${permissionType.description} 권한이 필요합니다.") } else { - showSettingsDialog(permissionType, object : PermissionDialogButtonClickListener{ + showSettingsDialog(permissionType, object : PermissionDialogButtonClickListener { override fun onLeftButtonClicked() { showToastMessage("AR 기능을 사용하려면 ${permissionType.description} 권한이 필요합니다.") } override fun onRightButtonClicked() { - navigateToAppSettings{requestPermissionLauncher.launch(arPermissionList)} + navigateToAppSettings { arPermissionLauncher.launch(arPermissionList) } } }) } - } @@ -156,7 +221,7 @@ class MainActivity : BaseActivity(R.layout.activi private fun initBottomNav() { binding.fab.setOnClickListener { - requestPermissionLauncher.launch(arPermissionList) + arPermissionLauncher.launch(arPermissionList) } binding.bottomNavigation.setOnItemSelectedListener { @@ -189,7 +254,7 @@ class MainActivity : BaseActivity(R.layout.activi } - private fun showFragment(fragment: Fragment, tag: String) { + fun showFragment(fragment: Fragment, tag: String) { val findFragment = supportFragmentManager.findFragmentByTag(tag) supportFragmentManager.fragments.forEach { supportFragmentManager.beginTransaction().hide(it).commitAllowingStateLoss() @@ -227,6 +292,23 @@ class MainActivity : BaseActivity(R.layout.activi } } + override fun onResume() { + super.onResume() + if (!isPermissionRequested) { + isPermissionRequested = true + essentialPermissionLauncher.launch(essentialPermissionList) + } else { + if (essentialPermissionList.any { + ActivityCompat.checkSelfPermission( + this, + it + ) != PackageManager.PERMISSION_GRANTED + }) { + isPermissionRequested = false + } + } + } + companion object { fun goMain(context: Context) { val intent = Intent(context, MainActivity::class.java) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt index 83bd2c679..b6ab7be5a 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt @@ -1,10 +1,15 @@ package com.droidblossom.archive.presentation.ui.camera import android.Manifest +import android.content.pm.PackageManager +import android.os.Build import android.os.Bundle import android.util.Log import android.view.View import android.view.ViewGroup +import androidx.activity.result.contract.ActivityResultContracts +import androidx.core.app.ActivityCompat +import androidx.core.content.ContextCompat import androidx.fragment.app.FragmentManager import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle @@ -14,6 +19,9 @@ import com.droidblossom.archive.R import com.droidblossom.archive.databinding.FragmentCameraBinding import com.droidblossom.archive.domain.model.capsule.CapsuleAnchor import com.droidblossom.archive.presentation.base.BaseFragment +import com.droidblossom.archive.presentation.customview.PermissionDialogButtonClickListener +import com.droidblossom.archive.presentation.customview.PermissionDialogFragment +import com.droidblossom.archive.presentation.ui.MainActivity import com.droidblossom.archive.presentation.ui.home.dialog.CapsulePreviewDialogFragment import com.droidblossom.archive.util.FragmentManagerProvider import com.droidblossom.archive.util.LocationUtil @@ -40,6 +48,110 @@ class CameraFragment : private lateinit var config: Config private lateinit var viewAttachmentManager: ViewAttachmentManager private val locationUtil by lazy { LocationUtil(requireContext()) } + + private val arPermissionList = arrayOf( + Manifest.permission.CAMERA, + Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.ACCESS_COARSE_LOCATION + ) + + private val requestPermissionLauncher = + registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions -> + when { + permissions.all { it.value } -> { + locationUtil.getCurrentLocation { latitude, longitude -> + viewModel.getCapsules(latitude = latitude, longitude = longitude) + } + } + + permissions.none { it.value } -> { + handleAllPermissionsDenied() + } + + else -> { + handlePartialPermissionsDenied(permissions) + } + } + } + + private val requestCameraPermissionLauncher = + registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted -> + if (isGranted) { + viewAttachmentManager.onResume() + requestPermissionLauncher.launch(arPermissionList) + } else { + if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) { + showToastMessage("AR 기능을 사용하려면 카메라 권한이 필요합니다.") + } else { + requestPermissionLauncher.launch(arPermissionList) + } + + } + } + + private fun handleAllPermissionsDenied() { + if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA) || shouldShowRequestPermissionRationale( + Manifest.permission.ACCESS_COARSE_LOCATION + ) || shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION) + ) { + showToastMessage("AR 기능을 사용하려면 카메라, 위치 권한이 필요합니다.") + } else { + showSettingsDialog( + PermissionDialogFragment.PermissionType.AR, + object : PermissionDialogButtonClickListener { + override fun onLeftButtonClicked() { + showToastMessage("AR 기능을 사용하려면 카메라, 위치 권한이 필요합니다.") + //requireActivity().finish() + + } + + override fun onRightButtonClicked() { + navigateToAppSettings { requestPermissionLauncher.launch(arPermissionList) } + } + + }) + } + } + + private fun handlePartialPermissionsDenied(permissions: Map) { + permissions.forEach { (permission, granted) -> + if (!granted) { + when (permission) { + Manifest.permission.CAMERA -> showPermissionDialog(PermissionDialogFragment.PermissionType.CAMERA) + Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.ACCESS_COARSE_LOCATION -> { + if (!permissions.getValue(Manifest.permission.ACCESS_FINE_LOCATION) || + !permissions.getValue(Manifest.permission.ACCESS_COARSE_LOCATION) + ) { + showPermissionDialog(PermissionDialogFragment.PermissionType.LOCATION) + } + } + } + } + } + } + + private fun showPermissionDialog(permissionType: PermissionDialogFragment.PermissionType) { + + if (shouldShowRequestPermissionRationale(permissionType.toString())) { + showToastMessage("AR 기능을 사용하려면 ${permissionType.description} 권한이 필요합니다.") + } else { + showSettingsDialog(permissionType, object : PermissionDialogButtonClickListener { + override fun onLeftButtonClicked() { + showToastMessage("AR 기능을 사용하려면 ${permissionType.description} 권한이 필요합니다.") + //requireActivity().finish() + } + + override fun onRightButtonClicked() { + navigateToAppSettings { requestPermissionLauncher.launch(arPermissionList) } + } + + }) + } + + } + + override fun provideFragmentManager(): FragmentManager { return parentFragmentManager } @@ -81,6 +193,7 @@ class CameraFragment : } .collect { dismissLoading() + showToastMessage("${it.size}개의 캡슐을 찾았습니다.") } } } @@ -128,11 +241,6 @@ class CameraFragment : } - val permissionList = arrayOf( - Manifest.permission.ACCESS_FINE_LOCATION, - Manifest.permission.ACCESS_COARSE_LOCATION - ) - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) showLoading(requireContext()) @@ -150,7 +258,15 @@ class CameraFragment : binding.filterAll.layoutParams = layoutParams initView() - createSession() + if (ActivityCompat.checkSelfPermission( + requireContext(), + Manifest.permission.CAMERA + ) == PackageManager.PERMISSION_GRANTED + ) { + createSession() + }else{ + MainActivity.goMain(requireContext()) + } } private fun initView() { @@ -201,26 +317,32 @@ class CameraFragment : override fun onResume() { super.onResume() - viewAttachmentManager.onResume() - locationUtil.getCurrentLocation { latitude, longitude -> - viewModel.getCapsules(latitude = latitude, longitude = longitude) - } + requestCameraPermissionLauncher.launch(Manifest.permission.CAMERA) } override fun onHiddenChanged(hidden: Boolean) { super.onHiddenChanged(hidden) if (hidden) { dismissLoading() - arSceneView.clearChildNodes() - viewModel.clearAnchorNode() - viewAttachmentManager.onPause() - arSceneView.session?.pause() + if (ActivityCompat.checkSelfPermission( + requireContext(), + Manifest.permission.CAMERA + ) == PackageManager.PERMISSION_GRANTED + ){ + arSceneView.clearChildNodes() + viewModel.clearAnchorNode() + viewAttachmentManager.onPause() + arSceneView.session?.pause() + } } else { - showLoading(requireContext()) - arSceneView.session?.resume() - viewAttachmentManager.onResume() - locationUtil.getCurrentLocation { latitude, longitude -> - viewModel.getCapsules(latitude = latitude, longitude = longitude) + if (ActivityCompat.checkSelfPermission( + requireContext(), + Manifest.permission.CAMERA + ) == PackageManager.PERMISSION_GRANTED + ) { + arSceneView.session?.resume() + viewAttachmentManager.onResume() + requestPermissionLauncher.launch(arPermissionList) } } } diff --git a/frontend/ARchive/app/src/main/res/drawable/ic_essential_outline.xml b/frontend/ARchive/app/src/main/res/drawable/ic_essential_outline.xml new file mode 100644 index 000000000..812f72df3 --- /dev/null +++ b/frontend/ARchive/app/src/main/res/drawable/ic_essential_outline.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 77055781f9ee013df4c66e0ba72b0e7bd5682db9 Mon Sep 17 00:00:00 2001 From: comst19 Date: Sat, 11 May 2024 23:41:26 +0900 Subject: [PATCH 274/301] =?UTF-8?q?feat:=20Main=20=EC=A7=84=EC=9E=85=20?= =?UTF-8?q?=EC=A0=84=EC=97=90=20=EC=95=B1=20=ED=95=84=EC=88=98=20=EA=B6=8C?= =?UTF-8?q?=ED=95=9C=20=ED=99=95=EC=9D=B8=20=EB=B0=8F=20=EC=9A=94=EC=B2=AD?= =?UTF-8?q?=20=ED=9B=84=20=EB=B6=84=EA=B8=B0=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/presentation/base/BaseActivity.kt | 7 ++ .../archive/presentation/base/BaseFragment.kt | 6 ++ .../archive/presentation/ui/MainActivity.kt | 6 -- .../presentation/ui/auth/SignInFragment.kt | 42 +++++++++- .../ui/auth/SignUpSuccessFragment.kt | 44 ++++++++++- .../presentation/ui/splash/SplashActivity.kt | 78 +++++++++++++------ 6 files changed, 150 insertions(+), 33 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseActivity.kt index 51523bb53..0a8900855 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseActivity.kt @@ -1,5 +1,6 @@ package com.droidblossom.archive.presentation.base +import android.Manifest import android.app.Activity import android.content.ClipData import android.content.ClipboardManager @@ -44,6 +45,12 @@ abstract class BaseActivity(@LayoutRes v private lateinit var resultLauncher: ActivityResultLauncher private lateinit var onSettingsResult: () -> Unit + + val essentialPermissionList = arrayOf( + Manifest.permission.CAMERA, + Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.ACCESS_COARSE_LOCATION + ) abstract fun observeData() protected fun getStatusBarHeight(): Int { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseFragment.kt index 354745c2f..2317822b6 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseFragment.kt @@ -1,5 +1,6 @@ package com.droidblossom.archive.presentation.base +import android.Manifest import android.app.Activity import android.content.Context import android.content.Intent @@ -36,6 +37,11 @@ abstract class BaseFragment(@LayoutRes va private lateinit var resultLauncher: ActivityResultLauncher + val essentialPermissionList = arrayOf( + Manifest.permission.CAMERA, + Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.ACCESS_COARSE_LOCATION + ) protected fun getStatusBarHeight(): Int { val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android") return if (resourceId > 0) resources.getDimensionPixelSize(resourceId) else 0 diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt index cd0e075de..bc0f35f86 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt @@ -54,12 +54,6 @@ class MainActivity : BaseActivity(R.layout.activi Manifest.permission.ACCESS_COARSE_LOCATION ) - private val essentialPermissionList = arrayOf( - Manifest.permission.CAMERA, - Manifest.permission.ACCESS_FINE_LOCATION, - Manifest.permission.ACCESS_COARSE_LOCATION - ) - private val arPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions -> when { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/auth/SignInFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/auth/SignInFragment.kt index fa363e0eb..3812888ce 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/auth/SignInFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/auth/SignInFragment.kt @@ -1,5 +1,6 @@ package com.droidblossom.archive.presentation.ui.auth +import android.Manifest import android.app.Activity import android.os.Bundle import android.view.View @@ -15,6 +16,8 @@ import com.droidblossom.archive.databinding.FragmentSignInBinding import com.droidblossom.archive.domain.model.auth.SignUp import com.droidblossom.archive.domain.model.member.CheckStatus import com.droidblossom.archive.presentation.base.BaseFragment +import com.droidblossom.archive.presentation.customview.PermissionDialogButtonClickListener +import com.droidblossom.archive.presentation.customview.PermissionDialogFragment import com.droidblossom.archive.presentation.ui.MainActivity import com.droidblossom.archive.util.SocialLoginUtil import com.google.android.gms.auth.api.signin.GoogleSignIn @@ -38,6 +41,43 @@ class SignInFragment : BaseFragment(R.l } } + private val essentialPermissionLauncher = + registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions -> + when { + permissions.all { it.value } -> { + MainActivity.goMain(requireContext()) + } + + else -> { + handleEssentialPermissionsDenied() + } + } + + } + + private fun handleEssentialPermissionsDenied() { + if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA) || shouldShowRequestPermissionRationale( + Manifest.permission.ACCESS_COARSE_LOCATION + ) || shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION) + ) { + showToastMessage("ARchive 앱을 사용하려면 카메라, 위치 권한은 필수입니다.") + } else { + showSettingsDialog( + PermissionDialogFragment.PermissionType.ESSENTIAL, + object : PermissionDialogButtonClickListener { + override fun onLeftButtonClicked() { + showToastMessage("ARchive 앱을 사용하려면 카메라, 위치 권한은 필수입니다.") + requireActivity().finish() + } + + override fun onRightButtonClicked() { + navigateToAppSettings{essentialPermissionLauncher.launch(essentialPermissionList)} + } + + }) + } + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -71,7 +111,7 @@ class SignInFragment : BaseFragment(R.l when (event) { is AuthViewModel.SignInEvent.NavigateToMain -> { - MainActivity.goMain(requireContext()) + essentialPermissionLauncher.launch(essentialPermissionList) } is AuthViewModel.SignInEvent.NavigateToSignUp -> { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/auth/SignUpSuccessFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/auth/SignUpSuccessFragment.kt index 5e9f389df..937cac1d3 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/auth/SignUpSuccessFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/auth/SignUpSuccessFragment.kt @@ -1,13 +1,17 @@ package com.droidblossom.archive.presentation.ui.auth +import android.Manifest import android.os.Bundle import android.os.Handler import android.os.Looper import android.view.View +import androidx.activity.result.contract.ActivityResultContracts import androidx.fragment.app.activityViewModels import com.droidblossom.archive.R import com.droidblossom.archive.databinding.FragmentSignUpSuccessBinding import com.droidblossom.archive.presentation.base.BaseFragment +import com.droidblossom.archive.presentation.customview.PermissionDialogButtonClickListener +import com.droidblossom.archive.presentation.customview.PermissionDialogFragment import com.droidblossom.archive.presentation.ui.MainActivity import dagger.hilt.android.AndroidEntryPoint @@ -20,7 +24,45 @@ class SignUpSuccessFragment : BaseFragment + when { + permissions.all { it.value } -> { + MainActivity.goMain(requireContext()) + } + + else -> { + handleEssentialPermissionsDenied() + } + } + + } + + private fun handleEssentialPermissionsDenied() { + if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA) || shouldShowRequestPermissionRationale( + Manifest.permission.ACCESS_COARSE_LOCATION + ) || shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION) + ) { + showToastMessage("ARchive 앱을 사용하려면 카메라, 위치 권한은 필수입니다.") + } else { + showSettingsDialog( + PermissionDialogFragment.PermissionType.ESSENTIAL, + object : PermissionDialogButtonClickListener { + override fun onLeftButtonClicked() { + showToastMessage("ARchive 앱을 사용하려면 카메라, 위치 권한은 필수입니다.") + requireActivity().finish() + } + + override fun onRightButtonClicked() { + navigateToAppSettings{essentialPermissionLauncher.launch(essentialPermissionList)} + } + + }) + } } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/splash/SplashActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/splash/SplashActivity.kt index f32534774..647ea8343 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/splash/SplashActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/splash/SplashActivity.kt @@ -1,58 +1,86 @@ package com.droidblossom.archive.presentation.ui.splash -import android.content.Intent +import android.Manifest import android.os.Bundle import android.util.Log -import androidx.appcompat.app.AppCompatActivity +import androidx.activity.result.contract.ActivityResultContracts import androidx.lifecycle.lifecycleScope import com.droidblossom.archive.R +import com.droidblossom.archive.databinding.ActivitySplashBinding +import com.droidblossom.archive.presentation.base.BaseActivity +import com.droidblossom.archive.presentation.customview.PermissionDialogButtonClickListener +import com.droidblossom.archive.presentation.customview.PermissionDialogFragment import com.droidblossom.archive.presentation.ui.MainActivity import com.droidblossom.archive.presentation.ui.auth.AuthActivity -import com.droidblossom.archive.presentation.ui.skin.SkinFragment import com.droidblossom.archive.util.DataStoreUtils -import com.droidblossom.archive.util.MyFirebaseMessagingService import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.delay import kotlinx.coroutines.launch import javax.inject.Inject @AndroidEntryPoint -class SplashActivity : AppCompatActivity() { +class SplashActivity : BaseActivity(R.layout.activity_splash) { + @Inject lateinit var dataStoreUtils: DataStoreUtils + override val viewModel: Nothing? = null + override fun observeData() {} + + lateinit var viewBinding: ActivitySplashBinding + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_splash) + + viewBinding = binding // dataStoreUtils 사용 lifecycleScope.launch { delay(1000) if (dataStoreUtils.fetchAccessToken().isNotEmpty() && dataStoreUtils.fetchRefreshToken().isNotEmpty()) { - MainActivity.goMain(this@SplashActivity) + essentialPermissionLauncher.launch(essentialPermissionList) } else { AuthActivity.goAuth(this@SplashActivity) finish() } } -// handleIntent(intent) } -// override fun onNewIntent(intent: Intent?) { -// super.onNewIntent(intent) -// intent?.let { -// setIntent(it) -// handleIntent(it) -// } -// } -// private fun handleIntent(intent: Intent) { -// val destination = intent.getStringExtra("fragmentDestination") -// Log.d("어디로","스플 - 어디로 가야하오 데스티네이션: $destination") -// -// intent.extras?.let { bundle -> -// val nick = bundle.getString("click_action") -// Log.d("어디로","스플 - 어디로 가야하오 엑스트라: $nick") -// } -// -// } + + private val essentialPermissionLauncher = + registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions -> + when { + permissions.all { it.value } -> { + MainActivity.goMain(this@SplashActivity) + } + + else -> { + handleEssentialPermissionsDenied() + } + } + + } + + private fun handleEssentialPermissionsDenied() { + if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA) || shouldShowRequestPermissionRationale( + Manifest.permission.ACCESS_COARSE_LOCATION + ) || shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION) + ) { + showToastMessage("ARchive 앱을 사용하려면 카메라, 위치 권한은 필수입니다.") + } else { + showSettingsDialog( + PermissionDialogFragment.PermissionType.ESSENTIAL, + object : PermissionDialogButtonClickListener { + override fun onLeftButtonClicked() { + showToastMessage("ARchive 앱을 사용하려면 카메라, 위치 권한은 필수입니다.") + finish() + } + + override fun onRightButtonClicked() { + navigateToAppSettings{essentialPermissionLauncher.launch(essentialPermissionList)} + } + + }) + } + } } \ No newline at end of file From 08b6a97c204a206d08af75fc78660c5976701634 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Sun, 12 May 2024 01:58:08 +0900 Subject: [PATCH 275/301] =?UTF-8?q?fix:=20=EC=B9=B4=EB=A9=94=EB=9D=BC=20?= =?UTF-8?q?=EC=9C=84=EC=B9=98=20=EA=B6=8C=ED=95=9C=20=EC=97=86=EC=9C=BC?= =?UTF-8?q?=EB=A9=B4=20=EC=97=90=EB=9F=AC=ED=99=94=EB=A9=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit com.google.ar.core.exceptions.FineLocationPermissionNotGrantedException: The Android permission ACCESS_FINE_LOCATION has not been granted prior to calling Session.configure() with Geospatial mode enabled (Config.GeospatialMode.ENABLED). --- .../archive/presentation/ui/MainActivity.kt | 1 + .../presentation/ui/camera/CameraFragment.kt | 23 +++++++++++-------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt index 4336941fb..d78a19141 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt @@ -236,6 +236,7 @@ class MainActivity : BaseActivity(R.layout.activi } } + companion object { fun goMain(context: Context) { val intent = Intent(context, MainActivity::class.java) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt index b6ab7be5a..a360cffcc 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt @@ -2,14 +2,12 @@ package com.droidblossom.archive.presentation.ui.camera import android.Manifest import android.content.pm.PackageManager -import android.os.Build import android.os.Bundle import android.util.Log import android.view.View import android.view.ViewGroup import androidx.activity.result.contract.ActivityResultContracts import androidx.core.app.ActivityCompat -import androidx.core.content.ContextCompat import androidx.fragment.app.FragmentManager import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle @@ -35,7 +33,6 @@ import io.github.sceneview.ar.ARSceneView import io.github.sceneview.ar.node.AnchorNode import kotlinx.coroutines.flow.filter import kotlinx.coroutines.launch -import java.util.Date @AndroidEntryPoint class CameraFragment : @@ -90,9 +87,9 @@ class CameraFragment : } private fun handleAllPermissionsDenied() { - if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA) || shouldShowRequestPermissionRationale( - Manifest.permission.ACCESS_COARSE_LOCATION - ) || shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION) + if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA) || + shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_COARSE_LOCATION) || + shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION) ) { showToastMessage("AR 기능을 사용하려면 카메라, 위치 권한이 필요합니다.") } else { @@ -257,14 +254,22 @@ class CameraFragment : layoutParams.topMargin += getStatusBarHeight() binding.filterAll.layoutParams = layoutParams - initView() if (ActivityCompat.checkSelfPermission( requireContext(), Manifest.permission.CAMERA + ) == PackageManager.PERMISSION_GRANTED && + ActivityCompat.checkSelfPermission( + requireContext(), + Manifest.permission.ACCESS_FINE_LOCATION + ) == PackageManager.PERMISSION_GRANTED && + ActivityCompat.checkSelfPermission( + requireContext(), + Manifest.permission.ACCESS_COARSE_LOCATION ) == PackageManager.PERMISSION_GRANTED ) { + initView() createSession() - }else{ + } else { MainActivity.goMain(requireContext()) } } @@ -328,7 +333,7 @@ class CameraFragment : requireContext(), Manifest.permission.CAMERA ) == PackageManager.PERMISSION_GRANTED - ){ + ) { arSceneView.clearChildNodes() viewModel.clearAnchorNode() viewAttachmentManager.onPause() From 6d01361060ba78bf7a95c10ca022323345d7ce43 Mon Sep 17 00:00:00 2001 From: comst19 Date: Sun, 12 May 2024 15:50:39 +0900 Subject: [PATCH 276/301] =?UTF-8?q?fix:=20TAG=EB=A5=BC=20=EC=9D=B4?= =?UTF-8?q?=EC=9A=A9=ED=95=98=EC=97=AC=20=EC=A4=91=EB=B3=B5=20PermissionDi?= =?UTF-8?q?alog=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/presentation/base/BaseActivity.kt | 8 ++++++-- .../archive/presentation/base/BaseFragment.kt | 11 +++++++++-- .../customview/PermissionDialogFragment.kt | 2 ++ 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseActivity.kt index 0a8900855..61f4ece25 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseActivity.kt @@ -21,6 +21,7 @@ import androidx.annotation.LayoutRes import androidx.appcompat.app.AppCompatActivity import androidx.databinding.DataBindingUtil import androidx.databinding.ViewDataBinding +import androidx.fragment.app.DialogFragment import com.droidblossom.archive.presentation.customview.HomeSnackBarSmall import com.droidblossom.archive.presentation.customview.LoadingDialog import com.droidblossom.archive.presentation.customview.PermissionDialogButtonClickListener @@ -138,8 +139,11 @@ abstract class BaseActivity(@LayoutRes v } fun showSettingsDialog(permissionType: PermissionDialogFragment.PermissionType, listener: PermissionDialogButtonClickListener) { - val dialog = PermissionDialogFragment.newInstance(permissionType.name, listener) - dialog.show(supportFragmentManager, "PermissionDialog") + val existingDialog = supportFragmentManager.findFragmentByTag(PermissionDialogFragment.TAG) as DialogFragment? + if (existingDialog == null) { + val dialog = PermissionDialogFragment.newInstance(permissionType.name, listener) + dialog.show(supportFragmentManager, PermissionDialogFragment.TAG) + } } fun navigateToAppSettings(onComplete: () -> Unit) { onSettingsResult = onComplete // 저장된 콜백 설정 diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseFragment.kt index 2317822b6..77615399e 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/base/BaseFragment.kt @@ -17,6 +17,7 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.LayoutRes import androidx.databinding.DataBindingUtil import androidx.databinding.ViewDataBinding +import androidx.fragment.app.DialogFragment import androidx.fragment.app.Fragment import com.droidblossom.archive.presentation.customview.LoadingDialog import com.droidblossom.archive.presentation.customview.PermissionDialogButtonClickListener @@ -88,8 +89,14 @@ abstract class BaseFragment(@LayoutRes va } fun showSettingsDialog(permissionType: PermissionDialogFragment.PermissionType, listener: PermissionDialogButtonClickListener) { - val dialog = PermissionDialogFragment.newInstance(permissionType.name, listener) - dialog.show(parentFragmentManager, "PermissionDialog") + + val existingDialog = parentFragmentManager.findFragmentByTag(PermissionDialogFragment.TAG) as DialogFragment? + + if (existingDialog == null) { + val dialog = PermissionDialogFragment.newInstance(permissionType.name, listener) + dialog.show(parentFragmentManager, PermissionDialogFragment.TAG) + } + } fun navigateToAppSettings(onComplete: () -> Unit) { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/PermissionDialogFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/PermissionDialogFragment.kt index f57060191..2a4832589 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/PermissionDialogFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/customview/PermissionDialogFragment.kt @@ -90,6 +90,8 @@ class PermissionDialogFragment( } companion object { + + const val TAG = "PERMISSION_DIALOG" fun newInstance( permission: String, listener: PermissionDialogButtonClickListener From e28990f6feb8c894071abdd88f6e8d55e74c82f9 Mon Sep 17 00:00:00 2001 From: comst19 Date: Sun, 12 May 2024 15:51:44 +0900 Subject: [PATCH 277/301] =?UTF-8?q?feat:=20=EC=82=AC=EC=9A=A9=EC=9E=90?= =?UTF-8?q?=EC=9D=98=20Main=EC=9D=98=20=EB=A7=88=EC=A7=80=EB=A7=89=20Fragm?= =?UTF-8?q?ent=20=EC=A0=80=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/util/DataStoreUtils.kt | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/DataStoreUtils.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/DataStoreUtils.kt index d05e097d3..eea861dd5 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/DataStoreUtils.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/DataStoreUtils.kt @@ -7,6 +7,7 @@ import androidx.datastore.preferences.core.booleanPreferencesKey import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.preferencesDataStore +import com.droidblossom.archive.presentation.ui.MainActivity import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.last @@ -20,6 +21,18 @@ class DataStoreUtils @Inject constructor(private val context: Context) { private val REFRESH_TOKEN_KEY = stringPreferencesKey("RefreshToken") private val FCM_TOKEN_KEY = stringPreferencesKey("FcmToken") private val NOTIFICATIONS_ENABLED_KEY = booleanPreferencesKey("NotificationsEnabled") + private val SELECTED_TAB_KEY = stringPreferencesKey("SelectedTab") + } + + suspend fun saveSelectedTab(tabName: String) { + context.dataStore.edit { preferences -> + preferences[SELECTED_TAB_KEY] = tabName + } + } + + suspend fun fetchSelectedTab(): String { + val preferences = context.dataStore.data.first() + return preferences[SELECTED_TAB_KEY] ?: MainActivity.MainPage.HOME.name } suspend fun saveAccessToken(accessToken: String) { @@ -28,7 +41,7 @@ class DataStoreUtils @Inject constructor(private val context: Context) { preferences[ACCESS_TOKEN_KEY] = encryptedData.toBase64() + ":" + iv.toBase64() } } - + suspend fun fetchAccessToken(): String { val preferences = context.dataStore.data.first() val combined = preferences[ACCESS_TOKEN_KEY] ?: return "" @@ -83,5 +96,8 @@ class DataStoreUtils @Inject constructor(private val context: Context) { } } -private fun ByteArray.toBase64(): String = android.util.Base64.encodeToString(this, android.util.Base64.NO_WRAP) -private fun String.fromBase64(): ByteArray = android.util.Base64.decode(this, android.util.Base64.NO_WRAP) \ No newline at end of file +private fun ByteArray.toBase64(): String = + android.util.Base64.encodeToString(this, android.util.Base64.NO_WRAP) + +private fun String.fromBase64(): ByteArray = + android.util.Base64.decode(this, android.util.Base64.NO_WRAP) \ No newline at end of file From 39bbc5e3f8741ec178b2468fd0f4893e1dd9568e Mon Sep 17 00:00:00 2001 From: comst19 Date: Sun, 12 May 2024 15:52:35 +0900 Subject: [PATCH 278/301] =?UTF-8?q?feat:=20=EB=B0=94=ED=85=80=20=EB=84=A4?= =?UTF-8?q?=EB=B9=84=EA=B2=8C=EC=9D=B4=EC=85=98=20=EC=9D=B4=EB=8F=99=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=20=EA=B4=80=EB=A6=AC(flow)=20-=20=EA=B0=99?= =?UTF-8?q?=EC=9D=80=20=ED=83=AD=20=EC=97=B0=ED=83=80=20=EB=B0=A9=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/presentation/MainViewModelImpl.kt | 50 +++++ .../archive/presentation/ui/MainActivity.kt | 192 ++++++++++++++++-- .../archive/presentation/ui/MainViewModel.kt | 22 ++ .../presentation/ui/mypage/MyPageFragment.kt | 2 + 4 files changed, 250 insertions(+), 16 deletions(-) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/MainViewModelImpl.kt create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainViewModel.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/MainViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/MainViewModelImpl.kt new file mode 100644 index 000000000..cae6b7ecb --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/MainViewModelImpl.kt @@ -0,0 +1,50 @@ +package com.droidblossom.archive.presentation + +import androidx.lifecycle.viewModelScope +import com.droidblossom.archive.presentation.base.BaseViewModel +import com.droidblossom.archive.presentation.ui.MainActivity +import com.droidblossom.archive.presentation.ui.MainViewModel +import com.droidblossom.archive.util.DataStoreUtils +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class MainViewModelImpl @Inject constructor( + private val dataStoreUtils: DataStoreUtils +): BaseViewModel(), MainViewModel { + + + private val _mainEvents = MutableSharedFlow() + override val mainEvents: SharedFlow + get() = _mainEvents.asSharedFlow() + + private val _selectedMainTab = MutableStateFlow(null) + override val selectedMainTab: StateFlow + get() = _selectedMainTab + + init { + viewModelScope.launch { + val selectedTabId = dataStoreUtils.fetchSelectedTab() + val selectedTab = MainActivity.MainPage.values().find { it.name == selectedTabId } + _selectedMainTab.value = selectedTab ?: MainActivity.MainPage.HOME + } + } + + override fun setMainTab(selectedTab : MainActivity.MainPage) { + _selectedMainTab.value = selectedTab + } + + override fun mainEvent(event: MainViewModel.MainEvent) { + viewModelScope.launch { + _mainEvents.emit(event) + } + } + + +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt index d78a19141..fa65edc13 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt @@ -3,18 +3,26 @@ package com.droidblossom.archive.presentation.ui import android.Manifest import android.content.Context import android.content.Intent +import android.content.pm.PackageManager import android.os.Bundle import android.util.Log import androidx.activity.result.contract.ActivityResultContracts +import androidx.activity.viewModels +import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat.startActivity import androidx.fragment.app.Fragment +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import com.droidblossom.archive.R import com.droidblossom.archive.data.dto.member.request.FcmTokenRequsetDto import com.droidblossom.archive.databinding.ActivityMainBinding import com.droidblossom.archive.domain.usecase.member.FcmTokenUseCase +import com.droidblossom.archive.presentation.MainViewModelImpl import com.droidblossom.archive.presentation.base.BaseActivity import com.droidblossom.archive.presentation.customview.PermissionDialogButtonClickListener import com.droidblossom.archive.presentation.customview.PermissionDialogFragment +import com.droidblossom.archive.presentation.ui.auth.AuthViewModelImpl import com.droidblossom.archive.presentation.ui.camera.CameraFragment import com.droidblossom.archive.presentation.ui.home.HomeFragment import com.droidblossom.archive.presentation.ui.mypage.MyPageFragment @@ -29,11 +37,12 @@ import com.droidblossom.archive.util.onSuccess import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.launch import javax.inject.Inject @AndroidEntryPoint -class MainActivity : BaseActivity(R.layout.activity_main) { +class MainActivity : BaseActivity(R.layout.activity_main) { @Inject lateinit var fcmTokenUseCase: FcmTokenUseCase @@ -41,8 +50,10 @@ class MainActivity : BaseActivity(R.layout.activi @Inject lateinit var dataStoreUtils: DataStoreUtils - override val viewModel: Nothing? = null - lateinit var viewBinding: ActivityMainBinding + override val viewModel: MainViewModelImpl by viewModels() + + private var isPermissionRequested = false + private val arPermissionList = arrayOf( Manifest.permission.CAMERA, @@ -50,12 +61,13 @@ class MainActivity : BaseActivity(R.layout.activi Manifest.permission.ACCESS_COARSE_LOCATION ) - private val requestPermissionLauncher = + private val arPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions -> when { permissions.all { it.value } -> { - showFragment(CameraFragment.newIntent(), CameraFragment.TAG) - binding.bottomNavigation.selectedItemId = R.id.menuCamera + viewModel.setMainTab(MainPage.AR) + //showFragment(CameraFragment.newIntent(), CameraFragment.TAG) + //binding.bottomNavigation.selectedItemId = R.id.menuCamera } permissions.none { it.value } -> { @@ -68,6 +80,22 @@ class MainActivity : BaseActivity(R.layout.activi } } + private val essentialPermissionLauncher = + registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions -> + when { + permissions.all { it.value } -> { + + } + + else -> { + handleEssentialPermissionsDenied() + } + } + + } + + + private fun handleAllPermissionsDenied() { if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA) || shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_COARSE_LOCATION) || @@ -83,7 +111,7 @@ class MainActivity : BaseActivity(R.layout.activi } override fun onRightButtonClicked() { - navigateToAppSettings { requestPermissionLauncher.launch(arPermissionList) } + navigateToAppSettings { arPermissionLauncher.launch(arPermissionList) } } }) @@ -108,6 +136,40 @@ class MainActivity : BaseActivity(R.layout.activi } } + private fun handleEssentialPermissionsDenied() { + if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA) || shouldShowRequestPermissionRationale( + Manifest.permission.ACCESS_COARSE_LOCATION + ) || shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION) + ) { + showToastMessage("ARchive 앱을 사용하려면 카메라, 위치 권한은 필수입니다.") + } else { + showSettingsDialog( + PermissionDialogFragment.PermissionType.ESSENTIAL, + object : PermissionDialogButtonClickListener { + override fun onLeftButtonClicked() { + showToastMessage("ARchive 앱을 사용하려면 카메라, 위치 권한은 필수입니다.") + finish() + } + + override fun onRightButtonClicked() { + isPermissionRequested = false + navigateToAppSettings { + if (essentialPermissionList.any { + ActivityCompat.checkSelfPermission( + this@MainActivity, + it + ) != PackageManager.PERMISSION_GRANTED + }) { + showToastMessage("ARchive 앱을 사용하려면 카메라, 위치 권한은 필수입니다.") + goMain(this@MainActivity) + } + } + } + + }) + } + } + private fun showPermissionDialog(permissionType: PermissionDialogFragment.PermissionType) { if (shouldShowRequestPermissionRationale(permissionType.toString())) { @@ -119,7 +181,7 @@ class MainActivity : BaseActivity(R.layout.activi } override fun onRightButtonClicked() { - navigateToAppSettings { requestPermissionLauncher.launch(arPermissionList) } + navigateToAppSettings { arPermissionLauncher.launch(arPermissionList) } } }) @@ -128,13 +190,77 @@ class MainActivity : BaseActivity(R.layout.activi } - override fun observeData() {} + override fun observeData() { + lifecycleScope.launch { + lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.selectedMainTab + .filterNotNull() + .collect { tab -> + dataStoreUtils.saveSelectedTab(tab.name) + when (tab) { + MainPage.HOME -> { + showFragment(HomeFragment.newIntent(), HomeFragment.TAG) + binding.bottomNavigation.selectedItemId = R.id.menuHome + } + MainPage.SKIN -> { + showFragment(SkinFragment.newIntent(), SkinFragment.TAG) + binding.bottomNavigation.selectedItemId = R.id.menuSkin + } + MainPage.AR -> { + showFragment(CameraFragment.newIntent(), CameraFragment.TAG) + binding.bottomNavigation.selectedItemId = R.id.menuCamera + } + MainPage.SOCIAL -> { + showFragment(SocialFragment.newIntent(), SocialFragment.TAG) + binding.bottomNavigation.selectedItemId = R.id.menuSocial + } + MainPage.MY_PAGE -> { + showFragment(MyPageFragment.newIntent(), MyPageFragment.TAG) + binding.bottomNavigation.selectedItemId = R.id.menuMyPage + } + } + } + } + } + + lifecycleScope.launch { + lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.mainEvents.collect { event -> + when (event) { + MainViewModel.MainEvent.NavigateToHome -> { + viewModel.setMainTab(MainPage.HOME) + } + + MainViewModel.MainEvent.NavigateToSkin -> { + viewModel.setMainTab(MainPage.SKIN) + } + + MainViewModel.MainEvent.NavigateToCamera -> { + arPermissionLauncher.launch(arPermissionList) + } + + MainViewModel.MainEvent.NavigateToSocial -> { + viewModel.setMainTab(MainPage.SOCIAL) + } + + MainViewModel.MainEvent.NavigateToMyPage -> { + viewModel.setMainTab(MainPage.MY_PAGE) + } + + is MainViewModel.MainEvent.ShowToastMessage -> { + + } + } + } + } + } + } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - viewBinding = binding - showFragment(HomeFragment.newIntent(), HomeFragment.TAG) + //showFragment(HomeFragment.newIntent(), HomeFragment.TAG) + Log.d("흠", "온 크리에이트 ${viewModel.selectedMainTab.value}") initBottomNav() initFCM() @@ -165,28 +291,37 @@ class MainActivity : BaseActivity(R.layout.activi private fun initBottomNav() { binding.fab.setOnClickListener { - requestPermissionLauncher.launch(arPermissionList) + viewModel.mainEvent(MainViewModel.MainEvent.NavigateToCamera) + + //requestPermissionLauncher.launch(arPermissionList) } binding.bottomNavigation.setOnItemSelectedListener { when (it.itemId) { R.id.menuHome -> { - showFragment(HomeFragment.newIntent(), HomeFragment.TAG) + viewModel.mainEvent(MainViewModel.MainEvent.NavigateToHome) + //showFragment(HomeFragment.newIntent(), HomeFragment.TAG) return@setOnItemSelectedListener true } R.id.menuSkin -> { - showFragment(SkinFragment.newIntent(), SkinFragment.TAG) + viewModel.mainEvent(MainViewModel.MainEvent.NavigateToSkin) + + //showFragment(SkinFragment.newIntent(), SkinFragment.TAG) return@setOnItemSelectedListener true } R.id.menuSocial -> { - showFragment(SocialFragment(), SocialFragment.TAG) + viewModel.mainEvent(MainViewModel.MainEvent.NavigateToSocial) + + //showFragment(SocialFragment(), SocialFragment.TAG) return@setOnItemSelectedListener true } R.id.menuMyPage -> { - showFragment(MyPageFragment.newIntent(), MyPageFragment.TAG) + viewModel.mainEvent(MainViewModel.MainEvent.NavigateToMyPage) + + //showFragment(MyPageFragment.newIntent(), MyPageFragment.TAG) return@setOnItemSelectedListener true } @@ -236,6 +371,23 @@ class MainActivity : BaseActivity(R.layout.activi } } + override fun onResume() { + super.onResume() + if (!isPermissionRequested) { + isPermissionRequested = true + essentialPermissionLauncher.launch(essentialPermissionList) + } else { + if (essentialPermissionList.any { + ActivityCompat.checkSelfPermission( + this, + it + ) != PackageManager.PERMISSION_GRANTED + }) { + isPermissionRequested = false + } + } + } + companion object { fun goMain(context: Context) { @@ -245,4 +397,12 @@ class MainActivity : BaseActivity(R.layout.activi } } + enum class MainPage { + HOME, + SKIN, + AR, + SOCIAL, + MY_PAGE + } + } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainViewModel.kt new file mode 100644 index 000000000..dc860e997 --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainViewModel.kt @@ -0,0 +1,22 @@ +package com.droidblossom.archive.presentation.ui + +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.StateFlow + +interface MainViewModel { + + val mainEvents: SharedFlow + val selectedMainTab: StateFlow + + fun setMainTab(selectedTab : MainActivity.MainPage) + fun mainEvent(event: MainEvent) + + sealed class MainEvent { + object NavigateToCamera : MainEvent() + object NavigateToHome : MainEvent() + object NavigateToMyPage : MainEvent() + object NavigateToSocial : MainEvent() + object NavigateToSkin : MainEvent() + data class ShowToastMessage(val message: String) : MainEvent() + } +} \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt index afe14905c..c5a565307 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt @@ -1,6 +1,7 @@ package com.droidblossom.archive.presentation.ui.mypage import android.os.Bundle +import android.util.Log import android.view.View import android.view.ViewGroup import androidx.fragment.app.viewModels @@ -236,6 +237,7 @@ class MyPageFragment : if (viewModel.reloadMyInfo) { viewModel.getMe() } + viewModel.clearCapsules(false) } override fun onHiddenChanged(hidden: Boolean) { From 428ef6fbd9ab3069da5835295894f8b35d5e071a Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Sun, 12 May 2024 17:07:28 +0900 Subject: [PATCH 279/301] =?UTF-8?q?feat:=20=EB=A9=94=EC=9D=B8=20=EC=97=91?= =?UTF-8?q?=ED=8B=B0=20=EC=B2=98=EC=9D=8C=EC=97=90=20homeFragment=20,=20?= =?UTF-8?q?=EB=B0=94=ED=85=80=20=EB=84=A4=EB=B9=84=20=EC=84=A0=ED=83=9D?= =?UTF-8?q?=EB=90=9C=EA=B3=B3=20=EB=8B=A4=EC=8B=9C=20=EB=88=8C=EB=A6=AC?= =?UTF-8?q?=EA=B2=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/presentation/ui/MainActivity.kt | 47 ++++++++++--------- .../{ => ui}/MainViewModelImpl.kt | 4 +- .../presentation/ui/splash/SplashActivity.kt | 2 + .../util/MyFirebaseMessagingService.kt | 1 + 4 files changed, 30 insertions(+), 24 deletions(-) rename frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/{ => ui}/MainViewModelImpl.kt (90%) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt index fa65edc13..76604dd36 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt @@ -9,7 +9,6 @@ import android.util.Log import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.viewModels import androidx.core.app.ActivityCompat -import androidx.core.content.ContextCompat.startActivity import androidx.fragment.app.Fragment import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope @@ -18,11 +17,9 @@ import com.droidblossom.archive.R import com.droidblossom.archive.data.dto.member.request.FcmTokenRequsetDto import com.droidblossom.archive.databinding.ActivityMainBinding import com.droidblossom.archive.domain.usecase.member.FcmTokenUseCase -import com.droidblossom.archive.presentation.MainViewModelImpl import com.droidblossom.archive.presentation.base.BaseActivity import com.droidblossom.archive.presentation.customview.PermissionDialogButtonClickListener import com.droidblossom.archive.presentation.customview.PermissionDialogFragment -import com.droidblossom.archive.presentation.ui.auth.AuthViewModelImpl import com.droidblossom.archive.presentation.ui.camera.CameraFragment import com.droidblossom.archive.presentation.ui.home.HomeFragment import com.droidblossom.archive.presentation.ui.mypage.MyPageFragment @@ -95,7 +92,6 @@ class MainActivity : BaseActivity(R.layo } - private fun handleAllPermissionsDenied() { if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA) || shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_COARSE_LOCATION) || @@ -137,14 +133,13 @@ class MainActivity : BaseActivity(R.layo } private fun handleEssentialPermissionsDenied() { - if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA) || shouldShowRequestPermissionRationale( - Manifest.permission.ACCESS_COARSE_LOCATION - ) || shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION) + if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA) || + shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_COARSE_LOCATION) || + shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION) ) { showToastMessage("ARchive 앱을 사용하려면 카메라, 위치 권한은 필수입니다.") } else { - showSettingsDialog( - PermissionDialogFragment.PermissionType.ESSENTIAL, + showSettingsDialog(PermissionDialogFragment.PermissionType.ESSENTIAL, object : PermissionDialogButtonClickListener { override fun onLeftButtonClicked() { showToastMessage("ARchive 앱을 사용하려면 카메라, 위치 권한은 필수입니다.") @@ -197,27 +192,36 @@ class MainActivity : BaseActivity(R.layo .filterNotNull() .collect { tab -> dataStoreUtils.saveSelectedTab(tab.name) + Log.d("알림", tab.name) when (tab) { MainPage.HOME -> { showFragment(HomeFragment.newIntent(), HomeFragment.TAG) binding.bottomNavigation.selectedItemId = R.id.menuHome } + MainPage.SKIN -> { showFragment(SkinFragment.newIntent(), SkinFragment.TAG) binding.bottomNavigation.selectedItemId = R.id.menuSkin } + MainPage.AR -> { showFragment(CameraFragment.newIntent(), CameraFragment.TAG) binding.bottomNavigation.selectedItemId = R.id.menuCamera } + MainPage.SOCIAL -> { showFragment(SocialFragment.newIntent(), SocialFragment.TAG) binding.bottomNavigation.selectedItemId = R.id.menuSocial } + MainPage.MY_PAGE -> { showFragment(MyPageFragment.newIntent(), MyPageFragment.TAG) binding.bottomNavigation.selectedItemId = R.id.menuMyPage } + + MainPage.NULL -> { + + } } } } @@ -292,36 +296,29 @@ class MainActivity : BaseActivity(R.layo private fun initBottomNav() { binding.fab.setOnClickListener { viewModel.mainEvent(MainViewModel.MainEvent.NavigateToCamera) - - //requestPermissionLauncher.launch(arPermissionList) } binding.bottomNavigation.setOnItemSelectedListener { + viewModel.setMainTab(MainPage.NULL) + when (it.itemId) { R.id.menuHome -> { viewModel.mainEvent(MainViewModel.MainEvent.NavigateToHome) - //showFragment(HomeFragment.newIntent(), HomeFragment.TAG) return@setOnItemSelectedListener true } R.id.menuSkin -> { viewModel.mainEvent(MainViewModel.MainEvent.NavigateToSkin) - - //showFragment(SkinFragment.newIntent(), SkinFragment.TAG) return@setOnItemSelectedListener true } R.id.menuSocial -> { viewModel.mainEvent(MainViewModel.MainEvent.NavigateToSocial) - - //showFragment(SocialFragment(), SocialFragment.TAG) return@setOnItemSelectedListener true } R.id.menuMyPage -> { viewModel.mainEvent(MainViewModel.MainEvent.NavigateToMyPage) - - //showFragment(MyPageFragment.newIntent(), MyPageFragment.TAG) return@setOnItemSelectedListener true } @@ -353,8 +350,12 @@ class MainActivity : BaseActivity(R.layo Log.d("알림", destination.toString()) when (destination) { + MyFirebaseMessagingService.FragmentDestination.HOME_FRAGMENT.name -> { + viewModel.setMainTab(MainPage.HOME) + } + MyFirebaseMessagingService.FragmentDestination.SKIN_FRAGMENT.name -> { - showFragment(SkinFragment.newIntent(), SkinFragment.TAG) + viewModel.setMainTab(MainPage.HOME) } MyFirebaseMessagingService.FragmentDestination.FRIEND_REQUEST_ACTIVITY.name -> { @@ -392,6 +393,10 @@ class MainActivity : BaseActivity(R.layo companion object { fun goMain(context: Context) { val intent = Intent(context, MainActivity::class.java) + intent.putExtra( + "fragmentDestination", + MyFirebaseMessagingService.FragmentDestination.HOME_FRAGMENT.name + ) intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK context.startActivity(intent) } @@ -402,7 +407,7 @@ class MainActivity : BaseActivity(R.layo SKIN, AR, SOCIAL, - MY_PAGE + MY_PAGE, + NULL, } - } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/MainViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainViewModelImpl.kt similarity index 90% rename from frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/MainViewModelImpl.kt rename to frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainViewModelImpl.kt index cae6b7ecb..2bfa51cee 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/MainViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainViewModelImpl.kt @@ -1,9 +1,7 @@ -package com.droidblossom.archive.presentation +package com.droidblossom.archive.presentation.ui import androidx.lifecycle.viewModelScope import com.droidblossom.archive.presentation.base.BaseViewModel -import com.droidblossom.archive.presentation.ui.MainActivity -import com.droidblossom.archive.presentation.ui.MainViewModel import com.droidblossom.archive.util.DataStoreUtils import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableSharedFlow diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/splash/SplashActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/splash/SplashActivity.kt index 647ea8343..aa68d6963 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/splash/SplashActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/splash/SplashActivity.kt @@ -4,6 +4,7 @@ import android.Manifest import android.os.Bundle import android.util.Log import androidx.activity.result.contract.ActivityResultContracts +import androidx.activity.viewModels import androidx.lifecycle.lifecycleScope import com.droidblossom.archive.R import com.droidblossom.archive.databinding.ActivitySplashBinding @@ -11,6 +12,7 @@ import com.droidblossom.archive.presentation.base.BaseActivity import com.droidblossom.archive.presentation.customview.PermissionDialogButtonClickListener import com.droidblossom.archive.presentation.customview.PermissionDialogFragment import com.droidblossom.archive.presentation.ui.MainActivity +import com.droidblossom.archive.presentation.ui.MainViewModelImpl import com.droidblossom.archive.presentation.ui.auth.AuthActivity import com.droidblossom.archive.util.DataStoreUtils import dagger.hilt.android.AndroidEntryPoint diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/MyFirebaseMessagingService.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/MyFirebaseMessagingService.kt index ccbe088cd..2bfa6240b 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/MyFirebaseMessagingService.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/MyFirebaseMessagingService.kt @@ -177,6 +177,7 @@ class MyFirebaseMessagingService : FirebaseMessagingService() { } enum class FragmentDestination { + HOME_FRAGMENT, SKIN_FRAGMENT, FRIEND_REQUEST_ACTIVITY, FRIEND_ACCEPT_ACTIVITY, From 88c153f605aee18760e26c5da789685ef0778d0a Mon Sep 17 00:00:00 2001 From: comst19 Date: Sun, 12 May 2024 17:37:48 +0900 Subject: [PATCH 280/301] =?UTF-8?q?chore:=20setMainTab=EC=9D=98=20?= =?UTF-8?q?=EC=9D=B8=EC=9E=90=20MainPage.HOME=20->=20MainPage.SKIN?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/droidblossom/archive/presentation/ui/MainActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt index 76604dd36..58cf5b3a4 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/MainActivity.kt @@ -355,7 +355,7 @@ class MainActivity : BaseActivity(R.layo } MyFirebaseMessagingService.FragmentDestination.SKIN_FRAGMENT.name -> { - viewModel.setMainTab(MainPage.HOME) + viewModel.setMainTab(MainPage.SKIN) } MyFirebaseMessagingService.FragmentDestination.FRIEND_REQUEST_ACTIVITY.name -> { From 7804bd21dbe2ce7066553616d35bdcb4f458b245 Mon Sep 17 00:00:00 2001 From: comst19 Date: Sun, 12 May 2024 23:33:37 +0900 Subject: [PATCH 281/301] =?UTF-8?q?chore:=20TextView=EC=9D=98=20Writing,?= =?UTF-8?q?=20Gravity=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/src/main/res/layout/fragment_skin_make_success.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_skin_make_success.xml b/frontend/ARchive/app/src/main/res/layout/fragment_skin_make_success.xml index 0e9757c0d..9f220d2d1 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_skin_make_success.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_skin_make_success.xml @@ -66,8 +66,9 @@ android:layout_marginTop="32dp" android:textAppearance="@style/TextAppearance.App.h4" android:textColor="@color/gray_600" + android:gravity="center" android:visibility="@{vm.addMotion ? View.VISIBLE : View.GONE}" - android:text="모션이 적용되면 알림이 울려요" + android:text="스킨을 생성하는 데 약 50초가 소요됩니다.\n 모션이 적용되면 알림을 보내드릴게요!" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/skinMakeMessage" /> From 05c1e42b438fedc75afa495f5e24309578c91aa7 Mon Sep 17 00:00:00 2001 From: comst19 Date: Sun, 12 May 2024 23:49:26 +0900 Subject: [PATCH 282/301] =?UTF-8?q?feat:=20=EC=8A=A4=ED=82=A8=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EC=8B=9C=20=EC=8B=A4=ED=8C=A8=ED=96=88=EC=9D=84=20?= =?UTF-8?q?=EB=95=8C=20=EB=A1=9C=EB=94=A9=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/home/createcapsule/CreateCapsule3Fragment.kt | 5 +++++ .../ui/home/createcapsule/CreateCapsuleViewModel.kt | 2 ++ .../ui/home/createcapsule/CreateCapsuleViewModelImpl.kt | 2 ++ .../presentation/ui/skin/skinmake/SkinMakeFragment.kt | 3 +++ .../presentation/ui/skin/skinmake/SkinMakeViewModel.kt | 2 ++ .../presentation/ui/skin/skinmake/SkinMakeViewModelImpl.kt | 4 +++- 6 files changed, 17 insertions(+), 1 deletion(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsule3Fragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsule3Fragment.kt index 99dfa91d7..43853c389 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsule3Fragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsule3Fragment.kt @@ -218,8 +218,13 @@ class CreateCapsule3Fragment : showToastMessage(it.message) } + CreateCapsuleViewModel.Create3Event.ClickVideoUpLoad -> {} + CreateCapsuleViewModel.Create3Event.DismissLoading -> { + dismissLoading() + } + else -> {} } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModel.kt index 68cbb414d..3910d5857 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModel.kt @@ -108,6 +108,8 @@ interface CreateCapsuleViewModel { object ClickImgUpLoad : Create3Event() object CLickSingleImgUpLoad : Create3Event() object ClickVideoUpLoad : Create3Event() + + object DismissLoading : Create3Event() data class ShowToastMessage(val message : String) : Create3Event() } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt index f046d84e9..1d98e1e6e 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsuleViewModelImpl.kt @@ -481,6 +481,7 @@ class CreateCapsuleViewModelImpl @Inject constructor( it.preSignedVideoUrls ) }.onFail { + _create3Events.emit(CreateCapsuleViewModel.Create3Event.DismissLoading) Log.d("getUploadUrls", "getUploadUrl 실패") } } @@ -543,6 +544,7 @@ class CreateCapsuleViewModelImpl @Inject constructor( Log.d("uploadFilesToS3", "All files uploaded successfully") createCapsule() } else { + _create3Events.emit(CreateCapsuleViewModel.Create3Event.DismissLoading) Log.e("uploadFilesToS3", "One or more file uploads failed") } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeFragment.kt index 098698830..ef458ea2e 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeFragment.kt @@ -62,6 +62,9 @@ class SkinMakeFragment : BaseFragment { navController.navigate(R.id.action_skinMakeFragment_to_skinMakeSuccessFragment) } + is SkinMakeViewModel.SkinMakeEvent.DismissLoading -> { + dismissLoading() + } else -> {} } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModel.kt index 6fdfecf75..6e8a68ffd 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModel.kt @@ -27,6 +27,8 @@ interface SkinMakeViewModel { sealed class SkinMakeEvent { object SuccessSkinMake : SkinMakeEvent() + + object DismissLoading : SkinMakeEvent() data class ShowToastMessage(val message : String) : SkinMakeEvent() } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModelImpl.kt index d386021cf..03c6c20b9 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModelImpl.kt @@ -160,6 +160,7 @@ class SkinMakeViewModelImpl @Inject constructor( Log.d("getUploadUrls", "$it") uploadFilesToS3(skinImgFile.value!!, it.preSignedImageUrls[0]) }.onFail { + skinMakeEvent(SkinMakeViewModel.SkinMakeEvent.DismissLoading) Log.d("getUploadUrls", "getUploadUrl 실패") } } @@ -185,7 +186,7 @@ class SkinMakeViewModelImpl @Inject constructor( if (uploadSuccess) { submitSkin() } else { - + skinMakeEvent(SkinMakeViewModel.SkinMakeEvent.DismissLoading) } } } @@ -209,6 +210,7 @@ class SkinMakeViewModelImpl @Inject constructor( skinMakeEvent(SkinMakeViewModel.SkinMakeEvent.SuccessSkinMake) Log.d("스킨 생성", "생성 성공") }.onFail { + skinMakeEvent(SkinMakeViewModel.SkinMakeEvent.DismissLoading) Log.d("스킨 생성", "생성 실패") } } From 052e70b757f96548ea8e9e48b027a83ba13f0291 Mon Sep 17 00:00:00 2001 From: comst19 Date: Mon, 13 May 2024 00:07:05 +0900 Subject: [PATCH 283/301] =?UTF-8?q?feat:=20=EC=8A=A4=ED=82=A8=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1,=20=EC=BA=A1=EC=8A=90=20=EC=83=9D=EC=84=B1=20EditText?= =?UTF-8?q?=EC=97=90=20imeOptions=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/src/main/res/layout/fragment_create_capsule3.xml | 5 +++++ .../ARchive/app/src/main/res/layout/fragment_skin_make.xml | 2 ++ 2 files changed, 7 insertions(+) diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_create_capsule3.xml b/frontend/ARchive/app/src/main/res/layout/fragment_create_capsule3.xml index 5f5f71e6c..6ba738660 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_create_capsule3.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_create_capsule3.xml @@ -405,6 +405,10 @@ android:hint="캡슐 이름을 작성하세요." android:paddingHorizontal="24dp" android:paddingVertical="16dp" + android:inputType="text" + android:maxLines="1" + android:imeOptions="actionNext" + android:nextFocusForward="@+id/capsuleContentEditT" android:text="@={vm.capsuleTitle}" android:textAppearance="@style/TextAppearance.App.h4" android:textColorHint="@color/gray_600" @@ -422,6 +426,7 @@ android:background="@drawable/corner_radius_16" android:backgroundTint="@color/white" android:gravity="top" + android:inputType="textMultiLine" android:hint="캡슐 내용을 작성하세요." android:minHeight="180dp" android:padding="24dp" diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_skin_make.xml b/frontend/ARchive/app/src/main/res/layout/fragment_skin_make.xml index cc36d5e9d..bb5d8beb8 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_skin_make.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_skin_make.xml @@ -62,6 +62,8 @@ android:hint="@string/skinMakeHint" android:text="@={vm.skinName}" android:maxLines="1" + android:inputType="text" + android:imeOptions="actionDone" android:textAppearance="@style/TextAppearance.App.h2" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" From ec68a859acf031c6646cfb4a31613bb31b360856 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Mon, 13 May 2024 00:40:57 +0900 Subject: [PATCH 284/301] =?UTF-8?q?feat:=20custome=20=EC=83=9D=EB=AA=85?= =?UTF-8?q?=EC=A3=BC=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/util/CustomLifecycleOwner.kt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/CustomLifecycleOwner.kt diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/CustomLifecycleOwner.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/CustomLifecycleOwner.kt new file mode 100644 index 000000000..67586ed7c --- /dev/null +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/CustomLifecycleOwner.kt @@ -0,0 +1,17 @@ +package com.droidblossom.archive.util + +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.LifecycleRegistry + +class CustomLifecycleOwner : LifecycleOwner { + + private val customLifecycle = LifecycleRegistry(this) + + fun handleLifecycleEvent(event: Lifecycle.Event) { + customLifecycle.handleLifecycleEvent(event) + } + + override val lifecycle: Lifecycle + get() = customLifecycle +} From 25bb3c75c6042890c1faf23d16d239502ad01f7e Mon Sep 17 00:00:00 2001 From: comst19 Date: Mon, 13 May 2024 00:42:11 +0900 Subject: [PATCH 285/301] =?UTF-8?q?chore:=20=ED=81=B4=EB=9F=AC=EC=8A=A4?= =?UTF-8?q?=ED=84=B0=EB=A7=81=20=EB=90=98=EB=8A=94=20=EC=A4=8C=20=EB=A0=88?= =?UTF-8?q?=EB=B2=A8=EC=9D=84=208=20~=2016=EC=9C=BC=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/ARchive/app/build.gradle | 3 --- .../droidblossom/archive/presentation/ui/home/HomeFragment.kt | 4 ++-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/frontend/ARchive/app/build.gradle b/frontend/ARchive/app/build.gradle index 11c77ff95..fb654f877 100644 --- a/frontend/ARchive/app/build.gradle +++ b/frontend/ARchive/app/build.gradle @@ -189,9 +189,6 @@ dependencies { // ConcatAdapter implementation "androidx.recyclerview:recyclerview:1.3.2" - // TedPermission : https://github.com/ParkSangGwon/TedPermission - implementation("io.github.ParkSangGwon:tedpermission-normal:3.3.0") - } kapt { correctErrorTypes true diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt index d91f5cb2a..acf651d14 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt @@ -84,8 +84,8 @@ class HomeFragment : BaseFragment(R.layo } } }) - .minZoom(MINZOOM.toInt() + 2) - .maxZoom(MAXZOOM.toInt() - 2) + .minZoom(FIXZOOM.toInt() - 6) + .maxZoom(FIXZOOM.toInt() + 2) .build() From e4d5f5841b123da34dbc9e431a5d351fbbf04089 Mon Sep 17 00:00:00 2001 From: comst19 Date: Mon, 13 May 2024 01:09:22 +0900 Subject: [PATCH 286/301] =?UTF-8?q?design:=20=EC=BA=A1=EC=8A=90=20?= =?UTF-8?q?=EC=9A=94=EC=95=BD=20=EC=A0=95=EB=B3=B4,=20=EC=97=B4=EB=A6=B0?= =?UTF-8?q?=20=EC=86=8C=EC=85=9C=20=EC=BA=A1=EC=8A=90=20ImageView=20ScaleT?= =?UTF-8?q?ype=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../layout/fragment_capsule_preview_dialog.xml | 16 ++++++++-------- .../main/res/layout/item_social_capsule_open.xml | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_capsule_preview_dialog.xml b/frontend/ARchive/app/src/main/res/layout/fragment_capsule_preview_dialog.xml index 7460b0a13..250be8521 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_capsule_preview_dialog.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_capsule_preview_dialog.xml @@ -171,27 +171,27 @@ android:layout_marginEnd="16dp"/> - + app:cardCornerRadius="75dp"> - + + android:scaleType="centerCrop"/> From e2433c649b4bb3b61464ea2d37c0c853bb445998 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Mon, 13 May 2024 05:03:53 +0900 Subject: [PATCH 287/301] =?UTF-8?q?fix=20:=20=EB=8B=A4=EB=A5=B8=20activity?= =?UTF-8?q?=20=EC=97=90=EC=84=9C=20main=20activity=EB=A1=9C=20=EC=A0=84?= =?UTF-8?q?=ED=99=98=EC=8B=9C=20=EC=B9=B4=EB=A9=94=EB=9D=BC=20=EC=BC=9C?= =?UTF-8?q?=EC=A7=90=20=EC=9D=B4=EC=8A=88=20&=20flow=20=ED=98=B8=EC=B6=9C?= =?UTF-8?q?=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ui/camera/CameraFragment.kt | 88 +++++++++++++------ 1 file changed, 62 insertions(+), 26 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt index a360cffcc..7ec153d8e 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt @@ -11,6 +11,8 @@ import androidx.core.app.ActivityCompat import androidx.fragment.app.FragmentManager import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleEventObserver +import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import com.droidblossom.archive.R @@ -21,6 +23,7 @@ import com.droidblossom.archive.presentation.customview.PermissionDialogButtonCl import com.droidblossom.archive.presentation.customview.PermissionDialogFragment import com.droidblossom.archive.presentation.ui.MainActivity import com.droidblossom.archive.presentation.ui.home.dialog.CapsulePreviewDialogFragment +import com.droidblossom.archive.util.CustomLifecycleOwner import com.droidblossom.archive.util.FragmentManagerProvider import com.droidblossom.archive.util.LocationUtil import com.google.ar.core.Anchor @@ -46,6 +49,10 @@ class CameraFragment : private lateinit var viewAttachmentManager: ViewAttachmentManager private val locationUtil by lazy { LocationUtil(requireContext()) } + private val visibleLifecycleOwner: CustomLifecycleOwner by lazy { + CustomLifecycleOwner() + } + private val arPermissionList = arrayOf( Manifest.permission.CAMERA, Manifest.permission.ACCESS_FINE_LOCATION, @@ -75,6 +82,9 @@ class CameraFragment : registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted -> if (isGranted) { viewAttachmentManager.onResume() + if (this.isHidden) { + onHidden() + } requestPermissionLauncher.launch(arPermissionList) } else { if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) { @@ -154,8 +164,8 @@ class CameraFragment : } override fun observeData() { - viewLifecycleOwner.lifecycleScope.launch { - repeatOnLifecycle(Lifecycle.State.STARTED) { + visibleLifecycleOwner.lifecycleScope.launch { + visibleLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.cameraEvents.collect { event -> when (event) { is CameraViewModel.CameraEvent.ShowCapsulePreviewDialog -> { @@ -182,8 +192,8 @@ class CameraFragment : } } - viewLifecycleOwner.lifecycleScope.launch { - repeatOnLifecycle(Lifecycle.State.STARTED) { + visibleLifecycleOwner.lifecycleScope.launch { + visibleLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.anchorNodes .filter { anchorNodes -> anchorNodes.size == viewModel.capsuleListSize @@ -195,8 +205,8 @@ class CameraFragment : } } - viewLifecycleOwner.lifecycleScope.launch { - repeatOnLifecycle(Lifecycle.State.STARTED) { + visibleLifecycleOwner.lifecycleScope.launch { + visibleLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.capsuleList.collect { capsuleList -> arSceneView.onSessionUpdated = { session, frame -> if (viewModel.capsuleList.value.isNotEmpty() && !viewModel.isCapsulesAdded) { @@ -254,6 +264,8 @@ class CameraFragment : layoutParams.topMargin += getStatusBarHeight() binding.filterAll.layoutParams = layoutParams + initCustomLifeCycle() + if (ActivityCompat.checkSelfPermission( requireContext(), Manifest.permission.CAMERA @@ -320,6 +332,22 @@ class CameraFragment : session.configure(config) } + private fun initCustomLifeCycle() { + viewLifecycleOwner.lifecycle.addObserver(object : LifecycleEventObserver { + override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) { + when (event) { + else -> { + if (isHidden) { + visibleLifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_STOP) + } else { + visibleLifecycleOwner.handleLifecycleEvent(event) + } + } + } + } + }) + } + override fun onResume() { super.onResume() requestCameraPermissionLauncher.launch(Manifest.permission.CAMERA) @@ -328,27 +356,35 @@ class CameraFragment : override fun onHiddenChanged(hidden: Boolean) { super.onHiddenChanged(hidden) if (hidden) { - dismissLoading() - if (ActivityCompat.checkSelfPermission( - requireContext(), - Manifest.permission.CAMERA - ) == PackageManager.PERMISSION_GRANTED - ) { - arSceneView.clearChildNodes() - viewModel.clearAnchorNode() - viewAttachmentManager.onPause() - arSceneView.session?.pause() - } + onHidden() } else { - if (ActivityCompat.checkSelfPermission( - requireContext(), - Manifest.permission.CAMERA - ) == PackageManager.PERMISSION_GRANTED - ) { - arSceneView.session?.resume() - viewAttachmentManager.onResume() - requestPermissionLauncher.launch(arPermissionList) - } + onShow() + } + } + + private fun onHidden() { + dismissLoading() + if (ActivityCompat.checkSelfPermission( + requireContext(), + Manifest.permission.CAMERA + ) == PackageManager.PERMISSION_GRANTED + ) { + arSceneView.clearChildNodes() + viewModel.clearAnchorNode() + viewAttachmentManager.onPause() + arSceneView.session?.pause() + } + } + + private fun onShow() { + if (ActivityCompat.checkSelfPermission( + requireContext(), + Manifest.permission.CAMERA + ) == PackageManager.PERMISSION_GRANTED + ) { + arSceneView.session?.resume() + viewAttachmentManager.onResume() + requestPermissionLauncher.launch(arPermissionList) } } From 10589d5e86d3cefd20bd4a4df0e8084ad5009721 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Mon, 13 May 2024 05:33:17 +0900 Subject: [PATCH 288/301] =?UTF-8?q?fix=20:=20=EC=A7=80=EB=8F=84=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EB=94=94=ED=85=8C=EC=9D=BC=20->=20back=20=EC=8B=9C?= =?UTF-8?q?=20=ED=95=84=ED=84=B0=20=EC=97=90=EB=9F=AC=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ui/home/HomeFragment.kt | 46 +++++++++++-------- .../presentation/ui/home/HomeViewModel.kt | 2 + .../presentation/ui/home/HomeViewModelImpl.kt | 32 +++++++++++++ 3 files changed, 60 insertions(+), 20 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt index acf651d14..77617a00c 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt @@ -279,19 +279,22 @@ class HomeFragment : BaseFragment(R.layo private fun fetchCapsulesNearUser() { locationUtil.getCurrentLocation { latitude, longitude -> val radius = if (clusterer.map != null) getRadiusForCurrentZoom() else DEFATULTRADIUS - viewModel.getNearbyMyCapsules( - latitude, - longitude, - radius, - viewModel.filterCapsuleSelect.value.toString() - ) - if (viewModel.isFriendsCapsuleDisplay.value && (viewModel.filterCapsuleSelect.value == HomeViewModel.CapsuleFilter.ALL - || viewModel.filterCapsuleSelect.value == HomeViewModel.CapsuleFilter.PUBLIC) + if (viewModel.isFriendsCapsuleDisplay.value && + (viewModel.filterCapsuleSelect.value == HomeViewModel.CapsuleFilter.ALL || + viewModel.filterCapsuleSelect.value == HomeViewModel.CapsuleFilter.PUBLIC) ) { - viewModel.getNearbyFriendsCapsules( + viewModel.getNearbyMyAndFriendsCapsules( + latitude, + longitude, + radius, + viewModel.filterCapsuleSelect.value.toString() + ) + } else { + viewModel.getNearbyMyCapsules( latitude, longitude, - getRadiusForCurrentZoom() + radius, + viewModel.filterCapsuleSelect.value.toString() ) } } @@ -300,19 +303,22 @@ class HomeFragment : BaseFragment(R.layo private fun fetchCapsulesInCameraFocus() { clusterer.map?.let { map -> val cameraTarget = map.cameraPosition.target - viewModel.getNearbyMyCapsules( - cameraTarget.latitude, - cameraTarget.longitude, - getRadiusForCurrentZoom(), - viewModel.filterCapsuleSelect.value.toString() - ) - if (viewModel.isFriendsCapsuleDisplay.value && (viewModel.filterCapsuleSelect.value == HomeViewModel.CapsuleFilter.ALL - || viewModel.filterCapsuleSelect.value == HomeViewModel.CapsuleFilter.PUBLIC) + if (viewModel.isFriendsCapsuleDisplay.value && + (viewModel.filterCapsuleSelect.value == HomeViewModel.CapsuleFilter.ALL || + viewModel.filterCapsuleSelect.value == HomeViewModel.CapsuleFilter.PUBLIC) ) { - viewModel.getNearbyFriendsCapsules( + viewModel.getNearbyMyAndFriendsCapsules( + cameraTarget.latitude, + cameraTarget.longitude, + getRadiusForCurrentZoom(), + viewModel.filterCapsuleSelect.value.toString() + ) + } else { + viewModel.getNearbyMyCapsules( cameraTarget.latitude, cameraTarget.longitude, - getRadiusForCurrentZoom() + getRadiusForCurrentZoom(), + viewModel.filterCapsuleSelect.value.toString() ) } } diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeViewModel.kt index 365bb248a..8b43bcaf5 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeViewModel.kt @@ -27,6 +27,8 @@ interface HomeViewModel { fun resetNearbyCapsules() fun getNearbyMyCapsules(latitude: Double, longitude: Double, distance: Double, capsuleType: String) fun getNearbyFriendsCapsules(latitude: Double, longitude: Double, distance: Double) + fun getNearbyMyAndFriendsCapsules(latitude: Double, longitude: Double, distance: Double, capsuleType: String) + fun homeEvent(event:HomeEvent) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeViewModelImpl.kt index 1b9cecd4d..465ef5d2f 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeViewModelImpl.kt @@ -158,4 +158,36 @@ class HomeViewModelImpl @Inject constructor( } } + override fun getNearbyMyAndFriendsCapsules( + latitude: Double, + longitude: Double, + distance: Double, + capsuleType: String + ) { + viewModelScope.launch { + nearbyMyCapsulesHomeUseCase( + latitude, + longitude, + distance, + capsuleType + ).collect { result -> + result.onSuccess { + _capsuleList.emit(it.capsuleMarkers) + nearbyFriendsCapsulesHomeUseCase( + latitude, + longitude, + distance, + ).collect { result -> + result.onSuccess { + _capsuleList.emit(capsuleList.value + it.capsuleMarkers) + }.onFail { + Log.d("티티", "getNearbyCapsules 실패") + } + } + }.onFail { + Log.d("티티", "getNearbyCapsules 실패") + } + } + } + } } \ No newline at end of file From f248989501fbd8cc95bd8ae63b9ca1ad0780026a Mon Sep 17 00:00:00 2001 From: comst19 Date: Mon, 13 May 2024 10:41:23 +0900 Subject: [PATCH 289/301] =?UTF-8?q?feat:=20=EB=AA=A8=EB=93=A0=20Activity?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EC=84=B8=EB=A1=9C=EB=AA=A8=EB=93=9C=20?= =?UTF-8?q?=EA=B3=A0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/ARchive/app/src/main/AndroidManifest.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/frontend/ARchive/app/src/main/AndroidManifest.xml b/frontend/ARchive/app/src/main/AndroidManifest.xml index 57e081f53..ec263a56d 100644 --- a/frontend/ARchive/app/src/main/AndroidManifest.xml +++ b/frontend/ARchive/app/src/main/AndroidManifest.xml @@ -35,24 +35,31 @@ tools:targetApi="31"> Date: Mon, 13 May 2024 10:44:39 +0900 Subject: [PATCH 290/301] =?UTF-8?q?fix:=20show/hide=20lifecycle=EB=A1=9C?= =?UTF-8?q?=20=EC=9D=B8=ED=95=9C=20flow=20=EB=AC=B8=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/presentation/ui/camera/CameraFragment.kt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt index 7ec153d8e..f5d2ff38a 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt @@ -336,13 +336,19 @@ class CameraFragment : viewLifecycleOwner.lifecycle.addObserver(object : LifecycleEventObserver { override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) { when (event) { - else -> { + Lifecycle.Event.ON_START, + Lifecycle.Event.ON_CREATE, + Lifecycle.Event.ON_RESUME, + Lifecycle.Event.ON_PAUSE, -> { if (isHidden) { visibleLifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_STOP) } else { visibleLifecycleOwner.handleLifecycleEvent(event) } } + else -> { + visibleLifecycleOwner.handleLifecycleEvent(event) + } } } }) @@ -377,6 +383,7 @@ class CameraFragment : } private fun onShow() { + visibleLifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_START) if (ActivityCompat.checkSelfPermission( requireContext(), Manifest.permission.CAMERA From ca01c32f35b15261f9f0d55a44dac28f1c6438f8 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Mon, 13 May 2024 15:22:03 +0900 Subject: [PATCH 291/301] =?UTF-8?q?design=20:=20=EC=BA=A1=EC=8A=90=20?= =?UTF-8?q?=EB=AF=B8=EB=A6=AC=EB=B3=B4=EA=B8=B0=20=EC=8A=A4=ED=82=A8=20?= =?UTF-8?q?=EB=B9=84=EC=9C=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/util/CustomLifecycleOwner.kt | 6 +- .../fragment_capsule_preview_dialog.xml | 89 ++++++++++--------- 2 files changed, 49 insertions(+), 46 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/CustomLifecycleOwner.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/CustomLifecycleOwner.kt index 67586ed7c..69cfebd2c 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/CustomLifecycleOwner.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/util/CustomLifecycleOwner.kt @@ -6,12 +6,12 @@ import androidx.lifecycle.LifecycleRegistry class CustomLifecycleOwner : LifecycleOwner { - private val customLifecycle = LifecycleRegistry(this) + private val _customLifecycle = LifecycleRegistry(this) fun handleLifecycleEvent(event: Lifecycle.Event) { - customLifecycle.handleLifecycleEvent(event) + _customLifecycle.handleLifecycleEvent(event) } override val lifecycle: Lifecycle - get() = customLifecycle + get() = _customLifecycle } diff --git a/frontend/ARchive/app/src/main/res/layout/fragment_capsule_preview_dialog.xml b/frontend/ARchive/app/src/main/res/layout/fragment_capsule_preview_dialog.xml index 250be8521..a1aa726aa 100644 --- a/frontend/ARchive/app/src/main/res/layout/fragment_capsule_preview_dialog.xml +++ b/frontend/ARchive/app/src/main/res/layout/fragment_capsule_preview_dialog.xml @@ -1,11 +1,13 @@ + xmlns:bind="http://schemas.android.com/tools" + xmlns:tools="http://schemas.android.com/tools"> - + + + @@ -14,9 +16,9 @@ + android:backgroundTint="@android:color/transparent"> @@ -53,10 +55,10 @@ android:id="@+id/capsuleTitleTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:ellipsize="middle" + android:maxLines="1" android:text="@{vm.capsuleSummaryResponse.title}" android:textAppearance="@style/TextAppearance.App.h2" - android:maxLines="1" - android:ellipsize="middle" android:textColor="@color/white" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@id/capsuleTypeCardView" @@ -66,9 +68,9 @@ android:id="@+id/capsuleTypeCardView" android:layout_width="30dp" android:layout_height="30dp" + android:layout_marginEnd="@dimen/margin" app:cardCornerRadius="15dp" app:cardElevation="0dp" - android:layout_marginEnd="@dimen/margin" app:layout_constraintBottom_toBottomOf="@id/capsuleTitleTextView" app:layout_constraintEnd_toStartOf="@id/capsuleTitleTextView" app:layout_constraintHorizontal_chainStyle="packed" @@ -88,11 +90,11 @@ android:id="@+id/capsuleOpenDateTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:layout_marginTop="12dp" android:drawableStart="@drawable/ic_group_chip" android:drawablePadding="@dimen/padding_small" android:textAppearance="@style/TextAppearance.App.caption4" android:textColor="@color/white" - android:layout_marginTop="12dp" app:layout_constraintEnd_toStartOf="@id/capsuleLocationTextView" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/capsuleTitleTextView" @@ -102,9 +104,9 @@ android:id="@+id/capsuleLocationTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:layout_marginTop="12dp" android:drawableStart="@drawable/ic_group_chip" android:drawablePadding="@dimen/padding_small" - android:layout_marginTop="12dp" android:text="@{vm.capsuleSummaryResponse.roadName.empty ? vm.capsuleSummaryResponse.address : vm.capsuleSummaryResponse.roadName}" android:textAppearance="@style/TextAppearance.App.caption4" android:textColor="@color/white" @@ -140,54 +142,55 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" bind:imageUrl="@{vm.capsuleSummaryResponse.profileUrl}" - bind:placeholder="@{@drawable/app_symbol}"/> + bind:placeholder="@{@drawable/app_symbol}" /> + app:layout_constraintStart_toEndOf="@id/profileImg" + app:layout_constraintTop_toTopOf="@id/profileImg" /> + app:layout_constraintTop_toTopOf="@id/profileImg" + app:srcCompat="@drawable/ic_menu_24" /> + app:strokeWidth="0dp"> @@ -200,17 +203,17 @@ android:layout_height="200dp" android:layout_marginTop="6dp" android:layout_marginBottom="16dp" - android:indeterminate="false" android:elevation="2dp" + android:indeterminate="false" android:max="@{vm.totalTime}" android:progress="@{vm.timeProgress}" - android:visibility="@{vm.visibleTimeProgressBar ? View.VISIBLE : View.GONE}" android:progressDrawable="@drawable/circle_progressbar" + android:visibility="@{vm.visibleTimeProgressBar ? View.VISIBLE : View.GONE}" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintDimensionRatio="1:1" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/profileImg"/> + app:layout_constraintTop_toBottomOf="@id/profileImg" /> + app:layout_constraintTop_toBottomOf="@id/profileImg" /> + app:layout_constraintStart_toStartOf="@id/skinCardView" + app:layout_constraintTop_toTopOf="@id/skinCardView" /> @@ -256,9 +259,9 @@ android:layout_height="110dp" android:layout_marginHorizontal="16dp" android:layout_marginTop="8dp" - android:visibility="gone" android:background="@drawable/rectangle_solid_corner_30dp" android:backgroundTint="#ECEFFE" + android:visibility="gone" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/capsuleSkinLayout"> From 20fee2d112ce902ed04bb669a0274df825c48275 Mon Sep 17 00:00:00 2001 From: comst19 Date: Mon, 13 May 2024 16:24:03 +0900 Subject: [PATCH 292/301] =?UTF-8?q?fix:=20=EC=97=B0=ED=83=80=EC=8B=9C=20Ca?= =?UTF-8?q?psulePreviewDialog=20=EC=A4=91=EB=B3=B5=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ui/camera/ARContentNode.kt | 15 +++++++++++--- .../presentation/ui/camera/CameraFragment.kt | 20 ++++++++++++------- .../presentation/ui/home/HomeFragment.kt | 18 ++++++++++------- .../dialog/CapsulePreviewDialogFragment.kt | 2 ++ .../presentation/ui/mypage/MyPageFragment.kt | 20 ++++++++++++------- 5 files changed, 51 insertions(+), 24 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/ARContentNode.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/ARContentNode.kt index c0065baa2..6272cb360 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/ARContentNode.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/ARContentNode.kt @@ -2,6 +2,7 @@ package com.droidblossom.archive.presentation.ui.camera import android.content.Context import android.view.LayoutInflater +import androidx.fragment.app.DialogFragment import com.bumptech.glide.Glide import com.droidblossom.archive.R import com.droidblossom.archive.databinding.ItemCapsuleSkinBinding @@ -53,9 +54,17 @@ class ARContentNode( isEditable = true } onSingleTapConfirmed = { - val sheet = CapsulePreviewDialogFragment.newInstance("-1",capsule.id.toString(), capsule.capsuleType.toString(), true) - sheet.show(fragmentManagerProvider.provideFragmentManager(), "CapsulePreviewDialog") - false + val existingDialog = fragmentManagerProvider.provideFragmentManager().findFragmentByTag(CapsulePreviewDialogFragment.TAG) as DialogFragment? + if (existingDialog == null) { + val dialog = CapsulePreviewDialogFragment.newInstance( + "-1", + capsule.id.toString(), + capsule.capsuleType.toString(), + true + ) + dialog.show(fragmentManagerProvider.provideFragmentManager(), CapsulePreviewDialogFragment.TAG) + } + true } } onLoaded(viewNode) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt index f5d2ff38a..b5c9cb980 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt @@ -8,6 +8,7 @@ import android.view.View import android.view.ViewGroup import androidx.activity.result.contract.ActivityResultContracts import androidx.core.app.ActivityCompat +import androidx.fragment.app.DialogFragment import androidx.fragment.app.FragmentManager import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle @@ -169,13 +170,18 @@ class CameraFragment : viewModel.cameraEvents.collect { event -> when (event) { is CameraViewModel.CameraEvent.ShowCapsulePreviewDialog -> { - val sheet = CapsulePreviewDialogFragment.newInstance( - "-1", - event.capsuleId, - event.capsuleType, - true - ) - sheet.show(parentFragmentManager, "CapsulePreviewDialog") + + val existingDialog = parentFragmentManager.findFragmentByTag(CapsulePreviewDialogFragment.TAG) as DialogFragment? + if (existingDialog == null) { + val dialog = CapsulePreviewDialogFragment.newInstance( + "-1", + event.capsuleId, + event.capsuleType, + true + ) + dialog.show(parentFragmentManager, CapsulePreviewDialogFragment.TAG) + } + } is CameraViewModel.CameraEvent.ShowLoading -> { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt index 77617a00c..b2a7d0163 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/HomeFragment.kt @@ -5,6 +5,7 @@ import android.os.Bundle import android.view.View import android.view.ViewGroup import androidx.core.content.ContextCompat +import androidx.fragment.app.DialogFragment import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope @@ -151,13 +152,16 @@ class HomeFragment : BaseFragment(R.layo } is HomeViewModel.HomeEvent.ShowCapsulePreviewDialog -> { - val sheet = CapsulePreviewDialogFragment.newInstance( - "-1", - event.capsuleId, - event.capsuleType, - false - ) - sheet.show(parentFragmentManager, "CapsulePreviewDialog") + val existingDialog = parentFragmentManager.findFragmentByTag(CapsulePreviewDialogFragment.TAG) as DialogFragment? + if (existingDialog == null) { + val dialog = CapsulePreviewDialogFragment.newInstance( + "-1", + event.capsuleId, + event.capsuleType, + false + ) + dialog.show(parentFragmentManager, CapsulePreviewDialogFragment.TAG) + } } else -> {} diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogFragment.kt index a643b4c7b..088bca0a0 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/dialog/CapsulePreviewDialogFragment.kt @@ -267,6 +267,8 @@ class CapsulePreviewDialogFragment : } companion object { + + const val TAG = "CapsulePreView" fun newInstance( capsuleIndex: String, capsuleId: String, diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt index c5a565307..c0a4db58c 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt @@ -4,6 +4,7 @@ import android.os.Bundle import android.util.Log import android.view.View import android.view.ViewGroup +import androidx.fragment.app.DialogFragment import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope @@ -66,13 +67,18 @@ class MyPageFragment : ) }, { capsuleIndex, id, type -> - val sheet = CapsulePreviewDialogFragment.newInstance( - capsuleIndex.toString(), - id.toString(), - type.toString(), - false - ) - sheet.show(parentFragmentManager, "CapsulePreviewDialog") + + val existingDialog = parentFragmentManager.findFragmentByTag(CapsulePreviewDialogFragment.TAG) as DialogFragment? + if (existingDialog == null) { + val dialog = CapsulePreviewDialogFragment.newInstance( + capsuleIndex.toString(), + id.toString(), + type.toString(), + false + ) + dialog.show(parentFragmentManager, CapsulePreviewDialogFragment.TAG) + } + } ) } From 13f66944cb8e272d775e65003b365f215c47e387 Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Mon, 13 May 2024 16:45:45 +0900 Subject: [PATCH 293/301] =?UTF-8?q?fix:=20api=202=EB=B2=88=20=ED=86=B5?= =?UTF-8?q?=EC=8B=A0=ED=95=98=EB=8D=98=20=EB=B2=84=EA=B7=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/presentation/ui/camera/CameraFragment.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt index b5c9cb980..18c33987a 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/camera/CameraFragment.kt @@ -397,7 +397,7 @@ class CameraFragment : ) { arSceneView.session?.resume() viewAttachmentManager.onResume() - requestPermissionLauncher.launch(arPermissionList) + //requestPermissionLauncher.launch(arPermissionList) } } From d6f99365e2e0de660b6bece852d3408e197716fe Mon Sep 17 00:00:00 2001 From: hangunhee39 Date: Mon, 13 May 2024 17:09:57 +0900 Subject: [PATCH 294/301] =?UTF-8?q?refact:=20rv=20=EC=84=B1=EB=8A=A5?= =?UTF-8?q?=ED=96=A5=EC=83=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ui/home/createcapsule/CreateCapsule2Fragment.kt | 1 + .../presentation/ui/home/notification/NotificationActivity.kt | 1 + .../presentation/ui/mypage/setting/page/SettingAgreeFragment.kt | 1 + .../presentation/ui/mypage/setting/page/SettingNoticeFragment.kt | 1 + .../droidblossom/archive/presentation/ui/skin/SkinFragment.kt | 1 + 5 files changed, 5 insertions(+) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsule2Fragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsule2Fragment.kt index 44f5fcc40..e671f5c95 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsule2Fragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/createcapsule/CreateCapsule2Fragment.kt @@ -125,6 +125,7 @@ class CreateCapsule2Fragment : private fun initRVA() { binding.recycleView.adapter = skinRVA + binding.recycleView.setHasFixedSize(true) binding.recycleView.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { super.onScrollStateChanged(recyclerView, newState) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationActivity.kt index 03b2c68d2..aa5a0f0bd 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationActivity.kt @@ -71,6 +71,7 @@ class NotificationActivity : finish() } binding.rv.adapter = notificationRVA + binding.rv.setHasFixedSize(true) binding.rv.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { super.onScrollStateChanged(recyclerView, newState) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingAgreeFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingAgreeFragment.kt index eaaa0181c..239c080b0 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingAgreeFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingAgreeFragment.kt @@ -33,6 +33,7 @@ class SettingAgreeFragment : private fun initRVA() { binding.adapter.adapter = agreeAdapter + binding.adapter.setHasFixedSize(true) agreeAdapter.submitList( listOf( Agree("이용약관 1장", getString(R.string.agree_content)), diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingNoticeFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingNoticeFragment.kt index 97a5ddeae..3609cda20 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingNoticeFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/setting/page/SettingNoticeFragment.kt @@ -34,6 +34,7 @@ class SettingNoticeFragment : private fun initView() { binding.adapter.adapter = noticeAdapter + binding.adapter.setHasFixedSize(true) noticeAdapter.submitList( listOf( Notice( diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinFragment.kt index 31c23fcf0..d5993031d 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/SkinFragment.kt @@ -55,6 +55,7 @@ class SkinFragment : BaseFragment(R.layo private fun initRVA() { binding.skinRV.adapter = mySkinRVA + binding.skinRV.setHasFixedSize(true) binding.skinRV.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { super.onScrollStateChanged(recyclerView, newState) From 47e64ac3924fb722b12b54797540e72acf17b894 Mon Sep 17 00:00:00 2001 From: comst19 Date: Mon, 13 May 2024 19:21:03 +0900 Subject: [PATCH 295/301] =?UTF-8?q?chore:=20=EC=8A=A4=ED=82=A8=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=ED=94=84=EB=9E=98=EA=B7=B8=EB=A8=BC=ED=8A=B8?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EB=AA=A8=EC=85=98=20=EC=A0=81=EC=9A=A9?= =?UTF-8?q?=EB=90=9C=20=EC=8A=A4=ED=82=A8=EC=9C=BC=EB=A1=9C=20URL=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/skin/skinmake/SkinMakeViewModelImpl.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModelImpl.kt index 03c6c20b9..d319f6b4e 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/skin/skinmake/SkinMakeViewModelImpl.kt @@ -60,42 +60,42 @@ class SkinMakeViewModelImpl @Inject constructor( listOf( SkinMotion( 1L, - "https://cdn.pixabay.com/animation/2022/10/11/09/05/09-05-26-529_512.gif", + "https://github.com/comst19/GradProjectHub/blob/main/db7457be-b654-48ac-8b20-b47c3644fcab_1.gif?raw=true", Motion.JUMPING_JACKS, Retarget.CMU, false ), SkinMotion( 2L, - "https://cdn.pixabay.com/animation/2022/10/11/09/05/09-05-26-529_512.gif", + "https://github.com/comst19/GradProjectHub/blob/main/7a87f9b0-19dd-4ae8-b88e-3e1e142c5122_2.gif?raw=true", Motion.DAB, Retarget.FAIR, false ), SkinMotion( 3L, - "https://cdn.pixabay.com/animation/2022/10/11/09/05/09-05-26-529_512.gif", + "https://github.com/comst19/GradProjectHub/blob/main/45696698-7cad-4838-a947-2b07cb5b2c05_3.gif?raw=true", Motion.JUMPING, Retarget.FAIR, false ), SkinMotion( 4L, - "https://cdn.pixabay.com/animation/2022/10/11/09/05/09-05-26-529_512.gif", + "https://github.com/comst19/GradProjectHub/blob/main/b8112cd6-48fb-42de-b326-80efa4d20150_4.gif?raw=true", Motion.WAVE_HELLO, Retarget.FAIR, false ), SkinMotion( 5L, - "https://cdn.pixabay.com/animation/2022/10/11/09/05/09-05-26-529_512.gif", + "https://github.com/comst19/GradProjectHub/blob/main/02a9fb2b-f126-461c-886a-5f232e93c12b_5.gif?raw=true", Motion.ZOMBIE, Retarget.FAIR, false ), SkinMotion( 6L, - "https://cdn.pixabay.com/animation/2022/10/11/09/05/09-05-26-529_512.gif", + "https://github.com/comst19/GradProjectHub/blob/main/fd145d45-ba3b-41b3-9e58-6f089a4267a0_6.gif?raw=true", Motion.JESSE_DANCE, Retarget.ROKOKO, false From d4c36492b3da4f4a0021d3491eaea25cf56b1e51 Mon Sep 17 00:00:00 2001 From: comst19 Date: Mon, 13 May 2024 19:43:17 +0900 Subject: [PATCH 296/301] =?UTF-8?q?refact:=20rv=20=EC=84=B1=EB=8A=A5?= =?UTF-8?q?=ED=96=A5=EC=83=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/mypage/friend/addfriend/SearchFriendNicknameFragment.kt | 1 + .../ui/mypage/friend/addfriend/SearchFriendNumberFragment.kt | 1 + .../presentation/ui/mypage/friend/page/FriendListFragment.kt | 2 +- .../ui/mypage/friendaccept/page/FriendAcceptFragment.kt | 2 +- 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNicknameFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNicknameFragment.kt index 7fa38b5d9..07af7cabb 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNicknameFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNicknameFragment.kt @@ -157,6 +157,7 @@ class SearchFriendNicknameFragment : binding.closeBtn.layoutParams = layoutParams binding.recycleView.adapter = addFriendRVA + binding.recycleView.setHasFixedSize(true) binding.closeBtn.setOnClickListener { (activity as AddFriendActivity).finish() diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNumberFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNumberFragment.kt index a90039278..20112b44b 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNumberFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/addfriend/SearchFriendNumberFragment.kt @@ -163,6 +163,7 @@ class SearchFriendNumberFragment : binding.closeBtn.layoutParams = layoutParams binding.recycleView.adapter = addFriendRVA + binding.recycleView.setHasFixedSize(true) binding.closeBtn.setOnClickListener { navController.popBackStack() diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/FriendListFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/FriendListFragment.kt index b099feaec..0c8c5a4df 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/FriendListFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friend/page/FriendListFragment.kt @@ -39,7 +39,7 @@ class FriendListFragment : private fun initView() { binding.friendRV.adapter = friendRVA - + binding.friendRV.setHasFixedSize(true) binding.friendRV.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { super.onScrollStateChanged(recyclerView, newState) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/page/FriendAcceptFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/page/FriendAcceptFragment.kt index c9e9eacac..81bc2849a 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/page/FriendAcceptFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/friendaccept/page/FriendAcceptFragment.kt @@ -41,7 +41,7 @@ class FriendAcceptFragment : private fun initRV() { binding.rv.adapter = friendAcceptRVA - + binding.rv.setHasFixedSize(true) binding.rv.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { super.onScrollStateChanged(recyclerView, newState) From a5f2b14a12fe3fde30e8a3101aae5cd6c14a425b Mon Sep 17 00:00:00 2001 From: comst19 Date: Tue, 14 May 2024 01:14:40 +0900 Subject: [PATCH 297/301] =?UTF-8?q?fix:=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?-=20=EC=8A=A4=ED=81=AC=EB=A1=A4=20=EC=98=A4=EB=A5=98,=20?= =?UTF-8?q?=EC=8A=A4=ED=94=BC=EB=84=88=20=EA=B0=92=20=EA=B0=B1=EC=8B=A0=20?= =?UTF-8?q?=EC=98=A4=EB=A5=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ui/mypage/MyPageFragment.kt | 122 +++++++++++++----- .../presentation/ui/mypage/MyPageViewModel.kt | 2 +- .../ui/mypage/MyPageViewModelImpl.kt | 10 +- .../ui/mypage/adapter/SpinnerAdapter.kt | 27 ++-- 4 files changed, 107 insertions(+), 54 deletions(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt index c0a4db58c..3e60b5602 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt @@ -1,12 +1,17 @@ package com.droidblossom.archive.presentation.ui.mypage +import android.Manifest +import android.content.pm.PackageManager import android.os.Bundle import android.util.Log import android.view.View import android.view.ViewGroup +import androidx.core.app.ActivityCompat import androidx.fragment.app.DialogFragment import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleEventObserver +import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import androidx.recyclerview.widget.ConcatAdapter @@ -24,15 +29,20 @@ import com.droidblossom.archive.presentation.ui.mypage.adapter.SpinnerAdapter import com.droidblossom.archive.presentation.ui.mypage.friend.FriendActivity import com.droidblossom.archive.presentation.ui.mypage.friendaccept.FriendAcceptActivity import com.droidblossom.archive.presentation.ui.mypage.setting.SettingActivity +import com.droidblossom.archive.util.CustomLifecycleOwner import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch @AndroidEntryPoint -class MyPageFragment : - BaseFragment(R.layout.fragment_my_page) { +class MyPageFragment : BaseFragment(R.layout.fragment_my_page) { + override val viewModel: MyPageViewModelImpl by viewModels() + private val visibleLifecycleOwner: CustomLifecycleOwner by lazy { + CustomLifecycleOwner() + } + private val profileRVA: ProfileRVA by lazy { ProfileRVA( { @@ -42,7 +52,6 @@ class MyPageFragment : startActivity(FriendActivity.newIntent(requireContext(), FriendActivity.FRIEND)) }, { - viewModel.reloadMyInfo = true startActivity( FriendAcceptActivity.newIntent( requireContext(), @@ -67,8 +76,8 @@ class MyPageFragment : ) }, { capsuleIndex, id, type -> - - val existingDialog = parentFragmentManager.findFragmentByTag(CapsulePreviewDialogFragment.TAG) as DialogFragment? + val existingDialog = + parentFragmentManager.findFragmentByTag(CapsulePreviewDialogFragment.TAG) as DialogFragment? if (existingDialog == null) { val dialog = CapsulePreviewDialogFragment.newInstance( capsuleIndex.toString(), @@ -92,6 +101,7 @@ class MyPageFragment : requireContext(), capsuleTypes ) { capsuleTypes -> + Log.d("라이프", "스피너 어댑터 안 ${viewModel.capsuleType.value} vs $capsuleTypes") viewModel.selectSpinnerItem(capsuleTypes) } @@ -106,8 +116,6 @@ class MyPageFragment : } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -123,9 +131,10 @@ class MyPageFragment : capsuleRVA.notifyItemChanged(capsuleIndex) } } - + initCustomLifeCycle() initMyPageRVA() - val layoutParams = binding.myPageSwipeRefreshLayout.layoutParams as ViewGroup.MarginLayoutParams + val layoutParams = + binding.myPageSwipeRefreshLayout.layoutParams as ViewGroup.MarginLayoutParams layoutParams.topMargin += getStatusBarHeight() binding.myPageSwipeRefreshLayout.layoutParams = layoutParams } @@ -175,22 +184,22 @@ class MyPageFragment : } override fun observeData() { - viewLifecycleOwner.lifecycleScope.launch { - repeatOnLifecycle(Lifecycle.State.STARTED) { + visibleLifecycleOwner.lifecycleScope.launch { + visibleLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.myCapsules.collect { capsule -> - if (viewModel.clearCapsule){ + if (viewModel.clearCapsule) { viewModel.clearCapsule = false - }else{ + } else { viewModel.updateMyCapsulesUI() } } } } - viewLifecycleOwner.lifecycleScope.launch { - repeatOnLifecycle(Lifecycle.State.STARTED) { + visibleLifecycleOwner.lifecycleScope.launch { + visibleLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.myCapsulesUI.collect { capsule -> - capsuleRVA.submitList(capsule){ - if (binding.myPageSwipeRefreshLayout.isRefreshing){ + capsuleRVA.submitList(capsule) { + if (binding.myPageSwipeRefreshLayout.isRefreshing) { binding.myPageSwipeRefreshLayout.isRefreshing = false binding.myPageRV.scrollToPosition(0) } @@ -199,8 +208,8 @@ class MyPageFragment : } } - viewLifecycleOwner.lifecycleScope.launch { - repeatOnLifecycle(Lifecycle.State.STARTED) { + visibleLifecycleOwner.lifecycleScope.launch { + visibleLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.myPageEvents.collect { event -> when (event) { is MyPageViewModel.MyPageEvent.ShowToastMessage -> { @@ -221,38 +230,79 @@ class MyPageFragment : } } - viewLifecycleOwner.lifecycleScope.launch { - repeatOnLifecycle(Lifecycle.State.STARTED) { + visibleLifecycleOwner.lifecycleScope.launch { + visibleLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.myInfo.collect { memberDetail -> profileRVA.submitList(listOf(memberDetail.toUIModel())) } } } - viewLifecycleOwner.lifecycleScope.launch { - repeatOnLifecycle(Lifecycle.State.STARTED) { + visibleLifecycleOwner.lifecycleScope.launch { + visibleLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.capsuleType.collect { - viewModel.clearCapsules(false) + if (viewModel.viewModelReload){ + viewModel.clearCapsules(false) + } + } } } } - override fun onResume() { - super.onResume() - if (viewModel.reloadMyInfo) { - viewModel.getMe() - } - viewModel.clearCapsules(false) + private fun initCustomLifeCycle() { + viewLifecycleOwner.lifecycle.addObserver(object : LifecycleEventObserver { + override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) { + when (event) { + Lifecycle.Event.ON_START, + Lifecycle.Event.ON_CREATE, + Lifecycle.Event.ON_RESUME, + Lifecycle.Event.ON_PAUSE, -> { + if (isHidden) { + visibleLifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_STOP) + } else { + visibleLifecycleOwner.handleLifecycleEvent(event) + } + } + else -> { + visibleLifecycleOwner.handleLifecycleEvent(event) + } + } + } + }) } override fun onHiddenChanged(hidden: Boolean) { super.onHiddenChanged(hidden) - if (!hidden) { + if (hidden) { + onHidden() + } else { + onShow() + } + } + + private fun onHidden() { + Log.d("라이프","히든 히든 - const : $reload, viewmodel : ${viewModel.viewModelReload}") + viewModel.viewModelReload = false + reloadState = true + } + + private fun onShow() { + visibleLifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_START) + if (reload){ viewModel.load() + reload = false + viewModel.viewModelReload = false + binding.myPageRV.scrollToPosition(0) } } + override fun onResume() { + super.onResume() + reloadState = false + reload = false + } + companion object { const val TAG = "MY" @@ -260,7 +310,15 @@ class MyPageFragment : const val STORY_TYPE = 2 const val SPINNER_TYPE = 3 const val CAPSULE_TYPE = 4 - fun newIntent() = MyPageFragment() + + private var reloadState = true + private var reload = false + fun newIntent() : MyPageFragment{ + if (reloadState){ + reload = true + } + return MyPageFragment() + } } enum class SpinnerCapsuleType(val description: String) { diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModel.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModel.kt index bd53dcbf7..c09355fae 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModel.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModel.kt @@ -14,7 +14,7 @@ interface MyPageViewModel { val hasNextPage : StateFlow val lastCreatedTime : StateFlow val capsuleType: StateFlow - var reloadMyInfo:Boolean + var viewModelReload:Boolean var clearCapsule:Boolean fun getMe() diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt index d2d5cbfc3..b18de5f0e 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageViewModelImpl.kt @@ -66,11 +66,11 @@ class MyPageViewModelImpl @Inject constructor( private var getCapsuleListJob: Job? = null - override var reloadMyInfo = false + override var viewModelReload = false override var clearCapsule = false init { - getMe() + load() viewModelScope.launch { scrollEventFlow.collect { getCapsulePage() @@ -100,7 +100,6 @@ class MyPageViewModelImpl @Inject constructor( memberUseCase().collect { result -> result.onSuccess { _myInfo.emit(it) - reloadMyInfo = false }.onFail { myPageEvent(MyPageViewModel.MyPageEvent.ShowToastMessage("정보 불러오기 실패")) } @@ -216,6 +215,9 @@ class MyPageViewModelImpl @Inject constructor( } override fun selectSpinnerItem(item: MyPageFragment.SpinnerCapsuleType) { - _capsuleType.value = item + if (capsuleType.value != item){ + viewModelReload = true + _capsuleType.value = item + } } } \ No newline at end of file diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/SpinnerAdapter.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/SpinnerAdapter.kt index 875fa1c76..90361eeb0 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/SpinnerAdapter.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/SpinnerAdapter.kt @@ -16,13 +16,16 @@ class SpinnerAdapter( private val selectedCapsuleType: (MyPageFragment.SpinnerCapsuleType) -> Unit ) : RecyclerView.Adapter() { + private var selectedPosition = 0 + inner class ItemViewHolder( private val binding: ItemMyPageSpinnerBinding ) : RecyclerView.ViewHolder(binding.root) { - fun bind(spinnerItems: Array) { + fun bind(spinnerItems: Array, position: Int) { val spinnerAdapter = CapsuleTypeSpinner(context, spinnerItems) binding.capsuleTypeSpinner.adapter = spinnerAdapter + binding.capsuleTypeSpinner.setSelection(selectedPosition, false) binding.capsuleTypeSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { override fun onItemSelected( @@ -32,16 +35,11 @@ class SpinnerAdapter( id: Long ) { selectedCapsuleType(spinnerItems[position]) + selectedPosition = position } - override fun onNothingSelected(parent: AdapterView<*>?) { - - } + override fun onNothingSelected(parent: AdapterView<*>?) {} } - binding.capsuleTypeSpinner.viewTreeObserver.addOnWindowFocusChangeListener { hasFocus -> - spinnerAdapter.spinnerIsOpened = hasFocus - spinnerAdapter.notifyDataSetChanged() - } } } @@ -49,14 +47,9 @@ class SpinnerAdapter( setHasStableIds(true) } - override fun getItemId(position: Int): Long { - return 1L - } - + override fun getItemId(position: Int): Long = position.toLong() - override fun getItemViewType(position: Int): Int { - return MyPageFragment.SPINNER_TYPE - } + override fun getItemViewType(position: Int): Int = MyPageFragment.SPINNER_TYPE override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder { return ItemViewHolder( @@ -69,8 +62,8 @@ class SpinnerAdapter( } override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { - holder.bind(spinnerItems) + holder.bind(spinnerItems, position) } override fun getItemCount(): Int = 1 -} +} \ No newline at end of file From 0e30e3d24adc55fcaa4bf70cb1b1d17ecedd0c8d Mon Sep 17 00:00:00 2001 From: comst19 Date: Tue, 14 May 2024 02:53:23 +0900 Subject: [PATCH 298/301] =?UTF-8?q?fix:=20my=20->=20=EB=8B=A4=EB=A5=B8=20f?= =?UTF-8?q?rag=20->=20Activity=20->=20=EB=92=A4=EB=A1=9C=EA=B0=80=EA=B8=B0?= =?UTF-8?q?=20->=20my=20=EC=8B=9C=EC=97=90=20reload?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/presentation/ui/mypage/MyPageFragment.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt index 3e60b5602..1e170b5ea 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt @@ -282,7 +282,6 @@ class MyPageFragment : BaseFragment( } private fun onHidden() { - Log.d("라이프","히든 히든 - const : $reload, viewmodel : ${viewModel.viewModelReload}") viewModel.viewModelReload = false reloadState = true } @@ -295,6 +294,12 @@ class MyPageFragment : BaseFragment( viewModel.viewModelReload = false binding.myPageRV.scrollToPosition(0) } + if (!reload && !reloadState){ + viewModel.load() + reload = false + viewModel.viewModelReload = false + binding.myPageRV.scrollToPosition(0) + } } override fun onResume() { From b6d355737e8682d1ef7344cef7ce660eaa5ca5bc Mon Sep 17 00:00:00 2001 From: comst19 Date: Tue, 14 May 2024 02:59:00 +0900 Subject: [PATCH 299/301] =?UTF-8?q?feat:=20=EC=8A=A4=ED=94=BC=EB=84=88=20?= =?UTF-8?q?=EB=8B=AB=ED=9E=90=20=EB=96=84=20UI=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ui/mypage/adapter/SpinnerAdapter.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/SpinnerAdapter.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/SpinnerAdapter.kt index 90361eeb0..cff5b2c7d 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/SpinnerAdapter.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/adapter/SpinnerAdapter.kt @@ -38,8 +38,14 @@ class SpinnerAdapter( selectedPosition = position } - override fun onNothingSelected(parent: AdapterView<*>?) {} + override fun onNothingSelected(parent: AdapterView<*>?) { + + } } + binding.capsuleTypeSpinner.viewTreeObserver.addOnWindowFocusChangeListener { hasFocus -> + spinnerAdapter.spinnerIsOpened = hasFocus + spinnerAdapter.notifyDataSetChanged() + } } } From 44cb9143bf80b55f0715c1f60bfed3a1d8f783fb Mon Sep 17 00:00:00 2001 From: comst19 Date: Tue, 14 May 2024 03:46:45 +0900 Subject: [PATCH 300/301] =?UTF-8?q?feat:=20MyPage=20->=20=EA=B7=B8?= =?UTF-8?q?=EB=A3=B9,=20=EC=B9=9C=EA=B5=AC,=20=EC=B9=9C=EA=B5=AC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EC=95=A1=ED=8B=B0=EB=B9=84=ED=8B=B0=20->?= =?UTF-8?q?=20MyPage=20=EC=9D=BC=20=EB=95=8C=20=EB=82=B4=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EA=B0=B1=EC=8B=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../archive/presentation/ui/mypage/MyPageFragment.kt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt index 1e170b5ea..c2a9ce5c4 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/mypage/MyPageFragment.kt @@ -38,6 +38,7 @@ import kotlinx.coroutines.launch class MyPageFragment : BaseFragment(R.layout.fragment_my_page) { override val viewModel: MyPageViewModelImpl by viewModels() + private var reloadMyInfo = false private val visibleLifecycleOwner: CustomLifecycleOwner by lazy { CustomLifecycleOwner() @@ -47,9 +48,11 @@ class MyPageFragment : BaseFragment( ProfileRVA( { startActivity(FriendActivity.newIntent(requireContext(), FriendActivity.GROUP)) + reloadMyInfo = true }, { startActivity(FriendActivity.newIntent(requireContext(), FriendActivity.FRIEND)) + reloadMyInfo = true }, { startActivity( @@ -58,6 +61,7 @@ class MyPageFragment : BaseFragment( FriendAcceptActivity.FRIEND ) ) + reloadMyInfo = true }, { startActivity(SettingActivity.newIntent(requireContext())) @@ -101,7 +105,6 @@ class MyPageFragment : BaseFragment( requireContext(), capsuleTypes ) { capsuleTypes -> - Log.d("라이프", "스피너 어댑터 안 ${viewModel.capsuleType.value} vs $capsuleTypes") viewModel.selectSpinnerItem(capsuleTypes) } @@ -300,6 +303,10 @@ class MyPageFragment : BaseFragment( viewModel.viewModelReload = false binding.myPageRV.scrollToPosition(0) } + + if (reloadMyInfo){ + viewModel.getMe() + } } override fun onResume() { From 42588f55e5f643a28dea6dd482e528d5bc01a8a8 Mon Sep 17 00:00:00 2001 From: comst19 Date: Tue, 14 May 2024 12:39:34 +0900 Subject: [PATCH 301/301] =?UTF-8?q?feat:=20NotificationActivity=20?= =?UTF-8?q?=EC=A7=84=EC=9E=85=20=EC=8B=9C=20=EC=95=8C=EB=9E=8C=20=EA=B6=8C?= =?UTF-8?q?=ED=95=9C=20=EC=97=AC=EB=B6=80=EC=97=90=20=EB=94=B0=EB=A5=B8=20?= =?UTF-8?q?=EB=B6=84=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/home/notification/NotificationActivity.kt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationActivity.kt b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationActivity.kt index aa5a0f0bd..66635f239 100644 --- a/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationActivity.kt +++ b/frontend/ARchive/app/src/main/java/com/droidblossom/archive/presentation/ui/home/notification/NotificationActivity.kt @@ -1,9 +1,12 @@ package com.droidblossom.archive.presentation.ui.home.notification +import android.Manifest import android.content.Context import android.content.Intent +import android.os.Build import android.os.Bundle import android.view.ViewGroup +import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.viewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope @@ -55,9 +58,21 @@ class NotificationActivity : } } + private val requestNotificationLauncher = + registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted -> + if (isGranted) { + + } else { + showToastMessage("ARchive 앱의 알림을 받기 위해서는 알림 권한이 필요합니다. 알림을 통해 중요한 정보와 업데이트를 놓치지 마세요.") + } + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding.vm = viewModel + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + requestNotificationLauncher.launch(Manifest.permission.POST_NOTIFICATIONS) + } initView() viewModel.getNotificationPage() }