From a75217ee09c8412f907ea99b27ff5fcfd6b42141 Mon Sep 17 00:00:00 2001 From: klaus Date: Wed, 9 Aug 2023 08:39:24 +0900 Subject: [PATCH] =?UTF-8?q?=EA=B3=A0=EA=B0=9D=EC=84=BC=ED=84=B0=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=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/AndroidManifest.xml | 1 + .../java/kr/co/vividnext/sodalive/di/AppDI.kt | 6 + .../sodalive/mypage/MyPageFragment.kt | 10 +- .../sodalive/mypage/service_center/Faq.kt | 8 ++ .../mypage/service_center/FaqAdapter.kt | 93 +++++++++++++++ .../sodalive/mypage/service_center/FaqApi.kt | 20 ++++ .../service_center/FaqCategoryAdapter.kt | 67 +++++++++++ .../mypage/service_center/FaqRepository.kt | 9 ++ .../service_center/ServiceCenterActivity.kt | 107 ++++++++++++++++++ .../service_center/ServiceCenterViewModel.kt | 81 +++++++++++++ .../sodalive/settings/SettingsActivity.kt | 7 ++ .../res/drawable-xxhdpi/btn_dropdown_down.png | Bin 0 -> 291 bytes .../res/drawable-xxhdpi/btn_dropdown_up.png | Bin 0 -> 276 bytes .../ic_service_center_kakao.png | Bin 0 -> 514 bytes .../drawable/bg_round_corner_4_7_222222.xml | 8 ++ .../res/drawable/bg_round_corner_8_ffe368.xml | 8 ++ .../res/layout/activity_service_center.xml | 91 +++++++++++++++ app/src/main/res/layout/item_faq.xml | 91 +++++++++++++++ app/src/main/res/layout/item_faq_category.xml | 18 +++ app/src/main/res/values/colors.xml | 1 + 20 files changed, 625 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/mypage/service_center/Faq.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/mypage/service_center/FaqAdapter.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/mypage/service_center/FaqApi.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/mypage/service_center/FaqCategoryAdapter.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/mypage/service_center/FaqRepository.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/mypage/service_center/ServiceCenterActivity.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/mypage/service_center/ServiceCenterViewModel.kt create mode 100644 app/src/main/res/drawable-xxhdpi/btn_dropdown_down.png create mode 100644 app/src/main/res/drawable-xxhdpi/btn_dropdown_up.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_service_center_kakao.png create mode 100644 app/src/main/res/drawable/bg_round_corner_4_7_222222.xml create mode 100644 app/src/main/res/drawable/bg_round_corner_8_ffe368.xml create mode 100644 app/src/main/res/layout/activity_service_center.xml create mode 100644 app/src/main/res/layout/item_faq.xml create mode 100644 app/src/main/res/layout/item_faq_category.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7071186..b1f79f2 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -88,6 +88,7 @@ + (FragmentMyBinding::inflat ) } - binding.rlServiceCenter.setOnClickListener {} + binding.rlServiceCenter.setOnClickListener { + startActivity( + Intent( + requireActivity(), + ServiceCenterActivity::class.java + ) + ) + } binding.tvAuth.setOnClickListener { Auth.auth(requireActivity(), requireContext()) { diff --git a/app/src/main/java/kr/co/vividnext/sodalive/mypage/service_center/Faq.kt b/app/src/main/java/kr/co/vividnext/sodalive/mypage/service_center/Faq.kt new file mode 100644 index 0000000..77c28ee --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/mypage/service_center/Faq.kt @@ -0,0 +1,8 @@ +package kr.co.vividnext.sodalive.mypage.service_center + +import com.google.gson.annotations.SerializedName + +data class Faq( + @SerializedName("question") val question: String, + @SerializedName("answer") val answer: String +) diff --git a/app/src/main/java/kr/co/vividnext/sodalive/mypage/service_center/FaqAdapter.kt b/app/src/main/java/kr/co/vividnext/sodalive/mypage/service_center/FaqAdapter.kt new file mode 100644 index 0000000..fec3f82 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/mypage/service_center/FaqAdapter.kt @@ -0,0 +1,93 @@ +package kr.co.vividnext.sodalive.mypage.service_center + +import android.annotation.SuppressLint +import android.graphics.Color +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.webkit.CookieManager +import android.webkit.WebSettings +import androidx.core.view.isVisible +import androidx.recyclerview.widget.RecyclerView +import androidx.webkit.WebSettingsCompat +import androidx.webkit.WebViewFeature +import kr.co.vividnext.sodalive.R +import kr.co.vividnext.sodalive.databinding.ItemFaqBinding + +class FaqAdapter : RecyclerView.Adapter() { + + val items = mutableListOf() + + @SuppressLint("SetJavaScriptEnabled") + inner class ViewHolder( + private val binding: ItemFaqBinding + ) : RecyclerView.ViewHolder(binding.root) { + init { + if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) { + WebSettingsCompat.setForceDark( + binding.wvAnswer.settings, + WebSettingsCompat.FORCE_DARK_ON + ) + } + + binding.wvAnswer.apply { + setLayerType(View.LAYER_TYPE_HARDWARE, null) + + clearCache(true) + settings.apply { + javaScriptEnabled = true + javaScriptCanOpenWindowsAutomatically = true + mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW + val cookieManager = CookieManager.getInstance() + cookieManager.setAcceptCookie(true) + cookieManager.setAcceptThirdPartyCookies(binding.wvAnswer, true) + cacheMode = WebSettings.LOAD_NO_CACHE + blockNetworkImage = false + loadsImagesAutomatically = true + useWideViewPort = false + loadWithOverviewMode = true + javaScriptCanOpenWindowsAutomatically = true + + domStorageEnabled = true + loadWithOverviewMode = true + allowContentAccess = true + + setSupportZoom(false) + displayZoomControls = false + } + } + } + + fun bind(item: Faq) { + binding.tvQuestion.text = item.question + binding.wvAnswer.loadData(item.answer, "text/html", "UTF-8") + binding.wvAnswer.setBackgroundColor(Color.BLACK) + binding.root.setOnClickListener { + binding.llAnswer.isVisible = !binding.llAnswer.isVisible + binding.ivDropdown.setImageResource( + if (binding.llAnswer.isVisible) { + R.drawable.btn_dropdown_up + } else { + R.drawable.btn_dropdown_down + } + ) + } + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return ViewHolder( + ItemFaqBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + holder.bind(items[position]) + } + + override fun getItemCount() = items.count() +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/mypage/service_center/FaqApi.kt b/app/src/main/java/kr/co/vividnext/sodalive/mypage/service_center/FaqApi.kt new file mode 100644 index 0000000..39b4e37 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/mypage/service_center/FaqApi.kt @@ -0,0 +1,20 @@ +package kr.co.vividnext.sodalive.mypage.service_center + +import io.reactivex.rxjava3.core.Single +import kr.co.vividnext.sodalive.common.ApiResponse +import retrofit2.http.GET +import retrofit2.http.Header +import retrofit2.http.Query + +interface FaqApi { + @GET("/faq") + fun getFaqs( + @Query("category") category: String, + @Header("Authorization") authHeader: String + ): Single>> + + @GET("/faq/category") + fun getFaqCategories( + @Header("Authorization") authHeader: String + ): Single>> +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/mypage/service_center/FaqCategoryAdapter.kt b/app/src/main/java/kr/co/vividnext/sodalive/mypage/service_center/FaqCategoryAdapter.kt new file mode 100644 index 0000000..593b2d8 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/mypage/service_center/FaqCategoryAdapter.kt @@ -0,0 +1,67 @@ +package kr.co.vividnext.sodalive.mypage.service_center + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.BaseAdapter +import android.widget.TextView +import kr.co.vividnext.sodalive.R + +class FaqCategoryAdapter( + private val context: Context +) : BaseAdapter() { + + val items = mutableListOf() + private var selectedCategory = "" + + override fun getCount(): Int { + return items.count() + } + + override fun getItem(position: Int): Any { + return items[position] + } + + override fun getItemId(position: Int): Long { + return 0 + } + + override fun getView(position: Int, view: View?, parent: ViewGroup?): View { + var convertView = view + val viewHolder: ViewHolder + + if (convertView == null) { + convertView = + LayoutInflater.from(context).inflate(R.layout.item_faq_category, parent, false) + viewHolder = ViewHolder() + viewHolder.tvCategory = convertView!!.findViewById(R.id.tv_category) + convertView.tag = viewHolder + } else { + viewHolder = convertView.tag as ViewHolder + } + + val item = items[position] + viewHolder.tvCategory.text = item + viewHolder.tvCategory.setBackgroundResource( + if (item == selectedCategory) { + R.drawable.bg_round_corner_4_7_9970ff + } else { + R.drawable.bg_round_corner_4_7_222222 + } + ) + + return convertView + } + + fun selectCategory(position: Int): String { + selectedCategory = items[position] + notifyDataSetChanged() + + return selectedCategory + } + + inner class ViewHolder { + lateinit var tvCategory: TextView + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/mypage/service_center/FaqRepository.kt b/app/src/main/java/kr/co/vividnext/sodalive/mypage/service_center/FaqRepository.kt new file mode 100644 index 0000000..dbc3dbf --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/mypage/service_center/FaqRepository.kt @@ -0,0 +1,9 @@ +package kr.co.vividnext.sodalive.mypage.service_center + +class FaqRepository(private val api: FaqApi) { + fun getCategories(token: String) = api.getFaqCategories(authHeader = token) + fun getFaqs(category: String, token: String) = api.getFaqs( + category = category, + authHeader = token + ) +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/mypage/service_center/ServiceCenterActivity.kt b/app/src/main/java/kr/co/vividnext/sodalive/mypage/service_center/ServiceCenterActivity.kt new file mode 100644 index 0000000..c9a79c8 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/mypage/service_center/ServiceCenterActivity.kt @@ -0,0 +1,107 @@ +package kr.co.vividnext.sodalive.mypage.service_center + +import android.annotation.SuppressLint +import android.content.Intent +import android.graphics.Rect +import android.net.Uri +import android.os.Bundle +import android.view.View +import android.widget.LinearLayout +import android.widget.Toast +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import kr.co.vividnext.sodalive.base.BaseActivity +import kr.co.vividnext.sodalive.databinding.ActivityServiceCenterBinding +import kr.co.vividnext.sodalive.extensions.dpToPx +import org.koin.android.ext.android.inject +import kotlin.math.ceil + +class ServiceCenterActivity : BaseActivity( + ActivityServiceCenterBinding::inflate +) { + + private val viewModel: ServiceCenterViewModel by inject() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + viewModel.getFaqCategories() + } + + @SuppressLint("NotifyDataSetChanged") + override fun setupView() { + binding.toolbar.tvBack.text = "고객센터" + binding.toolbar.tvBack.setOnClickListener { finish() } + + val categoryAdapter = FaqCategoryAdapter(this) + binding.gvFaqCategory.adapter = categoryAdapter + binding.gvFaqCategory.setOnItemClickListener { _, _, position, _ -> + viewModel.getFaqs(categoryAdapter.selectCategory(position)) + } + + binding.llQuestionKakao.setOnClickListener { + startActivity( + Intent( + Intent.ACTION_VIEW, + Uri.parse("http://pf.kakao.com/_sZaeb") + ) + ) + } + + val recyclerView = binding.rvFaq + val faqAdapter = FaqAdapter() + + recyclerView.layoutManager = LinearLayoutManager( + this, + LinearLayoutManager.VERTICAL, + false + ) + + recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() { + override fun getItemOffsets( + outRect: Rect, + view: View, + parent: RecyclerView, + state: RecyclerView.State + ) { + super.getItemOffsets(outRect, view, parent, state) + + outRect.left = 13.3f.dpToPx().toInt() + outRect.right = 13.3f.dpToPx().toInt() + + when (parent.getChildAdapterPosition(view)) { + faqAdapter.itemCount - 1 -> { + outRect.top = 0.dpToPx().toInt() + outRect.bottom = 40.dpToPx().toInt() + } + + else -> { + outRect.top = 0 + outRect.bottom = 0 + } + } + } + }) + + recyclerView.adapter = faqAdapter + + viewModel.toastLiveData.observe(this) { + it?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show() } + } + + viewModel.categoriesLiveData.observe(this) { + categoryAdapter.items.addAll(it) + + val lp = binding.gvFaqCategory.layoutParams as LinearLayout.LayoutParams + lp.height = (ceil(it.count().toFloat() / 4f) * 46.7f + 10f).dpToPx().toInt() + + viewModel.getFaqs(categoryAdapter.selectCategory(0)) + } + + viewModel.faqLiveData.observe(this) { + faqAdapter.items.clear() + faqAdapter.items.addAll(it) + faqAdapter.notifyDataSetChanged() + } + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/mypage/service_center/ServiceCenterViewModel.kt b/app/src/main/java/kr/co/vividnext/sodalive/mypage/service_center/ServiceCenterViewModel.kt new file mode 100644 index 0000000..a5d6ece --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/mypage/service_center/ServiceCenterViewModel.kt @@ -0,0 +1,81 @@ +package kr.co.vividnext.sodalive.mypage.service_center + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import com.orhanobut.logger.Logger +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers +import io.reactivex.rxjava3.schedulers.Schedulers +import kr.co.vividnext.sodalive.base.BaseViewModel +import kr.co.vividnext.sodalive.common.SharedPreferenceManager + +class ServiceCenterViewModel(val repository: FaqRepository) : BaseViewModel() { + + private val _categoriesLiveData = MutableLiveData>() + val categoriesLiveData: LiveData> + get() = _categoriesLiveData + + private val _faqLiveData = MutableLiveData>() + val faqLiveData: LiveData> + get() = _faqLiveData + + private val _toastLiveData = MutableLiveData() + val toastLiveData: LiveData + get() = _toastLiveData + + fun getFaqCategories() { + compositeDisposable.add( + repository.getCategories(token = "Bearer ${SharedPreferenceManager.token}") + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { + if (it.success && it.data != null) { + _categoriesLiveData.postValue(it.data!!) + } else { + if (it.message != null) { + _toastLiveData.postValue(it.message) + } else { + _toastLiveData.postValue( + "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요." + ) + } + } + }, + { + it.message?.let { message -> Logger.e(message) } + _toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.") + } + ) + ) + } + + fun getFaqs(category: String) { + compositeDisposable.add( + repository.getFaqs( + category = category, + token = "Bearer ${SharedPreferenceManager.token}" + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { + if (it.success && it.data != null) { + _faqLiveData.postValue(it.data!!) + } else { + if (it.message != null) { + _toastLiveData.postValue(it.message) + } else { + _toastLiveData.postValue( + "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요." + ) + } + } + }, + { + it.message?.let { message -> Logger.e(message) } + _toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.") + } + ) + ) + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/settings/SettingsActivity.kt b/app/src/main/java/kr/co/vividnext/sodalive/settings/SettingsActivity.kt index ee08763..20ee5ad 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/settings/SettingsActivity.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/settings/SettingsActivity.kt @@ -7,6 +7,7 @@ import android.os.Bundle import android.widget.Toast import com.google.android.gms.oss.licenses.OssLicensesMenuActivity import kr.co.vividnext.sodalive.BuildConfig +import kr.co.vividnext.sodalive.audio_content.AudioContentPlayService import kr.co.vividnext.sodalive.base.BaseActivity import kr.co.vividnext.sodalive.base.SodaDialog import kr.co.vividnext.sodalive.common.LoadingDialog @@ -121,6 +122,12 @@ class SettingsActivity : BaseActivity(ActivitySettingsB } private fun logout() { + startService( + Intent(applicationContext, AudioContentPlayService::class.java).apply { + action = AudioContentPlayService.MusicAction.STOP.name + } + ) + viewModel.logout { SharedPreferenceManager.clear() finishAffinity() diff --git a/app/src/main/res/drawable-xxhdpi/btn_dropdown_down.png b/app/src/main/res/drawable-xxhdpi/btn_dropdown_down.png new file mode 100644 index 0000000000000000000000000000000000000000..609b40bc4d6059ccce43844b0dc3d11ae3d188ec GIT binary patch literal 291 zcmeAS@N?(olHy`uVBq!ia0vp^HXzKx3?xrnI^qbVSkfJRf%L|H?mvmFKt5w}kh>GZ zx^prwfgHsEpAc8~0-uE?|7R6~QPKaI1^?%k!MKGWuJ7f44}glfN`m}?8MtS@54k0G zz^th3+fzoMXqTspV~EA+)DhmvG>0pv+rk!&0DCdi9 z=E~mD2^M{+T7(8A5T-G@yGywpKy>FlZ literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxhdpi/btn_dropdown_up.png b/app/src/main/res/drawable-xxhdpi/btn_dropdown_up.png new file mode 100644 index 0000000000000000000000000000000000000000..58b32a2746b7007ac05c6df9e8b1e1a91cb2619d GIT binary patch literal 276 zcmeAS@N?(olHy`uVBq!ia0vp^HXzKx3?xrnI^qbVSkfJRf%L|H?mvmFKt5w}kh>GZ zx^prwfgHI2pAc8~0-rgh|7REdpH=vOR?+{NKoW=wK;+DV|IcegKLS;9mIV0)Gl&|# z3c0jxa`D^FKn|c_y{C&~h{fr*=e30$934YC^GZS^fg{53e|&jb8H!{T^bvL7s;)5^p<>5^i*)AU$kJt{KDpS63yn*zX|C* zXXm#ueYxgALG4GD^%rK>KfgX(>fX++7e)W;)_-DH|6#Vzw?yNL(v$~Gc}JpwPGRtL L^>bP0l+XkK>27TM literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxhdpi/ic_service_center_kakao.png b/app/src/main/res/drawable-xxhdpi/ic_service_center_kakao.png new file mode 100644 index 0000000000000000000000000000000000000000..cf8594571dbbc3b02ce855f017d76cb4cf113008 GIT binary patch literal 514 zcmeAS@N?(olHy`uVBq!ia0vp^_CRdG!3-p8TP}nGDVB6cUq=Rpjs4tz5?O(K#^NA% zCx&(BWL^R}mH|E?uI>dsKqeXBbirqNpf;hBAirP+_M5gs6)$Fe-Psv&(nlrJXjR7s zF*l%fo}Mm_As)wt51#{@k*&~`#zv@6i<*7vWW^axeQoQS`IpjQ?5(ZL>?~O!Ke|6WaGCu$Spyv3FSD&qYkz zj1T-{E{slS*4Yzu;PBZ(#x?2Y_2xQrB&}4f926|~U+1>_IQjmRdnSeJB)7lw6_~!O ze%9_Fhw|6TZ*Rz@RD7Axn|rBbLBFlR=G+_Cf2SrToXY)NW7lSINNQ2%#v9*Le$TP~ za^nvB&$i>aJHIEdS3NpuGK;;LqWO=!)Efu?-#1wQ_Wt+BU!tG!v#Bk2+ + + + + + diff --git a/app/src/main/res/drawable/bg_round_corner_8_ffe368.xml b/app/src/main/res/drawable/bg_round_corner_8_ffe368.xml new file mode 100644 index 0000000..3738388 --- /dev/null +++ b/app/src/main/res/drawable/bg_round_corner_8_ffe368.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/app/src/main/res/layout/activity_service_center.xml b/app/src/main/res/layout/activity_service_center.xml new file mode 100644 index 0000000..c5e2138 --- /dev/null +++ b/app/src/main/res/layout/activity_service_center.xml @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_faq.xml b/app/src/main/res/layout/item_faq.xml new file mode 100644 index 0000000..7c69544 --- /dev/null +++ b/app/src/main/res/layout/item_faq.xml @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_faq_category.xml b/app/src/main/res/layout/item_faq_category.xml new file mode 100644 index 0000000..451f7f9 --- /dev/null +++ b/app/src/main/res/layout/item_faq_category.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 1bdecb4..2dd14c7 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -96,4 +96,5 @@ #660FD4 #2F90B7 #A94400 + #FFE368