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 0000000..609b40b
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/btn_dropdown_down.png differ
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 0000000..58b32a2
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/btn_dropdown_up.png differ
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 0000000..cf85945
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_service_center_kakao.png differ
diff --git a/app/src/main/res/drawable/bg_round_corner_4_7_222222.xml b/app/src/main/res/drawable/bg_round_corner_4_7_222222.xml
new file mode 100644
index 0000000..a7561de
--- /dev/null
+++ b/app/src/main/res/drawable/bg_round_corner_4_7_222222.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
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