From 86ce5b69dbd98102a4d0533af8c79972cfcc527f Mon Sep 17 00:00:00 2001 From: soopeach Date: Fri, 15 Mar 2024 17:33:43 +0900 Subject: [PATCH 1/3] =?UTF-8?q?[feat/upgrade=5Fweb=5Fbridge]:=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=EB=90=9C=20=EB=B8=8C=EB=A6=BF=EC=A7=80=20=EC=8A=A4?= =?UTF-8?q?=ED=8C=A9=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EB=AA=A8=EB=8D=B8=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EB=B0=8F=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gdsc/presentation/view/WebAppInterface.kt | 83 ++++++++++++++++--- .../view/group/MyGroupFragment.kt | 20 ++++- 2 files changed, 89 insertions(+), 14 deletions(-) diff --git a/presentation/src/main/java/org/gdsc/presentation/view/WebAppInterface.kt b/presentation/src/main/java/org/gdsc/presentation/view/WebAppInterface.kt index 1225491e..1ad25d41 100644 --- a/presentation/src/main/java/org/gdsc/presentation/view/WebAppInterface.kt +++ b/presentation/src/main/java/org/gdsc/presentation/view/WebAppInterface.kt @@ -2,12 +2,42 @@ package org.gdsc.presentation.view import android.content.Context import android.webkit.JavascriptInterface -import android.widget.Toast -import org.gdsc.presentation.model.Route import org.json.JSONObject const val WEB_BASE_URL = "https://jmt-frontend-ad7b8.web.app/" +sealed class WebViewReceivedData { + data class Navigation( + val isVisible: Boolean + ): WebViewReceivedData() + + data class Back( + val enable: Boolean + ): WebViewReceivedData() +} +sealed class WebViewBrideObject { + data class Token(val data: Any? = null): WebViewBrideObject() + data class Other(val data: WebViewReceivedData): WebViewBrideObject() + +} + +fun JSONObject.toWebViewBrideObject(): WebViewBrideObject { + return when(this.get("name")) { + "token" -> { + WebViewBrideObject.Token() + } + "navigation" -> { + val isVisible = JSONObject(this.get("data").toString()).get("isVisible") as Boolean + WebViewBrideObject.Other(WebViewReceivedData.Navigation(isVisible)) + } + // back + else -> { + val enable = JSONObject(this.get("data").toString()).get("enable") as Boolean + WebViewBrideObject.Other(WebViewReceivedData.Back(enable)) + } + } +} + class WebAppInterface( private val mContext: Context, private val slideUpBottomNavigationView: () -> Unit = {}, @@ -28,8 +58,8 @@ class WebAppInterface( else slideDownBottomNavigationView() } - @JavascriptInterface - fun token() = setAccessToken() +// @JavascriptInterface +// fun token() = setAccessToken() // 딥링크 생성 필요 @JavascriptInterface @@ -37,20 +67,47 @@ class WebAppInterface( } // 데이터 구조는 다시 상의 후에 결정해서, 객체화 시키면 좋을 것으로 보임 +// @JavascriptInterface +// fun navigate(data: String) { +// val result = JSONObject(data) +// +// when(result.getString("route")) { +// Route.EDIT_RESTAURANT.route-> { +// val restaurantId = result.getString("restaurantId").toInt() +// navigateToRestaurantEdit(restaurantId) +// } +// } +// } + @JavascriptInterface - fun navigate(data: String) { - val result = JSONObject(data) + fun sendData(data: String) { + when (val result = JSONObject(data).toWebViewBrideObject()) { + is WebViewBrideObject.Token -> { + setAccessToken() + } + is WebViewBrideObject.Other -> { + when (val receivedData = result.data) { + is WebViewReceivedData.Navigation -> { + if (receivedData.isVisible) { + slideUpBottomNavigationView() + } else { + slideDownBottomNavigationView() + } + } + is WebViewReceivedData.Back -> { + if (receivedData.enable) { + + } else { - when(result.getString("route")) { - Route.EDIT_RESTAURANT.route-> { - val restaurantId = result.getString("restaurantId").toInt() - navigateToRestaurantEdit(restaurantId) + } + } + } } } } // webView.canGoBack으로 뒤로가기 처리 완료해서 비워뒀습니다. - @JavascriptInterface - fun back(isEnableBack: Boolean) { - } +// @JavascriptInterface +// fun back(isEnableBack: Boolean) { +// } } diff --git a/presentation/src/main/java/org/gdsc/presentation/view/group/MyGroupFragment.kt b/presentation/src/main/java/org/gdsc/presentation/view/group/MyGroupFragment.kt index c2602793..a488e81f 100644 --- a/presentation/src/main/java/org/gdsc/presentation/view/group/MyGroupFragment.kt +++ b/presentation/src/main/java/org/gdsc/presentation/view/group/MyGroupFragment.kt @@ -9,9 +9,12 @@ import android.webkit.WebViewClient import androidx.activity.addCallback import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels +import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.launch import org.gdsc.presentation.databinding.FragmentMyGroupBinding import org.gdsc.presentation.utils.repeatWhenUiStarted +import org.gdsc.presentation.view.MainActivity import org.gdsc.presentation.view.WEB_BASE_URL import org.gdsc.presentation.view.WebAppInterface import org.gdsc.presentation.view.webview.SpecificWebViewViewModel @@ -23,6 +26,8 @@ class MyGroupFragment: Fragment() { private val specificWebViewViewModel: SpecificWebViewViewModel by viewModels() + private val parentActivity by lazy { requireActivity() as MainActivity } + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? @@ -47,7 +52,20 @@ class MyGroupFragment: Fragment() { webViewClient = WebViewClient() addJavascriptInterface(WebAppInterface( - requireContext() + mContext = requireContext(), + slideUpBottomNavigationView = { + parentActivity.slideUpBottomNavigationView() + }, + slideDownBottomNavigationView ={ + parentActivity.slideDownBottomNavigationView() + }, + setAccessToken = { + viewLifecycleOwner.lifecycleScope.launch { + binding.webView.loadUrl( + "javascript:setAccessToken('${specificWebViewViewModel.getAccessToken()}')" + ) + } + } ), "webviewBridge") } From 274664651dfd53278835f017b692201fcd86fd1c Mon Sep 17 00:00:00 2001 From: soopeach Date: Fri, 15 Mar 2024 19:50:43 +0900 Subject: [PATCH 2/3] =?UTF-8?q?[feat/upgrade=5Fweb=5Fbridge]:=20=EA=B7=B8?= =?UTF-8?q?=EB=A3=B9=20=EB=A7=8C=EB=93=A4=EA=B8=B0=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EC=9B=B9=EB=B7=B0=20=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gdsc/presentation/view/WebViewActivity.kt | 37 ++++++++----------- .../presentation/view/home/HomeFragment.kt | 2 +- 2 files changed, 16 insertions(+), 23 deletions(-) diff --git a/presentation/src/main/java/org/gdsc/presentation/view/WebViewActivity.kt b/presentation/src/main/java/org/gdsc/presentation/view/WebViewActivity.kt index c94f8c0b..d19ccf76 100644 --- a/presentation/src/main/java/org/gdsc/presentation/view/WebViewActivity.kt +++ b/presentation/src/main/java/org/gdsc/presentation/view/WebViewActivity.kt @@ -20,36 +20,23 @@ import org.gdsc.presentation.utils.repeatWhenUiStarted class WebViewActivity : AppCompatActivity() { private lateinit var binding: ActivityWebViewBinding - private lateinit var webView:WebView + private lateinit var webView: WebView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityWebViewBinding.inflate(layoutInflater) setContentView(binding.root) -// webViewInit() - webView = binding.webView - setWebViewBackPress() - intent.extras?.let { - binding.webView.apply { - - repeatWhenUiStarted { - loadUrl(it.getString("url") ?: WEB_BASE_URL) - } + webViewInit() - settings.javaScriptEnabled = true - settings.domStorageEnabled = true - webViewClient = WebViewClient() - addJavascriptInterface(WebAppInterface(context), "webviewBridge") - } - - } + setWebViewBackPress() // val actionBar: ActionBar? = supportActionBar // actionBar!!.hide() } + private fun setWebViewBackPress() { this.onBackPressedDispatcher.addCallback(this) { if (binding.webView.canGoBack()) { @@ -60,14 +47,20 @@ class WebViewActivity : AppCompatActivity() { } } - fun webViewInit() { - val webSettings: WebSettings = webView.getSettings() - webSettings.javaScriptEnabled = true // allow the js + private fun webViewInit() { + + webView.apply { + loadUrl(WEB_BASE_URL + "group-create/name/") + + settings.javaScriptEnabled = true // allow the js + settings.domStorageEnabled = true + webViewClient = WebViewClient() + addJavascriptInterface(WebAppInterface(this@WebViewActivity), "webviewBridge") + } + window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) //화면이 계속 켜짐 requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_FULL_USER - webView.webViewClient = WebViewClient() - webView.addJavascriptInterface(WebAppInterface(this), "webviewBridge") } } diff --git a/presentation/src/main/java/org/gdsc/presentation/view/home/HomeFragment.kt b/presentation/src/main/java/org/gdsc/presentation/view/home/HomeFragment.kt index 051282dd..a39b1be8 100644 --- a/presentation/src/main/java/org/gdsc/presentation/view/home/HomeFragment.kt +++ b/presentation/src/main/java/org/gdsc/presentation/view/home/HomeFragment.kt @@ -368,7 +368,7 @@ class HomeFragment : Fragment(), ViewHolderBindListener { is ResultState.OnSuccess -> { val groupList = state.response - if (groupList.isNullOrEmpty()) { + if (groupList.isEmpty()) { viewModel.setCurrentGroup(null) BottomSheetDialog(requireContext()) From 68179ea4a4407a674447d4624474cb8b5bcfffe3 Mon Sep 17 00:00:00 2001 From: soopeach Date: Fri, 15 Mar 2024 20:22:55 +0900 Subject: [PATCH 3/3] =?UTF-8?q?[feat/upgrade=5Fweb=5Fbridge]:=20=EA=B7=B8?= =?UTF-8?q?=EB=A3=B9=20=EA=B2=80=EC=83=89=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EB=B0=8F=20BottomSheetDialog=20=EC=83=81=ED=83=9C=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EC=9D=B4=EC=8A=88=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../view/custom/BottomSheetDialog.kt | 17 ++- .../presentation/view/home/HomeFragment.kt | 135 ++++++++++++------ .../main/res/navigation/main_nav_graph.xml | 3 + 3 files changed, 108 insertions(+), 47 deletions(-) diff --git a/presentation/src/main/java/org/gdsc/presentation/view/custom/BottomSheetDialog.kt b/presentation/src/main/java/org/gdsc/presentation/view/custom/BottomSheetDialog.kt index 44dff558..039e9e51 100644 --- a/presentation/src/main/java/org/gdsc/presentation/view/custom/BottomSheetDialog.kt +++ b/presentation/src/main/java/org/gdsc/presentation/view/custom/BottomSheetDialog.kt @@ -34,11 +34,26 @@ class BottomSheetDialog { behavior.maxHeight = getBottomSheetDialogDefaultHeight() } - fun bindBuilder(binding: T, callback: T.(BottomSheetDialog) -> Unit) { + val isShowing: Boolean + get() { + return this.btmDlg.isShowing + } + + fun show() { + this.btmDlg.show() + } + + + fun bindBuilder( + binding: T, + callback: T.(BottomSheetDialog) -> Unit + ): org.gdsc.presentation.view.custom.BottomSheetDialog { this.customView = binding.root btmDlg.setContentView(this.customView!!) binding.callback(btmDlg) + return this } + private fun getBottomSheetDialogDefaultHeight(): Int { return getScreenRealHeight() * 70 / 100 } diff --git a/presentation/src/main/java/org/gdsc/presentation/view/home/HomeFragment.kt b/presentation/src/main/java/org/gdsc/presentation/view/home/HomeFragment.kt index a39b1be8..321e6809 100644 --- a/presentation/src/main/java/org/gdsc/presentation/view/home/HomeFragment.kt +++ b/presentation/src/main/java/org/gdsc/presentation/view/home/HomeFragment.kt @@ -13,6 +13,7 @@ import androidx.core.content.res.ResourcesCompat import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels +import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.ConcatAdapter import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -27,7 +28,6 @@ import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch import org.gdsc.domain.DrinkPossibility @@ -55,13 +55,46 @@ class HomeFragment : Fragment(), ViewHolderBindListener { private var _binding: FragmentHomeBinding? = null private val binding get() = _binding!! - val viewModel: HomeViewModel by viewModels() + private val viewModel: HomeViewModel by viewModels() private lateinit var mapView: MapView + private val bottomSheetDialog by lazy { + BottomSheetDialog(requireContext()) + .bindBuilder( + ContentSheetEmptyGroupBinding.inflate(LayoutInflater.from(requireContext())) + ) { dialog -> + with(dialog) { + binding.groupHeader.isVisible = false + dialog.setCancelable(false) + dialog.behavior.isDraggable = false + createGroupButton.setOnClickListener { + startActivity( + Intent(requireContext(), WebViewActivity::class.java) + ) + } + searchGroupButton.setOnClickListener { + dialog.dismiss() + findNavController().navigate( + HomeFragmentDirections.actionHomeFragmentToAllSearchFragment() + ) + } + show() + } + } + } + // TODO : titleAdapter 문구 정리 필요 - private val recommendPopularRestaurantTitleAdapter by lazy { RecommendPopularRestaurantTitleAdapter("그룹에서 인기가 많아요") } - private val recommendPopularRestaurantWrapperAdapter by lazy { RecommendPopularRestaurantWrapperAdapter(recommendPopularRestaurantList)} + private val recommendPopularRestaurantTitleAdapter by lazy { + RecommendPopularRestaurantTitleAdapter( + "그룹에서 인기가 많아요" + ) + } + private val recommendPopularRestaurantWrapperAdapter by lazy { + RecommendPopularRestaurantWrapperAdapter( + recommendPopularRestaurantList + ) + } private val restaurantFilterAdapter by lazy { RestaurantFilterAdapter(this) } private val restaurantListAdapter by lazy { MapMarkerWithRestaurantsAdatper() } private val mapMarkerAdapter by lazy { MapMarkerWithRestaurantsAdatper() } @@ -76,6 +109,16 @@ class HomeFragment : Fragment(), ViewHolderBindListener { savedInstanceState: Bundle? ): View { _binding = FragmentHomeBinding.inflate(inflater, container, false) + return binding.root + } + + private fun setRecyclerView() { + binding.recyclerView.adapter = concatAdapter + binding.recyclerView.layoutManager = LinearLayoutManager(requireContext()) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) setMap(savedInstanceState) observeState() @@ -93,17 +136,6 @@ class HomeFragment : Fragment(), ViewHolderBindListener { restaurantListAdapter ) } - - return binding.root - } - - private fun setRecyclerView() { - binding.recyclerView.adapter = concatAdapter - binding.recyclerView.layoutManager = LinearLayoutManager(requireContext()) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) setRestaurantListBottomSheet() setGroup() @@ -115,7 +147,8 @@ class HomeFragment : Fragment(), ViewHolderBindListener { val sortSpinner = holder.itemView.findViewById(R.id.sort_spinner) val foodSpinner = holder.itemView.findViewById(R.id.food_category_spinner) - val drinkSpinner = holder.itemView.findViewById(R.id.drink_possibility_spinner) + val drinkSpinner = + holder.itemView.findViewById(R.id.drink_possibility_spinner) sortSpinner.setMenu(SortType.getAllText()) { viewModel.setSortType(SortType.values()[it.itemId]) @@ -168,8 +201,10 @@ class HomeFragment : Fragment(), ViewHolderBindListener { if (holder is GroupSelectAdapter.GroupSelectViewHolder && _item is Group) { with(holder.itemView) { findViewById(R.id.group_name).text = _item.groupName - findViewById(R.id.select_button).isVisible = _item.isSelected - Glide.with(this).load(_item.groupProfileImageUrl).into(findViewById(R.id.group_image)) + findViewById(R.id.select_button).isVisible = + _item.isSelected + Glide.with(this).load(_item.groupProfileImageUrl) + .into(findViewById(R.id.group_image)) setOnClickListener { // TODO GroupList : 추후에 Room에 Current Group 넣어주게 되면, 반영해야 하는 부분 @@ -188,10 +223,11 @@ class HomeFragment : Fragment(), ViewHolderBindListener { with(dialog) { val groupSelectAdapter = GroupSelectAdapter(listener) viewModel.myGroupList.value.let { - when(it) { + when (it) { is ResultState.OnSuccess -> { groupSelectAdapter.submitList(it.response ?: emptyList()) } + else -> {} } } @@ -236,7 +272,8 @@ class HomeFragment : Fragment(), ViewHolderBindListener { naverMap.moveCamera(zoom) } - mapMarkerAdapter.registerAdapterDataObserver(object: RecyclerView.AdapterDataObserver() { + mapMarkerAdapter.registerAdapterDataObserver(object : + RecyclerView.AdapterDataObserver() { override fun onItemRangeInserted(positionStart: Int, itemCount: Int) { super.onItemRangeInserted(positionStart, itemCount) markerManager.updateDataList(mapMarkerAdapter.snapshot().items) @@ -258,10 +295,25 @@ class HomeFragment : Fragment(), ViewHolderBindListener { val projection: Projection = naverMap.projection val topLeft: LatLng = projection.fromScreenLocation(PointF(0F, 0F)) - val bottomRight: LatLng = projection.fromScreenLocation(PointF(naverMap.width.toFloat(), naverMap.height.toFloat())) - - viewModel.setStartLocation(Location(topLeft.longitude.toString(), topLeft.latitude.toString())) - viewModel.setEndLocation(Location(bottomRight.longitude.toString(), bottomRight.latitude.toString())) + val bottomRight: LatLng = projection.fromScreenLocation( + PointF( + naverMap.width.toFloat(), + naverMap.height.toFloat() + ) + ) + + viewModel.setStartLocation( + Location( + topLeft.longitude.toString(), + topLeft.latitude.toString() + ) + ) + viewModel.setEndLocation( + Location( + bottomRight.longitude.toString(), + bottomRight.latitude.toString() + ) + ) } } } @@ -294,10 +346,12 @@ class HomeFragment : Fragment(), ViewHolderBindListener { object : BottomSheetBehavior.BottomSheetCallback() { fun setBottomSheetRelatedView(isExpanded: Boolean) { viewModel.currentGroup.value.let { - val isNotFullExpanded = isExpanded.not() || it == null || it.restaurantCnt == 0 + val isNotFullExpanded = + isExpanded.not() || it == null || it.restaurantCnt == 0 with(binding) { - binding.bottomSheetActionButtons.isVisible = isExpanded && it != null && it.restaurantCnt != 0 + binding.bottomSheetActionButtons.isVisible = + isExpanded && it != null && it.restaurantCnt != 0 bottomSheetHandle.isVisible = isNotFullExpanded bottomSheetHandleSpace.isVisible = isNotFullExpanded mapOptionContainer.isVisible = isNotFullExpanded @@ -328,7 +382,7 @@ class HomeFragment : Fragment(), ViewHolderBindListener { } } - override fun onSlide(bottomSheet: View, slideOffset: Float) { } + override fun onSlide(bottomSheet: View, slideOffset: Float) {} }) CoroutineScope(Dispatchers.Main).launch { @@ -364,29 +418,16 @@ class HomeFragment : Fragment(), ViewHolderBindListener { repeatWhenUiStarted { viewModel.myGroupList.collect { state -> - when(state) { + when (state) { is ResultState.OnSuccess -> { val groupList = state.response if (groupList.isEmpty()) { viewModel.setCurrentGroup(null) - BottomSheetDialog(requireContext()) - .bindBuilder( - ContentSheetEmptyGroupBinding.inflate(LayoutInflater.from(requireContext())) - ) { dialog -> - with(dialog) { - binding.groupHeader.isVisible = false - dialog.setCancelable(false) - dialog.behavior.isDraggable = false - createGroupButton.setOnClickListener { - startActivity( - Intent(requireContext(), WebViewActivity::class.java) - ) - } - show() - } - } + if (bottomSheetDialog.isShowing.not()) { + bottomSheetDialog.show() + } } else { groupList.forEach { if (it.isSelected) { @@ -413,11 +454,13 @@ class HomeFragment : Fragment(), ViewHolderBindListener { } if (it.restaurantCnt == 0) { - binding.bottomSheet.layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT + binding.bottomSheet.layoutParams.height = + ViewGroup.LayoutParams.WRAP_CONTENT binding.recyclerView.isVisible = false binding.registLayout.isVisible = true } else { - binding.bottomSheet.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT + binding.bottomSheet.layoutParams.height = + ViewGroup.LayoutParams.MATCH_PARENT binding.recyclerView.isVisible = true binding.registLayout.isVisible = false } diff --git a/presentation/src/main/res/navigation/main_nav_graph.xml b/presentation/src/main/res/navigation/main_nav_graph.xml index 84b88c04..c32fe705 100644 --- a/presentation/src/main/res/navigation/main_nav_graph.xml +++ b/presentation/src/main/res/navigation/main_nav_graph.xml @@ -13,6 +13,9 @@ +