From cc8fab76b0886000fc6b988856afff6919b96259 Mon Sep 17 00:00:00 2001 From: klaus Date: Wed, 2 Aug 2023 17:26:46 +0900 Subject: [PATCH] =?UTF-8?q?=EC=84=A4=EC=A0=95=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=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 | 7 + .../co/vividnext/sodalive/common/Constants.kt | 2 + .../java/kr/co/vividnext/sodalive/di/AppDI.kt | 14 + .../sodalive/mypage/MyPageFragment.kt | 10 +- .../sodalive/settings/SettingsActivity.kt | 130 ++++++++ .../sodalive/settings/SettingsViewModel.kt | 47 +++ .../sodalive/settings/event/EventActivity.kt | 102 +++++++ .../sodalive/settings/event/EventAdapter.kt | 41 +++ .../settings/event/EventDetailActivity.kt | 55 ++++ .../sodalive/settings/event/EventViewModel.kt | 66 ++++ .../settings/notice/GetNoticeResponse.kt | 17 ++ .../settings/notice/NoticeActivity.kt | 107 +++++++ .../sodalive/settings/notice/NoticeAdapter.kt | 41 +++ .../sodalive/settings/notice/NoticeApi.kt | 17 ++ .../settings/notice/NoticeDetailActivity.kt | 55 ++++ .../settings/notice/NoticeRepository.kt | 15 + .../settings/notice/NoticeViewModel.kt | 66 ++++ .../NotificationSettingsActivity.kt | 81 +++++ .../NotificationSettingsViewModel.kt | 110 +++++++ .../settings/signout/SignOutActivity.kt | 106 +++++++ .../settings/signout/SignOutRequest.kt | 8 + .../settings/signout/SignOutViewModel.kt | 70 +++++ .../kr/co/vividnext/sodalive/user/UserApi.kt | 10 + .../vividnext/sodalive/user/UserRepository.kt | 8 + .../drawable/bg_round_corner_10_3e737c.xml | 8 + app/src/main/res/layout/activity_event.xml | 17 ++ .../main/res/layout/activity_event_detail.xml | 44 +++ app/src/main/res/layout/activity_notice.xml | 17 ++ .../res/layout/activity_notice_detail.xml | 50 +++ .../layout/activity_notification_settings.xml | 111 +++++++ app/src/main/res/layout/activity_settings.xml | 287 +++++++++++++++++ app/src/main/res/layout/activity_sign_out.xml | 288 ++++++++++++++++++ app/src/main/res/layout/item_event.xml | 17 ++ app/src/main/res/layout/item_notice.xml | 42 +++ app/src/main/res/values/colors.xml | 1 + 35 files changed, 2066 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/settings/SettingsActivity.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/settings/SettingsViewModel.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/settings/event/EventActivity.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/settings/event/EventAdapter.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/settings/event/EventDetailActivity.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/settings/event/EventViewModel.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/settings/notice/GetNoticeResponse.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/settings/notice/NoticeActivity.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/settings/notice/NoticeAdapter.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/settings/notice/NoticeApi.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/settings/notice/NoticeDetailActivity.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/settings/notice/NoticeRepository.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/settings/notice/NoticeViewModel.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/settings/notification/NotificationSettingsActivity.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/settings/notification/NotificationSettingsViewModel.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/settings/signout/SignOutActivity.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/settings/signout/SignOutRequest.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/settings/signout/SignOutViewModel.kt create mode 100644 app/src/main/res/drawable/bg_round_corner_10_3e737c.xml create mode 100644 app/src/main/res/layout/activity_event.xml create mode 100644 app/src/main/res/layout/activity_event_detail.xml create mode 100644 app/src/main/res/layout/activity_notice.xml create mode 100644 app/src/main/res/layout/activity_notice_detail.xml create mode 100644 app/src/main/res/layout/activity_notification_settings.xml create mode 100644 app/src/main/res/layout/activity_settings.xml create mode 100644 app/src/main/res/layout/activity_sign_out.xml create mode 100644 app/src/main/res/layout/item_event.xml create mode 100644 app/src/main/res/layout/item_notice.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8c21014..d30549d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -48,6 +48,13 @@ + + + + + + + (FragmentMyBinding::inflat } private fun setupView() { - binding.ivSettings.setOnClickListener {} + binding.ivSettings.setOnClickListener { + startActivity( + Intent( + requireActivity(), + SettingsActivity::class.java + ) + ) + } binding.ivEdit.setOnClickListener {} 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 new file mode 100644 index 0000000..ee08763 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/settings/SettingsActivity.kt @@ -0,0 +1,130 @@ +package kr.co.vividnext.sodalive.settings + +import android.annotation.SuppressLint +import android.content.Intent +import android.graphics.Paint +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.base.BaseActivity +import kr.co.vividnext.sodalive.base.SodaDialog +import kr.co.vividnext.sodalive.common.LoadingDialog +import kr.co.vividnext.sodalive.common.SharedPreferenceManager +import kr.co.vividnext.sodalive.databinding.ActivitySettingsBinding +import kr.co.vividnext.sodalive.settings.event.EventActivity +import kr.co.vividnext.sodalive.settings.notice.NoticeActivity +import kr.co.vividnext.sodalive.settings.notification.NotificationSettingsActivity +import kr.co.vividnext.sodalive.settings.signout.SignOutActivity +import kr.co.vividnext.sodalive.settings.terms.TermsActivity +import kr.co.vividnext.sodalive.splash.SplashActivity +import org.koin.android.ext.android.inject + +class SettingsActivity : BaseActivity(ActivitySettingsBinding::inflate) { + private val logoutDialog: SodaDialog by lazy { + SodaDialog( + activity = this, + layoutInflater = layoutInflater, + title = "알림", + desc = "로그아웃 하시겠어요?", + confirmButtonTitle = "확인", + confirmButtonClick = { logout() }, + cancelButtonTitle = "취소", + cancelButtonClick = {} + ) + } + + private val viewModel: SettingsViewModel by inject() + + private lateinit var loadingDialog: LoadingDialog + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + bindData() + } + + @SuppressLint("SetTextI18n") + override fun setupView() { + binding.toolbar.tvBack.text = "설정" + binding.toolbar.tvBack.setOnClickListener { finish() } + + loadingDialog = LoadingDialog(this, layoutInflater) + + binding.rlNotice.setOnClickListener { + startActivity( + Intent( + applicationContext, + NoticeActivity::class.java + ) + ) + } + + binding.rlEvent.setOnClickListener { + startActivity( + Intent( + applicationContext, + EventActivity::class.java + ) + ) + } + + binding.rlNotificationSettings.setOnClickListener { + startActivity( + Intent( + applicationContext, + NotificationSettingsActivity::class.java + ) + ) + } + + binding.rlTerms.setOnClickListener { + val intent = Intent(applicationContext, TermsActivity::class.java) + intent.putExtra("terms", "terms") + startActivity(intent) + } + + binding.rlPrivacyPolicy.setOnClickListener { + val intent = Intent(applicationContext, TermsActivity::class.java) + intent.putExtra("terms", "privacy") + startActivity(intent) + } + + binding.rlOssLicense.setOnClickListener { + startActivity(Intent(applicationContext, OssLicensesMenuActivity::class.java)) + OssLicensesMenuActivity.setActivityTitle("오픈소스 라이선스") + } + + binding.tvVersion.text = "ver ${BuildConfig.VERSION_NAME}" + + binding.tvLogOut.setOnClickListener { + logoutDialog.show(screenWidth) + } + + binding.tvSignOut.paintFlags = binding.tvSignOut.paintFlags.or(Paint.UNDERLINE_TEXT_FLAG) + binding.tvSignOut.setOnClickListener { + startActivity(Intent(applicationContext, SignOutActivity::class.java)) + } + } + + private fun bindData() { + viewModel.isLoading.observe(this) { + if (it) { + loadingDialog.show(screenWidth) + } else { + loadingDialog.dismiss() + } + } + + viewModel.toastLiveData.observe(this) { + it?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show() } + } + } + + private fun logout() { + viewModel.logout { + SharedPreferenceManager.clear() + finishAffinity() + startActivity(Intent(applicationContext, SplashActivity::class.java)) + } + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/settings/SettingsViewModel.kt b/app/src/main/java/kr/co/vividnext/sodalive/settings/SettingsViewModel.kt new file mode 100644 index 0000000..b05ed9b --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/settings/SettingsViewModel.kt @@ -0,0 +1,47 @@ +package kr.co.vividnext.sodalive.settings + +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 +import kr.co.vividnext.sodalive.user.UserRepository + +class SettingsViewModel(private val userRepository: UserRepository) : BaseViewModel() { + private val _toastLiveData = MutableLiveData() + val toastLiveData: LiveData + get() = _toastLiveData + + private var _isLoading = MutableLiveData(false) + val isLoading: LiveData + get() = _isLoading + + fun logout(onSuccess: () -> Unit) { + compositeDisposable.add( + userRepository.logout(token = "Bearer ${SharedPreferenceManager.token}") + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { + if (it.success) { + onSuccess() + } 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/event/EventActivity.kt b/app/src/main/java/kr/co/vividnext/sodalive/settings/event/EventActivity.kt new file mode 100644 index 0000000..6bcbe1f --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/settings/event/EventActivity.kt @@ -0,0 +1,102 @@ +package kr.co.vividnext.sodalive.settings.event + +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.Toast +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import kr.co.vividnext.sodalive.base.BaseActivity +import kr.co.vividnext.sodalive.common.Constants +import kr.co.vividnext.sodalive.common.LoadingDialog +import kr.co.vividnext.sodalive.databinding.ActivityEventBinding +import kr.co.vividnext.sodalive.extensions.dpToPx +import org.koin.android.ext.android.inject + +class EventActivity : BaseActivity(ActivityEventBinding::inflate) { + + private val viewModel: EventViewModel by inject() + + private lateinit var loadingDialog: LoadingDialog + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + viewModel.isLoading.observe(this) { + if (it) { + loadingDialog.show(screenWidth, "이벤트를 불러오고 있습니다.") + } else { + loadingDialog.dismiss() + } + } + + viewModel.toastLiveData.observe(this) { + it?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show() } + } + + viewModel.getEvents() + } + + @SuppressLint("NotifyDataSetChanged") + override fun setupView() { + binding.toolbar.tvBack.text = "이벤트" + binding.toolbar.tvBack.setOnClickListener { finish() } + + loadingDialog = LoadingDialog(this, layoutInflater) + + val adapter = EventAdapter { + if (it.detailImageUrl != null) { + val intent = Intent(applicationContext, EventDetailActivity::class.java) + intent.putExtra(Constants.EXTRA_EVENT, it) + startActivity(intent) + } else if (!it.link.isNullOrBlank()) { + startActivity( + Intent( + Intent.ACTION_VIEW, + Uri.parse(it.link) + ) + ) + } + } + binding.rvEvent.layoutManager = LinearLayoutManager(this) + binding.rvEvent.addItemDecoration(object : RecyclerView.ItemDecoration() { + override fun getItemOffsets( + outRect: Rect, + view: View, + parent: RecyclerView, + state: RecyclerView.State + ) { + super.getItemOffsets(outRect, view, parent, state) + + when (parent.getChildAdapterPosition(view)) { + 0 -> { + outRect.top = 13.3f.dpToPx().toInt() + outRect.bottom = 6.7f.dpToPx().toInt() + } + + adapter.itemCount - 1 -> { + outRect.top = 6.7f.dpToPx().toInt() + outRect.bottom = 13.3f.dpToPx().toInt() + } + + else -> { + outRect.top = 6.7f.dpToPx().toInt() + outRect.bottom = 6.7f.dpToPx().toInt() + } + } + + outRect.left = 13.3f.dpToPx().toInt() + outRect.right = 13.3f.dpToPx().toInt() + } + }) + binding.rvEvent.adapter = adapter + + viewModel.eventLiveData.observe(this) { + adapter.items.addAll(it) + adapter.notifyDataSetChanged() + } + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/settings/event/EventAdapter.kt b/app/src/main/java/kr/co/vividnext/sodalive/settings/event/EventAdapter.kt new file mode 100644 index 0000000..2b5b54d --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/settings/event/EventAdapter.kt @@ -0,0 +1,41 @@ +package kr.co.vividnext.sodalive.settings.event + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import coil.load +import kr.co.vividnext.sodalive.databinding.ItemEventBinding + +class EventAdapter( + private val onClick: (EventItem) -> Unit +) : RecyclerView.Adapter() { + + val items = mutableListOf() + + inner class ViewHolder( + private val binding: ItemEventBinding, + private val onClick: (EventItem) -> Unit + ) : RecyclerView.ViewHolder(binding.root) { + fun bind(event: EventItem) { + binding.ivEvent.load(event.thumbnailImageUrl) + binding.root.setOnClickListener { onClick(event) } + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return ViewHolder( + ItemEventBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ), + onClick + ) + } + + 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/settings/event/EventDetailActivity.kt b/app/src/main/java/kr/co/vividnext/sodalive/settings/event/EventDetailActivity.kt new file mode 100644 index 0000000..13a8309 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/settings/event/EventDetailActivity.kt @@ -0,0 +1,55 @@ +package kr.co.vividnext.sodalive.settings.event + +import android.content.Intent +import android.net.Uri +import android.view.View +import android.widget.FrameLayout +import android.widget.RelativeLayout +import androidx.core.content.IntentCompat +import coil.load +import kr.co.vividnext.sodalive.base.BaseActivity +import kr.co.vividnext.sodalive.common.Constants +import kr.co.vividnext.sodalive.databinding.ActivityEventDetailBinding + +class EventDetailActivity : BaseActivity( + ActivityEventDetailBinding::inflate +) { + private lateinit var event: EventItem + + override fun setupView() { + event = IntentCompat.getParcelableExtra( + intent, + Constants.EXTRA_EVENT, + EventItem::class.java + )!! + + binding.toolbar.tvBack.text = "이벤트 상세" + binding.toolbar.tvBack.setOnClickListener { finish() } + + if (!event.link.isNullOrBlank()) { + binding.flParticipate.visibility = View.VISIBLE + + val flParticipateLp = + binding.flParticipate.layoutParams as RelativeLayout.LayoutParams + + val ivEventLp = binding.ivEvent.layoutParams as FrameLayout.LayoutParams + ivEventLp.bottomMargin = flParticipateLp.height + binding.ivEvent.layoutParams = ivEventLp + } else { + binding.flParticipate.visibility = View.GONE + } + + binding.tvParticipate.setOnClickListener { + if (!event.link.isNullOrBlank()) { + startActivity( + Intent( + Intent.ACTION_VIEW, + Uri.parse(event.link) + ) + ) + } + } + + binding.ivEvent.load(event.detailImageUrl) + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/settings/event/EventViewModel.kt b/app/src/main/java/kr/co/vividnext/sodalive/settings/event/EventViewModel.kt new file mode 100644 index 0000000..fd91fc2 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/settings/event/EventViewModel.kt @@ -0,0 +1,66 @@ +package kr.co.vividnext.sodalive.settings.event + +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 EventViewModel(private val repository: EventRepository) : BaseViewModel() { + private val _eventLiveData = MutableLiveData>() + val eventLiveData: LiveData> + get() = _eventLiveData + + private val _toastLiveData = MutableLiveData() + val toastLiveData: LiveData + get() = _toastLiveData + + private var _isLoading = MutableLiveData(false) + val isLoading: LiveData + get() = _isLoading + + private var totalCount = -1 + private var page = 1 + private val size = 10 + + fun getEvents() { + if (totalCount < 0 || page * size < totalCount) { + _isLoading.value = true + compositeDisposable.add( + repository.getEvents( + page = page - 1, + size = size, + token = "Bearer ${SharedPreferenceManager.token}" + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { + if (it.success && it.data != null) { + val data = it.data + this.totalCount = data.totalCount + _eventLiveData.postValue(data.eventList) + page += 1 + } else { + if (it.message != null) { + _toastLiveData.postValue(it.message) + } else { + _toastLiveData.postValue( + "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요." + ) + } + } + _isLoading.value = false + }, + { + _isLoading.value = false + it.message?.let { message -> Logger.e(message) } + _toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.") + } + ) + ) + } + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/settings/notice/GetNoticeResponse.kt b/app/src/main/java/kr/co/vividnext/sodalive/settings/notice/GetNoticeResponse.kt new file mode 100644 index 0000000..4eafae9 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/settings/notice/GetNoticeResponse.kt @@ -0,0 +1,17 @@ +package kr.co.vividnext.sodalive.settings.notice + +import android.os.Parcelable +import com.google.gson.annotations.SerializedName +import kotlinx.parcelize.Parcelize + +data class GetNoticeResponse( + @SerializedName("totalCount") val totalCount: Int, + @SerializedName("noticeList") val noticeList: List +) + +@Parcelize +data class NoticeItem( + @SerializedName("title") val title: String, + @SerializedName("content") val content: String, + @SerializedName("date") val date: String +) : Parcelable diff --git a/app/src/main/java/kr/co/vividnext/sodalive/settings/notice/NoticeActivity.kt b/app/src/main/java/kr/co/vividnext/sodalive/settings/notice/NoticeActivity.kt new file mode 100644 index 0000000..0e08066 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/settings/notice/NoticeActivity.kt @@ -0,0 +1,107 @@ +package kr.co.vividnext.sodalive.settings.notice + +import android.annotation.SuppressLint +import android.content.Intent +import android.graphics.Rect +import android.os.Bundle +import android.view.View +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.common.Constants +import kr.co.vividnext.sodalive.common.LoadingDialog +import kr.co.vividnext.sodalive.databinding.ActivityNoticeBinding +import kr.co.vividnext.sodalive.extensions.dpToPx +import org.koin.android.ext.android.inject + +class NoticeActivity : BaseActivity(ActivityNoticeBinding::inflate) { + + private val viewModel: NoticeViewModel by inject() + + private lateinit var loadingDialog: LoadingDialog + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + viewModel.isLoading.observe(this) { + if (it) { + loadingDialog.show(screenWidth, "공지사항을 불러오고 있습니다.") + } else { + loadingDialog.dismiss() + } + } + + viewModel.toastLiveData.observe(this) { + it?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show() } + } + + viewModel.getNotices() + } + + @SuppressLint("NotifyDataSetChanged") + override fun setupView() { + binding.toolbar.tvBack.text = "공지사항" + binding.toolbar.tvBack.setOnClickListener { finish() } + + loadingDialog = LoadingDialog(this, layoutInflater) + + val adapter = NoticeAdapter { + val intent = Intent(applicationContext, NoticeDetailActivity::class.java) + intent.putExtra(Constants.EXTRA_NOTICE, it) + startActivity(intent) + } + binding.rvNotice.layoutManager = LinearLayoutManager(this) + binding.rvNotice.addItemDecoration(object : RecyclerView.ItemDecoration() { + override fun getItemOffsets( + outRect: Rect, + view: View, + parent: RecyclerView, + state: RecyclerView.State + ) { + super.getItemOffsets(outRect, view, parent, state) + + when (parent.getChildAdapterPosition(view)) { + 0 -> { + outRect.top = 13.3f.dpToPx().toInt() + outRect.bottom = 0 + } + + adapter.itemCount - 1 -> { + outRect.top = 0 + outRect.bottom = 13.3f.dpToPx().toInt() + } + + else -> { + outRect.top = 0 + outRect.bottom = 0 + } + } + + outRect.left = 13.3f.dpToPx().toInt() + outRect.right = 13.3f.dpToPx().toInt() + } + }) + + binding.rvNotice.adapter = adapter + + binding.rvNotice.addOnScrollListener(object : RecyclerView.OnScrollListener() { + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + super.onScrolled(recyclerView, dx, dy) + + val lastVisiblePosition = (recyclerView.layoutManager as LinearLayoutManager) + .findLastVisibleItemPosition() + val itemTotalCount = adapter.itemCount - 1 + + if (lastVisiblePosition == itemTotalCount) { + viewModel.getNotices() + } + } + }) + + viewModel.noticeLiveData.observe(this) { + adapter.items.addAll(it) + adapter.notifyDataSetChanged() + } + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/settings/notice/NoticeAdapter.kt b/app/src/main/java/kr/co/vividnext/sodalive/settings/notice/NoticeAdapter.kt new file mode 100644 index 0000000..08ffc12 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/settings/notice/NoticeAdapter.kt @@ -0,0 +1,41 @@ +package kr.co.vividnext.sodalive.settings.notice + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import kr.co.vividnext.sodalive.databinding.ItemNoticeBinding + +class NoticeAdapter( + private val onClick: (NoticeItem) -> Unit +) : RecyclerView.Adapter() { + + val items = mutableListOf() + + inner class ViewHolder( + private val binding: ItemNoticeBinding, + private val onClick: (NoticeItem) -> Unit + ) : RecyclerView.ViewHolder(binding.root) { + fun bind(notice: NoticeItem) { + binding.tvTitle.text = notice.title + binding.tvDate.text = notice.date + binding.root.setOnClickListener { onClick(notice) } + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return ViewHolder( + ItemNoticeBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ), + onClick + ) + } + + 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/settings/notice/NoticeApi.kt b/app/src/main/java/kr/co/vividnext/sodalive/settings/notice/NoticeApi.kt new file mode 100644 index 0000000..cbf1fb8 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/settings/notice/NoticeApi.kt @@ -0,0 +1,17 @@ +package kr.co.vividnext.sodalive.settings.notice + +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 NoticeApi { + @GET("/notice") + fun getNotices( + @Query("timezone") timezone: String, + @Query("page") page: Int, + @Query("size") size: Int, + @Header("Authorization") authHeader: String + ): Single> +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/settings/notice/NoticeDetailActivity.kt b/app/src/main/java/kr/co/vividnext/sodalive/settings/notice/NoticeDetailActivity.kt new file mode 100644 index 0000000..de5620c --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/settings/notice/NoticeDetailActivity.kt @@ -0,0 +1,55 @@ +package kr.co.vividnext.sodalive.settings.notice + +import android.annotation.SuppressLint +import androidx.core.content.IntentCompat +import androidx.webkit.WebSettingsCompat +import androidx.webkit.WebViewFeature +import kr.co.vividnext.sodalive.base.BaseActivity +import kr.co.vividnext.sodalive.common.Constants +import kr.co.vividnext.sodalive.databinding.ActivityNoticeDetailBinding + +class NoticeDetailActivity : BaseActivity( + ActivityNoticeDetailBinding::inflate +) { + private lateinit var notice: NoticeItem + + @SuppressLint("SetJavaScriptEnabled") + override fun setupView() { + if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) { + WebSettingsCompat.setForceDark( + binding.webView.settings, + WebSettingsCompat.FORCE_DARK_ON + ) + } + + notice = IntentCompat.getParcelableExtra( + intent, + Constants.EXTRA_NOTICE, + NoticeItem::class.java + )!! + + binding.toolbar.tvBack.text = "공지사항 상세" + binding.toolbar.tvBack.setOnClickListener { finish() } + + binding.tvTitle.text = notice.title + binding.tvDate.text = notice.date + + val viewPort = "" + val data = viewPort + notice.content + binding.webView.settings.apply { + javaScriptEnabled = true // 자바스크립트 실행 허용 + javaScriptCanOpenWindowsAutomatically = false // 자바스크립트에서 새창 실 행 허용 + setSupportMultipleWindows(false) // 새 창 실행 허용 + loadWithOverviewMode = true // 메타 태그 허용 + + useWideViewPort = true // 화면 사이즈 맞추기 허용 + setSupportZoom(false) // 화면 줌 허용 + builtInZoomControls = false // 화면 확대 축소 허용 여부 + } + binding.webView.loadData( + data, + "text/html; charset=utf-8", + "utf-8" + ) + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/settings/notice/NoticeRepository.kt b/app/src/main/java/kr/co/vividnext/sodalive/settings/notice/NoticeRepository.kt new file mode 100644 index 0000000..d73de8e --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/settings/notice/NoticeRepository.kt @@ -0,0 +1,15 @@ +package kr.co.vividnext.sodalive.settings.notice + +import io.reactivex.rxjava3.core.Single +import kr.co.vividnext.sodalive.common.ApiResponse +import java.util.TimeZone + +class NoticeRepository(private val api: NoticeApi) { + fun getNotices( + page: Int, + size: Int, + token: String + ): Single> { + return api.getNotices(TimeZone.getDefault().id, page, size, authHeader = token) + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/settings/notice/NoticeViewModel.kt b/app/src/main/java/kr/co/vividnext/sodalive/settings/notice/NoticeViewModel.kt new file mode 100644 index 0000000..4c790f1 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/settings/notice/NoticeViewModel.kt @@ -0,0 +1,66 @@ +package kr.co.vividnext.sodalive.settings.notice + +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 NoticeViewModel(private val repository: NoticeRepository) : BaseViewModel() { + private val _noticeLiveData = MutableLiveData>() + val noticeLiveData: LiveData> + get() = _noticeLiveData + + private val _toastLiveData = MutableLiveData() + val toastLiveData: LiveData + get() = _toastLiveData + + private var _isLoading = MutableLiveData(false) + val isLoading: LiveData + get() = _isLoading + + private var totalCount = -1 + private var page = 1 + private val size = 10 + + fun getNotices() { + if (totalCount < 0 || page * size < totalCount) { + _isLoading.value = true + compositeDisposable.add( + repository.getNotices( + page = page - 1, + size = size, + token = "Bearer ${SharedPreferenceManager.token}" + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { + if (it.success && it.data != null) { + val data = it.data + this.totalCount = data.totalCount + _noticeLiveData.postValue(data.noticeList) + page += 1 + } else { + if (it.message != null) { + _toastLiveData.postValue(it.message) + } else { + _toastLiveData.postValue( + "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요." + ) + } + } + _isLoading.value = false + }, + { + _isLoading.value = false + it.message?.let { message -> Logger.e(message) } + _toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.") + } + ) + ) + } + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/settings/notification/NotificationSettingsActivity.kt b/app/src/main/java/kr/co/vividnext/sodalive/settings/notification/NotificationSettingsActivity.kt new file mode 100644 index 0000000..46947d4 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/settings/notification/NotificationSettingsActivity.kt @@ -0,0 +1,81 @@ +package kr.co.vividnext.sodalive.settings.notification + +import android.os.Bundle +import android.widget.Toast +import kr.co.vividnext.sodalive.R +import kr.co.vividnext.sodalive.base.BaseActivity +import kr.co.vividnext.sodalive.common.LoadingDialog +import kr.co.vividnext.sodalive.databinding.ActivityNotificationSettingsBinding +import org.koin.android.ext.android.inject + +class NotificationSettingsActivity : BaseActivity( + ActivityNotificationSettingsBinding::inflate +) { + + private val viewModel: NotificationSettingsViewModel by inject() + + private lateinit var loadingDialog: LoadingDialog + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + viewModel.getNotificationSettings() + } + + override fun setupView() { + binding.toolbar.tvBack.text = "알림 설정" + binding.toolbar.tvBack.setOnClickListener { finish() } + + loadingDialog = LoadingDialog(this, layoutInflater) + + binding.ivNotifiedLive.setOnClickListener { + viewModel.toggleFollowingChannelLiveNotice() + } + binding.ivNotifiedUploadContent.setOnClickListener { + viewModel.toggleFollowingChannelUploadContentNotice() + } + binding.ivMessage.setOnClickListener { viewModel.toggleMessage() } + + viewModel.followingChannelLiveNotice.observe(this) { + binding.ivNotifiedLive.setImageResource( + if (it) { + R.drawable.btn_toggle_on_big + } else { + R.drawable.btn_toggle_off_big + } + ) + } + + viewModel.followingChannelUploadContentNotice.observe(this) { + binding.ivNotifiedUploadContent.setImageResource( + if (it) { + R.drawable.btn_toggle_on_big + } else { + R.drawable.btn_toggle_off_big + } + ) + } + + viewModel.isMessage.observe(this) { + binding.ivMessage.setImageResource( + if (it) { + R.drawable.btn_toggle_on_big + } else { + R.drawable.btn_toggle_off_big + } + ) + } + + viewModel.isLoading.observe(this) { + if (it) { + loadingDialog.show(screenWidth, "") + } else { + loadingDialog.dismiss() + } + } + + viewModel.toastLiveData.observe(this) { + it?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show() } + } + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/settings/notification/NotificationSettingsViewModel.kt b/app/src/main/java/kr/co/vividnext/sodalive/settings/notification/NotificationSettingsViewModel.kt new file mode 100644 index 0000000..8cec88c --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/settings/notification/NotificationSettingsViewModel.kt @@ -0,0 +1,110 @@ +package kr.co.vividnext.sodalive.settings.notification + +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 +import kr.co.vividnext.sodalive.user.UserRepository + +class NotificationSettingsViewModel( + private val userRepository: UserRepository +) : BaseViewModel() { + private val _toastLiveData = MutableLiveData() + val toastLiveData: LiveData + get() = _toastLiveData + + private var _isLoading = MutableLiveData(false) + val isLoading: LiveData + get() = _isLoading + + private var _followingChannelLiveNotice = MutableLiveData(false) + val followingChannelLiveNotice: LiveData + get() = _followingChannelLiveNotice + + private var _isMessage = MutableLiveData(false) + val isMessage: LiveData + get() = _isMessage + + private var _followingChannelUploadContentNotice = MutableLiveData(false) + val followingChannelUploadContentNotice: LiveData + get() = _followingChannelUploadContentNotice + + fun getNotificationSettings() { + _isLoading.value = true + + compositeDisposable.add( + userRepository.getMemberInfo(token = "Bearer ${SharedPreferenceManager.token}") + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { + _isLoading.value = false + + if (it.success && it.data != null) { + val data = it.data!! + _isMessage.value = data.messageNotice ?: false + _followingChannelUploadContentNotice.value = + data.followingChannelUploadContentNotice ?: false + _followingChannelLiveNotice.value = + data.followingChannelLiveNotice ?: false + } else { + if (it.message != null) { + _toastLiveData.postValue(it.message) + } else { + _toastLiveData.postValue( + "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요." + ) + } + } + }, + { + _isLoading.value = false + it.message?.let { message -> Logger.e(message) } + _toastLiveData.postValue( + "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + ) + } + ) + ) + } + + fun toggleFollowingChannelLiveNotice() { + _followingChannelLiveNotice.value = !followingChannelLiveNotice.value!! + updateNotificationSettings(live = followingChannelLiveNotice.value!!) + } + + fun toggleFollowingChannelUploadContentNotice() { + _followingChannelUploadContentNotice.value = !followingChannelUploadContentNotice.value!! + updateNotificationSettings(uploadContent = followingChannelUploadContentNotice.value!!) + } + + fun toggleMessage() { + _isMessage.value = !isMessage.value!! + updateNotificationSettings(message = isMessage.value!!) + } + + private fun updateNotificationSettings( + live: Boolean? = null, + uploadContent: Boolean? = null, + message: Boolean? = null + ) { + if (live != null || uploadContent != null || message != null) { + compositeDisposable.add( + userRepository.updateNotificationSettings( + request = UpdateNotificationSettingRequest( + live, + uploadContent, + message + ), + token = "Bearer ${SharedPreferenceManager.token}" + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({}, {}) + ) + } + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/settings/signout/SignOutActivity.kt b/app/src/main/java/kr/co/vividnext/sodalive/settings/signout/SignOutActivity.kt new file mode 100644 index 0000000..9139078 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/settings/signout/SignOutActivity.kt @@ -0,0 +1,106 @@ +package kr.co.vividnext.sodalive.settings.signout + +import android.content.Intent +import android.os.Bundle +import android.widget.TextView +import android.widget.Toast +import com.jakewharton.rxbinding4.widget.textChanges +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers +import io.reactivex.rxjava3.schedulers.Schedulers +import kr.co.vividnext.sodalive.base.BaseActivity +import kr.co.vividnext.sodalive.common.LoadingDialog +import kr.co.vividnext.sodalive.databinding.ActivitySignOutBinding +import kr.co.vividnext.sodalive.splash.SplashActivity +import org.koin.android.ext.android.inject +import java.util.concurrent.TimeUnit + +class SignOutActivity : BaseActivity(ActivitySignOutBinding::inflate) { + private val viewModel: SignOutViewModel by inject() + + private val reasonButtons = mutableListOf() + private lateinit var loadingDialog: LoadingDialog + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + bindData() + } + + private fun bindData() { + viewModel.toastLiveData.observe(this) { + it?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show() } + } + + viewModel.isLoading.observe(this) { + if (it) { + loadingDialog.show(screenWidth, "") + } else { + loadingDialog.dismiss() + } + } + + compositeDisposable.add( + binding.etPassword.textChanges().skip(1) + .debounce(500, TimeUnit.MILLISECONDS) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { + viewModel.password = it.toString() + } + ) + compositeDisposable.add( + binding.etReasonEtc.textChanges().skip(1) + .debounce(500, TimeUnit.MILLISECONDS) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { + if (binding.tvReason10.isSelected) { + viewModel.reason = it.toString() + } + } + ) + } + + override fun setupView() { + reasonButtons.add(binding.tvReason1) + reasonButtons.add(binding.tvReason2) + reasonButtons.add(binding.tvReason3) + reasonButtons.add(binding.tvReason4) + reasonButtons.add(binding.tvReason5) + reasonButtons.add(binding.tvReason6) + reasonButtons.add(binding.tvReason7) + reasonButtons.add(binding.tvReason8) + reasonButtons.add(binding.tvReason9) + reasonButtons.add(binding.tvReason10) + + loadingDialog = LoadingDialog(this, layoutInflater) + binding.toolbar.tvBack.text = "회원탈퇴" + binding.toolbar.tvBack.setOnClickListener { finish() } + + for (reasonButton in reasonButtons) { + val reason = reasonButton.text.toString() + reasonButton.setOnClickListener { + checkboxSelectedFalse() + reasonButton.isSelected = true + viewModel.reason = if (reason == "기타") { + binding.etReasonEtc.text.toString() + } else { + reasonButton.text.toString() + } + } + } + + binding.tvSignOut.setOnClickListener { + viewModel.signOut { + finishAffinity() + startActivity(Intent(applicationContext, SplashActivity::class.java)) + } + } + } + + private fun checkboxSelectedFalse() { + for (reasonButton in reasonButtons) { + reasonButton.isSelected = false + } + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/settings/signout/SignOutRequest.kt b/app/src/main/java/kr/co/vividnext/sodalive/settings/signout/SignOutRequest.kt new file mode 100644 index 0000000..6d61121 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/settings/signout/SignOutRequest.kt @@ -0,0 +1,8 @@ +package kr.co.vividnext.sodalive.settings.signout + +import com.google.gson.annotations.SerializedName + +data class SignOutRequest( + @SerializedName("reason") val reason: String, + @SerializedName("password") val password: String +) diff --git a/app/src/main/java/kr/co/vividnext/sodalive/settings/signout/SignOutViewModel.kt b/app/src/main/java/kr/co/vividnext/sodalive/settings/signout/SignOutViewModel.kt new file mode 100644 index 0000000..fbf1c24 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/settings/signout/SignOutViewModel.kt @@ -0,0 +1,70 @@ +package kr.co.vividnext.sodalive.settings.signout + +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 +import kr.co.vividnext.sodalive.user.UserRepository + +class SignOutViewModel(private val repository: UserRepository) : BaseViewModel() { + + private val _toastLiveData = MutableLiveData() + val toastLiveData: LiveData + get() = _toastLiveData + + private var _isLoading = MutableLiveData(false) + val isLoading: LiveData + get() = _isLoading + + var reason: String = "" + var password = "" + + fun signOut(onSuccess: () -> Unit) { + if (!_isLoading.value!!) { + if (reason.isBlank()) { + _toastLiveData.postValue("계정을 삭제하려는 이유를 선택해 주세요.") + return + } + + if (password.isBlank()) { + _toastLiveData.postValue("비밀번호를 입력해 주세요.") + return + } + + _isLoading.value = true + val request = SignOutRequest(reason.trim(), password) + + compositeDisposable.add( + repository.signOut(request, "Bearer ${SharedPreferenceManager.token}") + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { + _isLoading.value = false + + if (it.message != null) { + _toastLiveData.postValue(it.message) + } else { + _toastLiveData.postValue( + "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요." + ) + } + + if (it.success) { + SharedPreferenceManager.clear() + onSuccess() + } + }, + { + _isLoading.value = false + it.message?.let { message -> Logger.e(message) } + _toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.") + } + ) + ) + } + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/user/UserApi.kt b/app/src/main/java/kr/co/vividnext/sodalive/user/UserApi.kt index d3350ff..f87d5e6 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/user/UserApi.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/user/UserApi.kt @@ -8,6 +8,7 @@ import kr.co.vividnext.sodalive.main.PushTokenUpdateRequest import kr.co.vividnext.sodalive.mypage.MyPageResponse import kr.co.vividnext.sodalive.settings.notification.GetMemberInfoResponse import kr.co.vividnext.sodalive.settings.notification.UpdateNotificationSettingRequest +import kr.co.vividnext.sodalive.settings.signout.SignOutRequest import kr.co.vividnext.sodalive.user.find_password.ForgotPasswordRequest import kr.co.vividnext.sodalive.user.login.LoginRequest import kr.co.vividnext.sodalive.user.login.LoginResponse @@ -88,4 +89,13 @@ interface UserApi { @Query("nickname") nickname: String, @Header("Authorization") authHeader: String ): Single>> + + @POST("/member/sign_out") + fun signOut( + @Body request: SignOutRequest, + @Header("Authorization") authHeader: String + ): Single> + + @POST("/member/logout") + fun logout(@Header("Authorization") authHeader: String): Single> } diff --git a/app/src/main/java/kr/co/vividnext/sodalive/user/UserRepository.kt b/app/src/main/java/kr/co/vividnext/sodalive/user/UserRepository.kt index f6e7cea..7506e26 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/user/UserRepository.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/user/UserRepository.kt @@ -9,6 +9,7 @@ import kr.co.vividnext.sodalive.mypage.MyPageResponse import kr.co.vividnext.sodalive.settings.notification.UpdateNotificationSettingRequest import kr.co.vividnext.sodalive.user.find_password.ForgotPasswordRequest import kr.co.vividnext.sodalive.user.login.LoginRequest +import kr.co.vividnext.sodalive.settings.signout.SignOutRequest import okhttp3.MultipartBody import okhttp3.RequestBody @@ -70,4 +71,11 @@ class UserRepository(private val userApi: UserApi) { ): Single>> { return userApi.searchUser(nickname, authHeader = token) } + + fun signOut( + request: SignOutRequest, + token: String + ) = userApi.signOut(request, authHeader = token) + + fun logout(token: String) = userApi.logout(authHeader = token) } diff --git a/app/src/main/res/drawable/bg_round_corner_10_3e737c.xml b/app/src/main/res/drawable/bg_round_corner_10_3e737c.xml new file mode 100644 index 0000000..7ad47c9 --- /dev/null +++ b/app/src/main/res/drawable/bg_round_corner_10_3e737c.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/app/src/main/res/layout/activity_event.xml b/app/src/main/res/layout/activity_event.xml new file mode 100644 index 0000000..5d1a549 --- /dev/null +++ b/app/src/main/res/layout/activity_event.xml @@ -0,0 +1,17 @@ + + + + + + + + diff --git a/app/src/main/res/layout/activity_event_detail.xml b/app/src/main/res/layout/activity_event_detail.xml new file mode 100644 index 0000000..e7476c3 --- /dev/null +++ b/app/src/main/res/layout/activity_event_detail.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_notice.xml b/app/src/main/res/layout/activity_notice.xml new file mode 100644 index 0000000..1cc7f1f --- /dev/null +++ b/app/src/main/res/layout/activity_notice.xml @@ -0,0 +1,17 @@ + + + + + + + + diff --git a/app/src/main/res/layout/activity_notice_detail.xml b/app/src/main/res/layout/activity_notice_detail.xml new file mode 100644 index 0000000..fa4cc17 --- /dev/null +++ b/app/src/main/res/layout/activity_notice_detail.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_notification_settings.xml b/app/src/main/res/layout/activity_notification_settings.xml new file mode 100644 index 0000000..980ba3b --- /dev/null +++ b/app/src/main/res/layout/activity_notification_settings.xml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml new file mode 100644 index 0000000..03ff3f5 --- /dev/null +++ b/app/src/main/res/layout/activity_settings.xml @@ -0,0 +1,287 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_sign_out.xml b/app/src/main/res/layout/activity_sign_out.xml new file mode 100644 index 0000000..1c207dd --- /dev/null +++ b/app/src/main/res/layout/activity_sign_out.xml @@ -0,0 +1,288 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_event.xml b/app/src/main/res/layout/item_event.xml new file mode 100644 index 0000000..2426f55 --- /dev/null +++ b/app/src/main/res/layout/item_event.xml @@ -0,0 +1,17 @@ + + + + + + diff --git a/app/src/main/res/layout/item_notice.xml b/app/src/main/res/layout/item_notice.xml new file mode 100644 index 0000000..0ffadd7 --- /dev/null +++ b/app/src/main/res/layout/item_notice.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 1d8a4a0..f23a871 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -69,4 +69,5 @@ #99000000 #4C9970FF #4DD8D8D8 + #3E737C