diff --git a/app/src/main/java/com/egobook/app/ui/account/view/AccountBottomSheetFragment.kt b/app/src/main/java/com/egobook/app/ui/account/view/AccountBottomSheetFragment.kt index 70d4940d..27a0c93a 100644 --- a/app/src/main/java/com/egobook/app/ui/account/view/AccountBottomSheetFragment.kt +++ b/app/src/main/java/com/egobook/app/ui/account/view/AccountBottomSheetFragment.kt @@ -13,7 +13,6 @@ import com.egobook.app.databinding.FragmentAccountBottomSheetBinding import com.egobook.app.ui.account.viewmodel.AccountViewModel import com.egobook.app.util.UiState import com.google.android.material.bottomsheet.BottomSheetDialogFragment -import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.launch class AccountBottomSheetFragment : BottomSheetDialogFragment() { @@ -55,23 +54,24 @@ class AccountBottomSheetFragment : BottomSheetDialogFragment() { viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { - // 연동 상태 및 이메일 관찰 + // 연동 상태 관찰 launch { viewModel.linkState.collect { state -> val isLinked = state is UiState.Success - if (isLinked) { - binding.tvAccountEmail.apply { - visibility = View.VISIBLE - text = viewModel.userEmail.firstOrNull() ?: "" - } - binding.btnBottomGoogleLogin.apply { - // 이메일이 있으면 표시, 없으면 기본 메시지 - text = "Google계정으로 연동되었습니다" - isEnabled = false - } + binding.tvAccountEmail.visibility = if (isLinked) View.VISIBLE else View.GONE + binding.btnBottomGoogleLogin.apply { + text = if (isLinked) "Google계정으로 연동되었습니다" else "Google계정 연동하기" + isEnabled = !isLinked } } } + + // 이메일 별도 관찰 - 연동 직후 이메일이 설정되면 UI 갱신 + launch { + viewModel.userEmail.collect { email -> + binding.tvAccountEmail.text = email ?: "" + } + } } } } diff --git a/app/src/main/java/com/egobook/app/ui/account/view/AccountFragment.kt b/app/src/main/java/com/egobook/app/ui/account/view/AccountFragment.kt index e7613770..6cfc1d5a 100644 --- a/app/src/main/java/com/egobook/app/ui/account/view/AccountFragment.kt +++ b/app/src/main/java/com/egobook/app/ui/account/view/AccountFragment.kt @@ -1,5 +1,7 @@ package com.egobook.app.ui.account.view +import android.content.Intent +import androidx.core.net.toUri import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -27,13 +29,20 @@ import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch import timber.log.Timber -import android.util.Base64 import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import com.egobook.app.BlurLevel import com.egobook.app.applyScreenBlur -import kotlinx.coroutines.flow.MutableSharedFlow -import org.json.JSONObject +import android.text.SpannableString +import android.text.Spanned +import android.text.TextPaint +import android.text.method.LinkMovementMethod +import android.text.style.ClickableSpan +import android.graphics.Color +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context + @AndroidEntryPoint class AccountFragment : Fragment() { @@ -178,6 +187,15 @@ class AccountFragment : Fragment() { accountDeleteDialog1Fragment.isCancelable = false accountDeleteDialog1Fragment.show(childFragmentManager, "AccountDeleteDialog1Fragment") } + + // ✅ 고객지원 클릭 + tvSupport.setOnClickListener { + copySupportEmailToClipboard() + } + + setupPromiseText() + + } } @@ -219,6 +237,63 @@ class AccountFragment : Fragment() { _binding = null } + private fun copySupportEmailToClipboard() { + val email = "egobook.official@gmail.com" // ← 여기 너네 고객지원 이메일로 바꿔 + + val clipboard = requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + val clip = ClipData.newPlainText("Support Email", email) + clipboard.setPrimaryClip(clip) + + Toast.makeText(requireContext(), "고객지원 이메일이 복사되었습니다.", Toast.LENGTH_SHORT).show() + } + + private fun setupPromiseText() { + val fullText = "이용약관 및 개인정보처리" + val spannable = SpannableString(fullText) + + // ===== 이용약관 ===== + val termsText = "이용약관" + val termsStart = fullText.indexOf(termsText) + val termsEnd = termsStart + termsText.length + + spannable.setSpan(object : ClickableSpan() { + override fun onClick(widget: View) { + val intent = Intent( + Intent.ACTION_VIEW, + "https://bevel-beetle-a49.notion.site/2f638a539ac5801aa872e99ec4282f28".toUri() // ← 약관 URL + ) + startActivity(intent) + } + + override fun updateDrawState(ds: TextPaint) { + ds.isUnderlineText = false + } + }, termsStart, termsEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) + + // ===== 개인정보처리 ===== + val privacyText = "개인정보처리" + val privacyStart = fullText.indexOf(privacyText) + val privacyEnd = privacyStart + privacyText.length + + spannable.setSpan(object : ClickableSpan() { + override fun onClick(widget: View) { + val intent = Intent( + Intent.ACTION_VIEW, + "https://bevel-beetle-a49.notion.site/2f638a539ac58059b9a1c883ad7d7164".toUri() // ← 개인정보 URL + ) + startActivity(intent) + } + + override fun updateDrawState(ds: TextPaint) { + ds.isUnderlineText = false + } + }, privacyStart, privacyEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) + + binding.tvPromise.text = spannable + binding.tvPromise.movementMethod = LinkMovementMethod.getInstance() + binding.tvPromise.highlightColor = Color.TRANSPARENT + } + //=============다이알로그 출력용 블러뷰 세팅==================== private fun setupBlur() { diff --git a/app/src/main/java/com/egobook/app/ui/login/view/LoginActivity.kt b/app/src/main/java/com/egobook/app/ui/login/view/LoginActivity.kt index e36fae06..a79200c0 100644 --- a/app/src/main/java/com/egobook/app/ui/login/view/LoginActivity.kt +++ b/app/src/main/java/com/egobook/app/ui/login/view/LoginActivity.kt @@ -36,6 +36,10 @@ import com.google.android.libraries.identity.googleid.GetGoogleIdOption import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential import com.egobook.app.ui.onboarding.view.OnboardingActivity import timber.log.Timber +import androidx.core.net.toUri +import android.text.method.LinkMovementMethod +import android.text.style.ClickableSpan +import android.graphics.Color @AndroidEntryPoint class LoginActivity : AppCompatActivity() { @@ -131,6 +135,12 @@ class LoginActivity : AppCompatActivity() { } } + + //약관 + binding.tvStartGuide.setOnClickListener { + val intent = Intent(Intent.ACTION_VIEW, "https://bevel-beetle-a49.notion.site/2f638a539ac58059b9a1c883ad7d7164".toUri()) + startActivity(intent) + } } private fun getGoogleRequest(): GetCredentialRequest { @@ -241,33 +251,71 @@ class LoginActivity : AppCompatActivity() { //========================================특정 char 폰트 커스텀=========================================================== - private fun setupGuideText() { - val fullText = "시작 시 이용약관 및\n개인정보 수집 및 이용에 동의하게 됩니다" - val spannableString = SpannableString(fullText) - - // 폰트 리소스를 Typeface 객체로 불러옴. - val semiBoldTypeface = ResourcesCompat.getFont(this, R.font.arita_semibold) ?: return - - // 폰트를 적용할 텍스트 ("이용약관") - val target1 = "이용약관" - val start1 = fullText.indexOf(target1) - if (start1 >= 0) { - val end1 = start1 + target1.length - spannableString.setSpan(CustomTypefaceSpan(semiBoldTypeface), start1, end1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) - } +private fun setupGuideText() { + val fullText = "시작 시 이용약관 및\n개인정보 수집 및 이용에 동의하게 됩니다" + val spannableString = SpannableString(fullText) + + val semiBoldTypeface = ResourcesCompat.getFont(this, R.font.arita_semibold) ?: return + + // ================= 이용약관 ================= + val termText = "이용약관" + val termStart = fullText.indexOf(termText) + if (termStart >= 0) { + val termEnd = termStart + termText.length + + spannableString.setSpan( + CustomTypefaceSpan(semiBoldTypeface), + termStart, termEnd, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE + ) + + spannableString.setSpan(object : ClickableSpan() { + override fun onClick(widget: View) { + val intent = Intent( + Intent.ACTION_VIEW, + "https://bevel-beetle-a49.notion.site/2f638a539ac5801aa872e99ec4282f28".toUri() // ← 약관 URL + ) + startActivity(intent) + } - // 폰트를 적용할 텍스트 ("개인정보 수집 및 이용") - val target2 = "개인정보 수집 및 이용" - val start2 = fullText.indexOf(target2) - if (start2 >= 0) { - val end2 = start2 + target2.length - spannableString.setSpan(CustomTypefaceSpan(semiBoldTypeface), start2, end2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) - } + override fun updateDrawState(ds: TextPaint) { + ds.isUnderlineText = false + } + }, termStart, termEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) + } + + // ================= 개인정보 ================= + val privacyText = "개인정보 수집 및 이용" + val privacyStart = fullText.indexOf(privacyText) + if (privacyStart >= 0) { + val privacyEnd = privacyStart + privacyText.length + + spannableString.setSpan( + CustomTypefaceSpan(semiBoldTypeface), + privacyStart, privacyEnd, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE + ) + + spannableString.setSpan(object : ClickableSpan() { + override fun onClick(widget: View) { + val intent = Intent( + Intent.ACTION_VIEW, + "https://bevel-beetle-a49.notion.site/2f638a539ac58059b9a1c883ad7d7164".toUri() // ← 개인정보 URL + ) + startActivity(intent) + } - // 완성된 SpannableString을 TextView에 적용 - binding.tvStartGuide.text = spannableString + override fun updateDrawState(ds: TextPaint) { + ds.isUnderlineText = false + } + }, privacyStart, privacyEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) } + binding.tvStartGuide.text = spannableString + binding.tvStartGuide.movementMethod = LinkMovementMethod.getInstance() + binding.tvStartGuide.highlightColor = Color.TRANSPARENT +} + //모든 API 레벨에서 커스텀 폰트를 적용하기 위한 Span 클래스 private class CustomTypefaceSpan(private val typeface: Typeface) : MetricAffectingSpan() { override fun updateDrawState(ds: TextPaint) { diff --git a/app/src/main/res/layout/fragment_account.xml b/app/src/main/res/layout/fragment_account.xml index ce48ed7e..131bef7f 100644 --- a/app/src/main/res/layout/fragment_account.xml +++ b/app/src/main/res/layout/fragment_account.xml @@ -408,6 +408,7 @@ app:layout_constraintEnd_toEndOf="@id/gd_end">