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