회원가입, 로그인 페이지 추가
|
@ -112,5 +112,7 @@ dependencies {
|
|||
// permission
|
||||
implementation "io.github.ParkSangGwon:tedpermission-normal:3.3.0"
|
||||
|
||||
implementation 'com.github.dhaval2404:imagepicker:2.1'
|
||||
|
||||
implementation 'com.google.android.gms:play-services-oss-licenses:17.0.1'
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application
|
||||
android:name=".app.SodaLiveApp"
|
||||
android:allowBackup="true"
|
||||
|
@ -11,7 +13,6 @@
|
|||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:largeHeap="true"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.SodaLive"
|
||||
android:usesCleartextTraffic="true"
|
||||
|
@ -26,6 +27,10 @@
|
|||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name=".main.MainActivity" />
|
||||
<activity android:name=".user.login.LoginActivity" />
|
||||
<activity android:name=".user.signup.SignUpActivity" />
|
||||
<activity android:name=".settings.TermsActivity" />
|
||||
<activity android:name=".user.find_password.FindPasswordActivity" />
|
||||
|
||||
<activity
|
||||
android:name="com.google.android.gms.oss.licenses.OssLicensesMenuActivity"
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
package kr.co.vividnext.sodalive.common
|
||||
|
||||
import retrofit2.Retrofit
|
||||
|
||||
class ApiBuilder {
|
||||
fun <T> build(retrofit: Retrofit, service: Class<T>): T {
|
||||
return retrofit.create(service)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package kr.co.vividnext.sodalive.common
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class ApiResponse<T>(
|
||||
@SerializedName("success") val success: Boolean,
|
||||
@SerializedName("data") val data: T? = null,
|
||||
@SerializedName("message") val message: String? = null,
|
||||
@SerializedName("errorProperty") val errorProperty: String? = null
|
||||
)
|
|
@ -1,4 +1,12 @@
|
|||
package kr.co.vividnext.sodalive.common
|
||||
|
||||
object Constants {
|
||||
const val PREF_TOKEN = "pref_token"
|
||||
const val PREF_EMAIL = "pref_email"
|
||||
const val PREF_USER_ID = "pref_user_id"
|
||||
const val PREF_NICKNAME = "pref_nickname"
|
||||
const val PREF_PROFILE_IMAGE = "pref_profile_image"
|
||||
|
||||
const val EXTRA_DATA = "extra_data"
|
||||
const val EXTRA_TERMS = "extra_terms"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
package kr.co.vividnext.sodalive.common
|
||||
|
||||
import android.app.Activity
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.AnimationDrawable
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.view.LayoutInflater
|
||||
import android.view.WindowManager
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import kr.co.vividnext.sodalive.databinding.DialogLoadingBinding
|
||||
import kr.co.vividnext.sodalive.extensions.dpToPx
|
||||
|
||||
class LoadingDialog(
|
||||
activity: Activity,
|
||||
layoutInflater: LayoutInflater
|
||||
) {
|
||||
private val alertDialog: AlertDialog
|
||||
private val dialogView = DialogLoadingBinding.inflate(layoutInflater)
|
||||
private val animationDrawable: AnimationDrawable
|
||||
|
||||
init {
|
||||
val dialogBuilder = AlertDialog.Builder(activity)
|
||||
dialogBuilder.setView(dialogView.root)
|
||||
|
||||
animationDrawable = dialogView.tvLoading.compoundDrawables[1] as AnimationDrawable
|
||||
|
||||
alertDialog = dialogBuilder.create()
|
||||
alertDialog.setCancelable(false)
|
||||
alertDialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
||||
}
|
||||
|
||||
fun show(width: Int, message: String = "") {
|
||||
alertDialog.show()
|
||||
animationDrawable.start()
|
||||
dialogView.tvLoading.text = message
|
||||
|
||||
val lp = WindowManager.LayoutParams()
|
||||
lp.copyFrom(alertDialog.window?.attributes)
|
||||
lp.width = width - (26.7f.dpToPx()).toInt()
|
||||
lp.height = WindowManager.LayoutParams.WRAP_CONTENT
|
||||
|
||||
alertDialog.window?.attributes = lp
|
||||
}
|
||||
|
||||
fun dismiss() {
|
||||
animationDrawable.stop()
|
||||
alertDialog.dismiss()
|
||||
}
|
||||
}
|
|
@ -43,4 +43,34 @@ object SharedPreferenceManager {
|
|||
else -> throw UnsupportedOperationException("Error")
|
||||
}
|
||||
}
|
||||
|
||||
var token: String
|
||||
get() = sharedPreferences[Constants.PREF_TOKEN, ""]
|
||||
set(value) {
|
||||
sharedPreferences[Constants.PREF_TOKEN] = value
|
||||
}
|
||||
|
||||
var userId: Long
|
||||
get() = sharedPreferences[Constants.PREF_USER_ID, 0]
|
||||
set(value) {
|
||||
sharedPreferences[Constants.PREF_USER_ID] = value
|
||||
}
|
||||
|
||||
var nickname: String
|
||||
get() = sharedPreferences[Constants.PREF_NICKNAME, ""]
|
||||
set(value) {
|
||||
sharedPreferences[Constants.PREF_NICKNAME] = value
|
||||
}
|
||||
|
||||
var email: String
|
||||
get() = sharedPreferences[Constants.PREF_EMAIL, ""]
|
||||
set(value) {
|
||||
sharedPreferences[Constants.PREF_EMAIL] = value
|
||||
}
|
||||
|
||||
var profileImage: String
|
||||
get() = sharedPreferences[Constants.PREF_PROFILE_IMAGE, ""]
|
||||
set(value) {
|
||||
sharedPreferences[Constants.PREF_PROFILE_IMAGE] = value
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,10 +3,20 @@ package kr.co.vividnext.sodalive.di
|
|||
import android.content.Context
|
||||
import com.google.gson.GsonBuilder
|
||||
import kr.co.vividnext.sodalive.BuildConfig
|
||||
import kr.co.vividnext.sodalive.common.ApiBuilder
|
||||
import kr.co.vividnext.sodalive.network.TokenAuthenticator
|
||||
import kr.co.vividnext.sodalive.settings.TermsApi
|
||||
import kr.co.vividnext.sodalive.settings.TermsRepository
|
||||
import kr.co.vividnext.sodalive.settings.TermsViewModel
|
||||
import kr.co.vividnext.sodalive.user.UserApi
|
||||
import kr.co.vividnext.sodalive.user.UserRepository
|
||||
import kr.co.vividnext.sodalive.user.find_password.FindPasswordViewModel
|
||||
import kr.co.vividnext.sodalive.user.login.LoginViewModel
|
||||
import kr.co.vividnext.sodalive.user.signup.SignUpViewModel
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.logging.HttpLoggingInterceptor
|
||||
import org.koin.android.ext.koin.androidContext
|
||||
import org.koin.androidx.viewmodel.dsl.viewModel
|
||||
import org.koin.core.context.startKoin
|
||||
import org.koin.dsl.module
|
||||
import retrofit2.Retrofit
|
||||
|
@ -44,11 +54,22 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
|
|||
.client(get())
|
||||
.build()
|
||||
}
|
||||
|
||||
single { ApiBuilder().build(get(), UserApi::class.java) }
|
||||
single { ApiBuilder().build(get(), TermsApi::class.java) }
|
||||
}
|
||||
|
||||
private val viewModelModule = module {}
|
||||
private val viewModelModule = module {
|
||||
viewModel { LoginViewModel(get()) }
|
||||
viewModel { SignUpViewModel(get()) }
|
||||
viewModel { TermsViewModel(get()) }
|
||||
viewModel { FindPasswordViewModel(get()) }
|
||||
}
|
||||
|
||||
private val repositoryModule = module {}
|
||||
private val repositoryModule = module {
|
||||
factory { UserRepository(get()) }
|
||||
factory { TermsRepository(get()) }
|
||||
}
|
||||
|
||||
private val moduleList = listOf(
|
||||
networkModule,
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package kr.co.vividnext.sodalive.extensions
|
||||
|
||||
import android.content.res.Resources
|
||||
import android.util.DisplayMetrics
|
||||
import java.text.DecimalFormat
|
||||
|
||||
fun Float.dpToPx(): Float {
|
||||
val metrics = Resources.getSystem().displayMetrics
|
||||
return this * (metrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT)
|
||||
}
|
||||
|
||||
fun Int.dpToPx(): Float {
|
||||
val metrics = Resources.getSystem().displayMetrics
|
||||
return this.toFloat() * (metrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT)
|
||||
}
|
||||
|
||||
fun Int.moneyFormat(): String = DecimalFormat("###,###").format(this)
|
||||
fun Long.moneyFormat(): String = DecimalFormat("###,###").format(this)
|
|
@ -0,0 +1,8 @@
|
|||
package kr.co.vividnext.sodalive.settings
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class GetTermsResponse(
|
||||
@SerializedName("title") val title: String,
|
||||
@SerializedName("description") val description: String
|
||||
)
|
|
@ -0,0 +1,84 @@
|
|||
package kr.co.vividnext.sodalive.settings
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
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.common.LoadingDialog
|
||||
import kr.co.vividnext.sodalive.databinding.ActivityTermsBinding
|
||||
import org.koin.android.ext.android.inject
|
||||
|
||||
class TermsActivity : BaseActivity<ActivityTermsBinding>(ActivityTermsBinding::inflate) {
|
||||
|
||||
private val viewModel: TermsViewModel by inject()
|
||||
|
||||
private lateinit var loadingDialog: LoadingDialog
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
bindData()
|
||||
|
||||
val terms = intent.getStringExtra(Constants.EXTRA_TERMS) ?: "terms"
|
||||
if (terms == "privacy") {
|
||||
viewModel.getPrivacyPolicy()
|
||||
} else {
|
||||
viewModel.getTermsOfService()
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
override fun setupView() {
|
||||
if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
|
||||
WebSettingsCompat.setForceDark(
|
||||
binding.webView.settings,
|
||||
WebSettingsCompat.FORCE_DARK_ON
|
||||
)
|
||||
}
|
||||
|
||||
loadingDialog = LoadingDialog(this, layoutInflater)
|
||||
binding.toolbar.tvBack.setOnClickListener { finish() }
|
||||
|
||||
binding.webView.settings.apply {
|
||||
javaScriptEnabled = false // 자바스크립트 실행 허용
|
||||
javaScriptCanOpenWindowsAutomatically = false // 자바스크립트에서 새창 실 행 허용
|
||||
setSupportMultipleWindows(false) // 새 창 실행 허용
|
||||
loadWithOverviewMode = true // 메타 태그 허용
|
||||
|
||||
useWideViewPort = true // 화면 사이즈 맞추기 허용
|
||||
setSupportZoom(false) // 화면 줌 허용
|
||||
builtInZoomControls = false // 화면 확대 축소 허용 여부
|
||||
}
|
||||
}
|
||||
|
||||
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() }
|
||||
}
|
||||
|
||||
viewModel.titleLiveData.observe(this) {
|
||||
binding.toolbar.tvBack.text = it
|
||||
}
|
||||
|
||||
viewModel.termsLiveData.observe(this) {
|
||||
val viewPort =
|
||||
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=0.8\">"
|
||||
val data = viewPort + it
|
||||
binding.webView.loadData(
|
||||
data,
|
||||
"text/html; charset=utf-8",
|
||||
"utf-8"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package kr.co.vividnext.sodalive.settings
|
||||
|
||||
import io.reactivex.rxjava3.core.Single
|
||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||
import retrofit2.http.GET
|
||||
|
||||
interface TermsApi {
|
||||
@GET("/stplat/terms_of_service")
|
||||
fun getTermsOfService(): Single<ApiResponse<GetTermsResponse>>
|
||||
|
||||
@GET("/stplat/privacy_policy")
|
||||
fun getPrivacyPolicy(): Single<ApiResponse<GetTermsResponse>>
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package kr.co.vividnext.sodalive.settings
|
||||
|
||||
class TermsRepository(private val api: TermsApi) {
|
||||
fun getTermsOfService() = api.getTermsOfService()
|
||||
fun getPrivacyPolicy() = api.getPrivacyPolicy()
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
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
|
||||
|
||||
class TermsViewModel(private val repository: TermsRepository) : BaseViewModel() {
|
||||
private val _titleLiveData = MutableLiveData<String>()
|
||||
val titleLiveData: LiveData<String>
|
||||
get() = _titleLiveData
|
||||
|
||||
private val _termsLiveData = MutableLiveData<String>()
|
||||
val termsLiveData: LiveData<String>
|
||||
get() = _termsLiveData
|
||||
|
||||
private val _toastLiveData = MutableLiveData<String?>()
|
||||
val toastLiveData: LiveData<String?>
|
||||
get() = _toastLiveData
|
||||
|
||||
private var _isLoading = MutableLiveData(false)
|
||||
val isLoading: LiveData<Boolean>
|
||||
get() = _isLoading
|
||||
|
||||
fun getTermsOfService() {
|
||||
_isLoading.value = true
|
||||
|
||||
compositeDisposable.add(
|
||||
repository.getTermsOfService()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{
|
||||
_isLoading.value = false
|
||||
|
||||
if (it.success && it.data != null) {
|
||||
_titleLiveData.postValue(it.data.title)
|
||||
_termsLiveData.postValue(it.data.description)
|
||||
} else {
|
||||
if (it.message != null) {
|
||||
_toastLiveData.postValue(it.message)
|
||||
} else {
|
||||
_toastLiveData.postValue(
|
||||
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
_isLoading.value = false
|
||||
it.message?.let { message -> Logger.e(message) }
|
||||
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun getPrivacyPolicy() {
|
||||
_isLoading.value = true
|
||||
|
||||
compositeDisposable.add(
|
||||
repository.getPrivacyPolicy()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{
|
||||
_isLoading.value = false
|
||||
|
||||
if (it.success && it.data != null) {
|
||||
_titleLiveData.postValue(it.data.title)
|
||||
_termsLiveData.postValue(it.data.description)
|
||||
} else {
|
||||
if (it.message != null) {
|
||||
_toastLiveData.postValue(it.message)
|
||||
} else {
|
||||
_toastLiveData.postValue(
|
||||
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
_isLoading.value = false
|
||||
it.message?.let { message -> Logger.e(message) }
|
||||
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
|
@ -6,8 +6,10 @@ import android.os.Bundle
|
|||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import kr.co.vividnext.sodalive.base.BaseActivity
|
||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||
import kr.co.vividnext.sodalive.databinding.ActivitySplashBinding
|
||||
import kr.co.vividnext.sodalive.main.MainActivity
|
||||
import kr.co.vividnext.sodalive.user.login.LoginActivity
|
||||
|
||||
@SuppressLint("CustomSplashScreen")
|
||||
class SplashActivity : BaseActivity<ActivitySplashBinding>(ActivitySplashBinding::inflate) {
|
||||
|
@ -17,6 +19,14 @@ class SplashActivity : BaseActivity<ActivitySplashBinding>(ActivitySplashBinding
|
|||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
if (SharedPreferenceManager.token.isBlank()) {
|
||||
showLoginActivity()
|
||||
} else {
|
||||
showMainActivity()
|
||||
}
|
||||
}
|
||||
|
||||
private fun showMainActivity() {
|
||||
handler.postDelayed({
|
||||
startActivity(
|
||||
Intent(applicationContext, MainActivity::class.java).apply {
|
||||
|
@ -28,5 +38,17 @@ class SplashActivity : BaseActivity<ActivitySplashBinding>(ActivitySplashBinding
|
|||
}, 500)
|
||||
}
|
||||
|
||||
private fun showLoginActivity() {
|
||||
handler.postDelayed({
|
||||
startActivity(
|
||||
Intent(applicationContext, LoginActivity::class.java).apply {
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
||||
}
|
||||
)
|
||||
finish()
|
||||
}, 500)
|
||||
}
|
||||
|
||||
override fun setupView() {}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package kr.co.vividnext.sodalive.user
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
enum class Gender {
|
||||
@SerializedName("MALE")
|
||||
MALE,
|
||||
@SerializedName("FEMALE")
|
||||
FEMALE,
|
||||
@SerializedName("NONE")
|
||||
NONE
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package kr.co.vividnext.sodalive.user
|
||||
|
||||
import io.reactivex.rxjava3.core.Single
|
||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||
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
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.RequestBody
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.Multipart
|
||||
import retrofit2.http.POST
|
||||
import retrofit2.http.Part
|
||||
|
||||
interface UserApi {
|
||||
@POST("/member/login")
|
||||
fun login(@Body request: LoginRequest): Single<ApiResponse<LoginResponse>>
|
||||
|
||||
@POST("/member/signup")
|
||||
@Multipart
|
||||
fun signUp(
|
||||
@Part profileImage: MultipartBody.Part?,
|
||||
@Part("request") request: RequestBody
|
||||
): Single<ApiResponse<LoginResponse>>
|
||||
|
||||
@POST("/member/forgot-password")
|
||||
fun findPassword(@Body request: ForgotPasswordRequest): Single<ApiResponse<Any>>
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package kr.co.vividnext.sodalive.user
|
||||
|
||||
import kr.co.vividnext.sodalive.user.find_password.ForgotPasswordRequest
|
||||
import kr.co.vividnext.sodalive.user.login.LoginRequest
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.RequestBody
|
||||
|
||||
class UserRepository(private val userApi: UserApi) {
|
||||
fun login(request: LoginRequest) = userApi.login(request)
|
||||
|
||||
fun signUp(profileImage: MultipartBody.Part?, request: RequestBody) = userApi.signUp(
|
||||
profileImage,
|
||||
request
|
||||
)
|
||||
|
||||
fun findPassword(request: ForgotPasswordRequest) = userApi.findPassword(request = request)
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
package kr.co.vividnext.sodalive.user.find_password
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
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.ActivityFindPasswordBinding
|
||||
import org.koin.android.ext.android.inject
|
||||
|
||||
class FindPasswordActivity : BaseActivity<ActivityFindPasswordBinding>(
|
||||
ActivityFindPasswordBinding::inflate
|
||||
) {
|
||||
|
||||
private val viewModel: FindPasswordViewModel by inject()
|
||||
|
||||
private lateinit var loadingDialog: LoadingDialog
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
bindData()
|
||||
}
|
||||
|
||||
override fun setupView() {
|
||||
loadingDialog = LoadingDialog(this, layoutInflater)
|
||||
binding.toolbar.tvBack.text = "비밀번호 재설정"
|
||||
binding.toolbar.tvBack.setOnClickListener { finish() }
|
||||
|
||||
binding.tvServiceCenter.setOnClickListener {
|
||||
startActivity(
|
||||
Intent(
|
||||
Intent.ACTION_VIEW,
|
||||
Uri.parse("http://pf.kakao.com/_sZaeb")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
binding.tvFindPassword.setOnClickListener { viewModel.findPassword { finish() } }
|
||||
}
|
||||
|
||||
private fun bindData() {
|
||||
compositeDisposable.add(
|
||||
binding.etEmail.textChanges().skip(1)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
viewModel.email = it.toString()
|
||||
}
|
||||
)
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package kr.co.vividnext.sodalive.user.find_password
|
||||
|
||||
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.user.UserRepository
|
||||
|
||||
class FindPasswordViewModel(private val repository: UserRepository) : BaseViewModel() {
|
||||
|
||||
var email = ""
|
||||
|
||||
private var _isLoading = MutableLiveData(false)
|
||||
val isLoading: LiveData<Boolean>
|
||||
get() = _isLoading
|
||||
|
||||
private val _toastLiveData = MutableLiveData<String?>()
|
||||
val toastLiveData: LiveData<String?>
|
||||
get() = _toastLiveData
|
||||
|
||||
fun findPassword(onSuccess: () -> Unit) {
|
||||
if (email.isBlank()) {
|
||||
_toastLiveData.postValue("이메일을 입력하세요.")
|
||||
return
|
||||
}
|
||||
|
||||
_isLoading.value = true
|
||||
val request = ForgotPasswordRequest(email = email)
|
||||
compositeDisposable.add(
|
||||
repository.findPassword(request)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{
|
||||
_isLoading.value = false
|
||||
if (it.success) {
|
||||
_toastLiveData.postValue(
|
||||
"임시 비밀번호가 입력하신 이메일로 발송되었습니다. 이메일을 확인해 주세요."
|
||||
)
|
||||
onSuccess()
|
||||
} else {
|
||||
if (it.message != null) {
|
||||
_toastLiveData.postValue(it.message)
|
||||
} else {
|
||||
_toastLiveData.postValue(
|
||||
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
_isLoading.value = false
|
||||
it.message?.let { message -> Logger.e(message) }
|
||||
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package kr.co.vividnext.sodalive.user.find_password
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class ForgotPasswordRequest(@SerializedName("email") val email: String)
|
|
@ -0,0 +1,123 @@
|
|||
package kr.co.vividnext.sodalive.user.login
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.text.InputType
|
||||
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.Constants
|
||||
import kr.co.vividnext.sodalive.common.LoadingDialog
|
||||
import kr.co.vividnext.sodalive.databinding.ActivityLoginBinding
|
||||
import kr.co.vividnext.sodalive.main.MainActivity
|
||||
import kr.co.vividnext.sodalive.user.find_password.FindPasswordActivity
|
||||
import kr.co.vividnext.sodalive.user.signup.SignUpActivity
|
||||
import org.koin.android.ext.android.inject
|
||||
|
||||
class LoginActivity : BaseActivity<ActivityLoginBinding>(ActivityLoginBinding::inflate) {
|
||||
|
||||
private val viewModel: LoginViewModel by inject()
|
||||
|
||||
private lateinit var loadingDialog: LoadingDialog
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
bindData()
|
||||
}
|
||||
|
||||
override fun setupView() {
|
||||
binding.tvToolbar.text = "로그인"
|
||||
loadingDialog = LoadingDialog(this, layoutInflater)
|
||||
|
||||
binding.tvLogin.setOnClickListener {
|
||||
viewModel.login {
|
||||
it?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show() }
|
||||
finishAffinity()
|
||||
val nextIntent = Intent(applicationContext, MainActivity::class.java)
|
||||
val extras = intent.getBundleExtra(Constants.EXTRA_DATA)
|
||||
?: if (intent.extras != null) {
|
||||
intent.extras
|
||||
} else {
|
||||
null
|
||||
}
|
||||
if (extras != null) {
|
||||
nextIntent.putExtra(Constants.EXTRA_DATA, extras)
|
||||
}
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
||||
startActivity(nextIntent)
|
||||
}
|
||||
}
|
||||
|
||||
binding.tvSignUp.setOnClickListener {
|
||||
val nextIntent = Intent(applicationContext, SignUpActivity::class.java)
|
||||
val extras = intent.getBundleExtra(Constants.EXTRA_DATA)
|
||||
?: if (intent.extras != null) {
|
||||
intent.extras
|
||||
} else {
|
||||
null
|
||||
}
|
||||
if (extras != null) {
|
||||
nextIntent.putExtra(Constants.EXTRA_DATA, extras)
|
||||
}
|
||||
startActivity(nextIntent)
|
||||
}
|
||||
|
||||
binding.tvForgotPassword.setOnClickListener {
|
||||
startActivity(
|
||||
Intent(
|
||||
applicationContext,
|
||||
FindPasswordActivity::class.java
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
binding.tvVisiblePassword.setOnClickListener { viewModel.onClickVisiblePassword() }
|
||||
}
|
||||
|
||||
private fun bindData() {
|
||||
compositeDisposable.add(
|
||||
binding.etEmail.textChanges().skip(1)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
viewModel.email = it.toString()
|
||||
}
|
||||
)
|
||||
|
||||
compositeDisposable.add(
|
||||
binding.etPassword.textChanges().skip(1)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
viewModel.password = it.toString()
|
||||
}
|
||||
)
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.visiblePasswordLiveData.observe(this) {
|
||||
binding.tvVisiblePassword.isSelected = it
|
||||
if (it) {
|
||||
binding.etPassword.inputType =
|
||||
InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
|
||||
} else {
|
||||
binding.etPassword.inputType =
|
||||
InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package kr.co.vividnext.sodalive.user.login
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class LoginRequest(
|
||||
@SerializedName("email") val email: String,
|
||||
@SerializedName("password") val password: String
|
||||
)
|
|
@ -0,0 +1,11 @@
|
|||
package kr.co.vividnext.sodalive.user.login
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class LoginResponse(
|
||||
@SerializedName("userId") val userId: Long,
|
||||
@SerializedName("token") val token: String,
|
||||
@SerializedName("nickname") val nickname: String,
|
||||
@SerializedName("email") val email: String,
|
||||
@SerializedName("profileImage") val profileImage: String
|
||||
)
|
|
@ -0,0 +1,78 @@
|
|||
package kr.co.vividnext.sodalive.user.login
|
||||
|
||||
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 LoginViewModel(private val repository: UserRepository) : BaseViewModel() {
|
||||
|
||||
var email = ""
|
||||
var password = ""
|
||||
|
||||
private val _visiblePasswordLiveData = MutableLiveData(false)
|
||||
val visiblePasswordLiveData: LiveData<Boolean>
|
||||
get() = _visiblePasswordLiveData
|
||||
|
||||
private val _toastLiveData = MutableLiveData<String?>()
|
||||
val toastLiveData: LiveData<String?>
|
||||
get() = _toastLiveData
|
||||
|
||||
private var _isLoading = MutableLiveData(false)
|
||||
val isLoading: LiveData<Boolean>
|
||||
get() = _isLoading
|
||||
|
||||
fun login(onSuccess: (String?) -> Unit) {
|
||||
if (email.isBlank()) {
|
||||
_toastLiveData.postValue("이메일을 입력하세요.")
|
||||
return
|
||||
}
|
||||
|
||||
if (password.isBlank()) {
|
||||
_toastLiveData.postValue("비밃번호를 입력하세요.")
|
||||
return
|
||||
}
|
||||
|
||||
_isLoading.value = true
|
||||
val request = LoginRequest(email, password)
|
||||
compositeDisposable.add(
|
||||
repository.login(request)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{
|
||||
_isLoading.value = false
|
||||
if (it.success && it.data != null) {
|
||||
SharedPreferenceManager.token = it.data.token
|
||||
SharedPreferenceManager.email = it.data.email
|
||||
SharedPreferenceManager.userId = it.data.userId
|
||||
SharedPreferenceManager.nickname = it.data.nickname
|
||||
SharedPreferenceManager.profileImage = it.data.profileImage
|
||||
onSuccess(it.message)
|
||||
} else {
|
||||
if (it.message != null) {
|
||||
_toastLiveData.postValue(it.message)
|
||||
} else {
|
||||
_toastLiveData.postValue(
|
||||
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
_isLoading.value = false
|
||||
it.message?.let { message -> Logger.e(message) }
|
||||
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun onClickVisiblePassword() {
|
||||
_visiblePasswordLiveData.postValue(!_visiblePasswordLiveData.value!!)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,254 @@
|
|||
package kr.co.vividnext.sodalive.user.signup
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import coil.load
|
||||
import coil.transform.RoundedCornersTransformation
|
||||
import com.github.dhaval2404.imagepicker.ImagePicker
|
||||
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.Constants
|
||||
import kr.co.vividnext.sodalive.common.LoadingDialog
|
||||
import kr.co.vividnext.sodalive.common.RealPathUtil
|
||||
import kr.co.vividnext.sodalive.databinding.ActivitySignupBinding
|
||||
import kr.co.vividnext.sodalive.extensions.dpToPx
|
||||
import kr.co.vividnext.sodalive.main.MainActivity
|
||||
import kr.co.vividnext.sodalive.settings.TermsActivity
|
||||
import kr.co.vividnext.sodalive.user.Gender
|
||||
import org.koin.android.ext.android.inject
|
||||
|
||||
class SignUpActivity : BaseActivity<ActivitySignupBinding>(ActivitySignupBinding::inflate) {
|
||||
|
||||
private val viewModel: SignUpViewModel by inject()
|
||||
private val onBackPressedCallback = object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
onClickBackButton()
|
||||
}
|
||||
}
|
||||
|
||||
private lateinit var loadingDialog: LoadingDialog
|
||||
|
||||
private val imageResult =
|
||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
val resultCode = result.resultCode
|
||||
val data = result.data
|
||||
|
||||
if (resultCode == RESULT_OK) {
|
||||
// Image Uri will not be null for RESULT_OK
|
||||
val fileUri = data?.data!!
|
||||
binding.ivProfile.background = null
|
||||
binding.ivProfile.load(fileUri) {
|
||||
crossfade(true)
|
||||
transformations(RoundedCornersTransformation(13.3f.dpToPx()))
|
||||
}
|
||||
viewModel.profileImageUri = fileUri
|
||||
} else if (resultCode == ImagePicker.RESULT_ERROR) {
|
||||
Toast.makeText(this, ImagePicker.getError(data), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
onBackPressedDispatcher.addCallback(this, onBackPressedCallback)
|
||||
|
||||
viewModel.getRealPathFromURI = {
|
||||
RealPathUtil.getRealPath(applicationContext, it)
|
||||
}
|
||||
|
||||
bindData()
|
||||
}
|
||||
|
||||
override fun setupView() {
|
||||
binding.toolbar.tvBack.text = "회원가입"
|
||||
binding.toolbar.tvBack.setOnClickListener { onClickBackButton() }
|
||||
|
||||
loadingDialog = LoadingDialog(this, layoutInflater)
|
||||
|
||||
binding.ivPhotoPicker.setOnClickListener {
|
||||
ImagePicker.with(this)
|
||||
.crop()
|
||||
.galleryOnly()
|
||||
.galleryMimeTypes( // Exclude gif images
|
||||
mimeTypes = arrayOf(
|
||||
"image/png",
|
||||
"image/jpg",
|
||||
"image/jpeg"
|
||||
)
|
||||
)
|
||||
.createIntent { imageResult.launch(it) }
|
||||
}
|
||||
|
||||
binding.tvMale.setOnClickListener {
|
||||
viewModel.changeGender(Gender.MALE)
|
||||
}
|
||||
|
||||
binding.tvFemale.setOnClickListener {
|
||||
viewModel.changeGender(Gender.FEMALE)
|
||||
}
|
||||
|
||||
binding.tvNone.setOnClickListener {
|
||||
viewModel.changeGender(Gender.NONE)
|
||||
}
|
||||
|
||||
binding.tvTermsOfService.setOnClickListener {
|
||||
val intent = Intent(applicationContext, TermsActivity::class.java)
|
||||
intent.putExtra("terms", "terms")
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
binding.tvPrivacyPolicy.setOnClickListener {
|
||||
val intent = Intent(applicationContext, TermsActivity::class.java)
|
||||
intent.putExtra("terms", "privacy")
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
binding.ivTermsOfService.setOnClickListener {
|
||||
viewModel.onClickCheckboxTermsOfService()
|
||||
}
|
||||
|
||||
binding.ivPrivacyPolicy.setOnClickListener {
|
||||
viewModel.onClickCheckboxPrivacyPolicy()
|
||||
}
|
||||
|
||||
binding.tvSignUp.setOnClickListener {
|
||||
viewModel.signUp {
|
||||
it?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show() }
|
||||
finishAffinity()
|
||||
val nextIntent = Intent(applicationContext, MainActivity::class.java)
|
||||
val extras = intent.getBundleExtra(Constants.EXTRA_DATA)
|
||||
?: if (intent.extras != null) {
|
||||
intent.extras
|
||||
} else {
|
||||
null
|
||||
}
|
||||
if (extras != null) {
|
||||
nextIntent.putExtra(Constants.EXTRA_DATA, extras)
|
||||
}
|
||||
startActivity(nextIntent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindData() {
|
||||
compositeDisposable.add(
|
||||
binding.etEmail.textChanges().skip(1)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
viewModel.email = it.toString()
|
||||
}
|
||||
)
|
||||
|
||||
compositeDisposable.add(
|
||||
binding.etPassword.textChanges().skip(1)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
viewModel.password = it.toString()
|
||||
}
|
||||
)
|
||||
|
||||
compositeDisposable.add(
|
||||
binding.etPasswordRe.textChanges().skip(1)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
viewModel.passwordRe = it.toString()
|
||||
}
|
||||
)
|
||||
|
||||
compositeDisposable.add(
|
||||
binding.etNickname.textChanges().skip(1)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
viewModel.nickname = it.toString()
|
||||
}
|
||||
)
|
||||
|
||||
viewModel.isAgreeTermsOfServiceLiveData.observe(this) {
|
||||
binding.ivTermsOfService.isSelected = it
|
||||
}
|
||||
|
||||
viewModel.isAgreePrivacyPolicyLiveData.observe(this) {
|
||||
binding.ivPrivacyPolicy.isSelected = it
|
||||
}
|
||||
|
||||
viewModel.genderLiveData.observe(this) {
|
||||
binding.tvMale.isSelected = false
|
||||
binding.tvFemale.isSelected = false
|
||||
binding.tvNone.isSelected = false
|
||||
|
||||
when (it) {
|
||||
Gender.MALE -> binding.tvMale.isSelected = true
|
||||
Gender.FEMALE -> binding.tvFemale.isSelected = true
|
||||
Gender.NONE -> binding.tvNone.isSelected = true
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.signUpErrorLiveData.observe(this) {
|
||||
Toast.makeText(applicationContext, it.message, Toast.LENGTH_LONG).show()
|
||||
|
||||
when (it.errorProperty) {
|
||||
"email" -> {
|
||||
viewModel.setStep(step = SignUpViewModel.EmailSignUpStep.STEP_1)
|
||||
binding.etEmail.error = it.message
|
||||
binding.etEmail.requestFocus()
|
||||
}
|
||||
|
||||
"password" -> {
|
||||
viewModel.setStep(step = SignUpViewModel.EmailSignUpStep.STEP_1)
|
||||
binding.etPassword.error = it.message
|
||||
binding.etPassword.requestFocus()
|
||||
}
|
||||
|
||||
"nickname" -> {
|
||||
binding.etNickname.error = it.message
|
||||
binding.etNickname.requestFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.stepLiveData.observe(this) {
|
||||
if (it == SignUpViewModel.EmailSignUpStep.STEP_2) {
|
||||
binding.toolbar.tvBack.text = "프로필 설정"
|
||||
binding.tvSignUp.text = "회원가입"
|
||||
binding.llStep1.visibility = View.GONE
|
||||
binding.llStep2.visibility = View.VISIBLE
|
||||
} else {
|
||||
binding.toolbar.tvBack.text = "회원가입"
|
||||
binding.tvSignUp.text = "다음"
|
||||
binding.llStep1.visibility = View.VISIBLE
|
||||
binding.llStep2.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onClickBackButton() {
|
||||
if (viewModel.stepLiveData.value!! == SignUpViewModel.EmailSignUpStep.STEP_2) {
|
||||
viewModel.setStep(SignUpViewModel.EmailSignUpStep.STEP_1)
|
||||
} else {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package kr.co.vividnext.sodalive.user.signup
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class SignUpError(
|
||||
@SerializedName("errorProperty") val errorProperty: String,
|
||||
@SerializedName("message") val message: String
|
||||
)
|
|
@ -0,0 +1,14 @@
|
|||
package kr.co.vividnext.sodalive.user.signup
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kr.co.vividnext.sodalive.user.Gender
|
||||
|
||||
data class SignUpRequest(
|
||||
@SerializedName("email") val email: String,
|
||||
@SerializedName("password") val password: String,
|
||||
@SerializedName("nickname") val nickname: String,
|
||||
@SerializedName("gender") val gender: Gender,
|
||||
@SerializedName("isAgreeTermsOfService") val isAgreeTermsOfService: Boolean,
|
||||
@SerializedName("isAgreePrivacyPolicy") val isAgreePrivacyPolicy: Boolean,
|
||||
@SerializedName("container") val container: String = "aos"
|
||||
)
|
|
@ -0,0 +1,241 @@
|
|||
package kr.co.vividnext.sodalive.user.signup
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.core.util.PatternsCompat
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.annotations.SerializedName
|
||||
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.Gender
|
||||
import kr.co.vividnext.sodalive.user.UserRepository
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.RequestBody.Companion.asRequestBody
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import java.io.File
|
||||
|
||||
class SignUpViewModel(private val repository: UserRepository) : BaseViewModel() {
|
||||
enum class EmailSignUpStep {
|
||||
@SerializedName("STEP_1") STEP_1,
|
||||
@SerializedName("STEP_2") STEP_2
|
||||
}
|
||||
|
||||
var email = ""
|
||||
var password = ""
|
||||
var passwordRe = ""
|
||||
var nickname = ""
|
||||
var profileImageUri: Uri? = null
|
||||
|
||||
private val _genderLiveData = MutableLiveData(Gender.NONE)
|
||||
val genderLiveData: LiveData<Gender>
|
||||
get() = _genderLiveData
|
||||
|
||||
private val _isAgreeTermsOfServiceLiveData = MutableLiveData(false)
|
||||
val isAgreeTermsOfServiceLiveData: LiveData<Boolean>
|
||||
get() = _isAgreeTermsOfServiceLiveData
|
||||
|
||||
private val _isAgreePrivacyPolicyLiveData = MutableLiveData(false)
|
||||
val isAgreePrivacyPolicyLiveData: LiveData<Boolean>
|
||||
get() = _isAgreePrivacyPolicyLiveData
|
||||
|
||||
private val _signUpErrorLiveData = MutableLiveData<SignUpError>()
|
||||
val signUpErrorLiveData: LiveData<SignUpError>
|
||||
get() = _signUpErrorLiveData
|
||||
|
||||
private val _toastLiveData = MutableLiveData<String?>()
|
||||
val toastLiveData: LiveData<String?>
|
||||
get() = _toastLiveData
|
||||
|
||||
private var _isLoading = MutableLiveData(false)
|
||||
val isLoading: LiveData<Boolean>
|
||||
get() = _isLoading
|
||||
|
||||
private val _stepLiveData = MutableLiveData(EmailSignUpStep.STEP_1)
|
||||
val stepLiveData: LiveData<EmailSignUpStep>
|
||||
get() = _stepLiveData
|
||||
|
||||
lateinit var getRealPathFromURI: (Uri) -> String?
|
||||
|
||||
fun setStep(step: EmailSignUpStep = EmailSignUpStep.STEP_2) {
|
||||
_stepLiveData.postValue(step)
|
||||
}
|
||||
|
||||
fun signUp(onSuccess: (String?) -> Unit) {
|
||||
if (stepLiveData.value!! == EmailSignUpStep.STEP_1) {
|
||||
if (validationStep1()) return
|
||||
|
||||
setStep()
|
||||
return
|
||||
}
|
||||
|
||||
if (validationStep2()) return
|
||||
|
||||
val request = SignUpRequest(
|
||||
email = email,
|
||||
password = password,
|
||||
nickname = nickname,
|
||||
gender = _genderLiveData.value!!,
|
||||
isAgreeTermsOfService = _isAgreeTermsOfServiceLiveData.value!!,
|
||||
isAgreePrivacyPolicy = _isAgreePrivacyPolicyLiveData.value!!
|
||||
)
|
||||
|
||||
val requestJson = Gson().toJson(request)
|
||||
|
||||
val profileImage = if (profileImageUri != null) {
|
||||
val file = File(getRealPathFromURI(profileImageUri!!))
|
||||
MultipartBody.Part.createFormData(
|
||||
"profileImage",
|
||||
file.name,
|
||||
file.asRequestBody("image/*".toMediaType())
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
_isLoading.value = true
|
||||
compositeDisposable.add(
|
||||
repository.signUp(
|
||||
profileImage,
|
||||
requestJson.toRequestBody("text/plain".toMediaType())
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{
|
||||
if (it.success && it.data != null) {
|
||||
SharedPreferenceManager.token = it.data.token
|
||||
SharedPreferenceManager.email = it.data.email
|
||||
SharedPreferenceManager.userId = it.data.userId
|
||||
SharedPreferenceManager.nickname = it.data.nickname
|
||||
SharedPreferenceManager.profileImage = it.data.profileImage
|
||||
_isLoading.value = false
|
||||
onSuccess(it.message)
|
||||
} else {
|
||||
_isLoading.value = false
|
||||
if (it.errorProperty != null && it.message != null) {
|
||||
_signUpErrorLiveData.postValue(
|
||||
SignUpError(it.errorProperty, it.message)
|
||||
)
|
||||
} else if (it.message != null) {
|
||||
_toastLiveData.postValue(it.message)
|
||||
} else {
|
||||
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
it.message?.let { message -> Logger.e(message) }
|
||||
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun validationStep1(): Boolean {
|
||||
if (email.isBlank()) {
|
||||
_signUpErrorLiveData.postValue(
|
||||
SignUpError(
|
||||
"email",
|
||||
"이메일을 입력하세요."
|
||||
)
|
||||
)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
if (password.isBlank()) {
|
||||
_signUpErrorLiveData.postValue(
|
||||
SignUpError(
|
||||
"password",
|
||||
"비밀번호를 입력하세요."
|
||||
)
|
||||
)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
if (password != passwordRe) {
|
||||
_signUpErrorLiveData.postValue(
|
||||
SignUpError(
|
||||
"password",
|
||||
"비밀번호가 일치하지 않습니다."
|
||||
)
|
||||
)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
if (
|
||||
!_isAgreePrivacyPolicyLiveData.value!! ||
|
||||
!_isAgreeTermsOfServiceLiveData.value!!
|
||||
) {
|
||||
_signUpErrorLiveData.postValue(
|
||||
SignUpError(
|
||||
"",
|
||||
"약관에 동의하셔야 회원가입이 가능합니다."
|
||||
)
|
||||
)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
if (!PatternsCompat.EMAIL_ADDRESS.matcher(email).matches()) {
|
||||
_signUpErrorLiveData.postValue(
|
||||
SignUpError(
|
||||
"email",
|
||||
"올바른 이메일을 입력해 주세요"
|
||||
)
|
||||
)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
if (
|
||||
"^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d$@!%*#?&]{8,}$"
|
||||
.toRegex()
|
||||
.matches(password)
|
||||
.not()
|
||||
) {
|
||||
_signUpErrorLiveData.postValue(
|
||||
SignUpError(
|
||||
"password",
|
||||
"영문, 숫자 포함 8자 이상의 비밀번호를 입력해 주세요."
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private fun validationStep2(): Boolean {
|
||||
if (nickname.isBlank() || nickname.length < 2) {
|
||||
_signUpErrorLiveData.postValue(
|
||||
SignUpError(
|
||||
"nickname",
|
||||
"닉네임은 2자 이상 입력해 주세요."
|
||||
)
|
||||
)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
fun onClickCheckboxTermsOfService() {
|
||||
_isAgreeTermsOfServiceLiveData.postValue(!_isAgreeTermsOfServiceLiveData.value!!)
|
||||
}
|
||||
|
||||
fun onClickCheckboxPrivacyPolicy() {
|
||||
_isAgreePrivacyPolicyLiveData.postValue(!_isAgreePrivacyPolicyLiveData.value!!)
|
||||
}
|
||||
|
||||
fun changeGender(gender: Gender) {
|
||||
_genderLiveData.postValue(gender)
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 6.3 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 5.1 KiB |
After Width: | Height: | Size: 4.6 KiB |
After Width: | Height: | Size: 509 B |
After Width: | Height: | Size: 668 B |
After Width: | Height: | Size: 544 B |
After Width: | Height: | Size: 680 B |
After Width: | Height: | Size: 274 B |
After Width: | Height: | Size: 479 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 55 KiB |
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/color_9970ff" />
|
||||
<corners android:radius="10dp" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/color_9970ff" />
|
||||
</shape>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/color_222222" />
|
||||
<corners android:radius="16.7dp" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/color_222222" />
|
||||
</shape>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/color_9970ff" />
|
||||
<corners android:radius="33.3dp" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/color_9970ff" />
|
||||
</shape>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/color_222222" />
|
||||
<corners android:radius="6.7dp" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/color_222222" />
|
||||
</shape>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/color_9970ff" />
|
||||
<corners android:radius="6.7dp" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/color_9970ff" />
|
||||
</shape>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@android:color/transparent" />
|
||||
<corners android:radius="8dp" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/color_9970ff" />
|
||||
</shape>
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/color_9970ff" />
|
||||
<size android:width="1.5dp" />
|
||||
</shape>
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/edittext_underline_normal" android:state_focused="false" />
|
||||
<item android:drawable="@drawable/edittext_underline_focused" android:state_focused="true" />
|
||||
</selector>
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
<item
|
||||
android:bottom="1dp"
|
||||
android:left="-2dp"
|
||||
android:right="-2dp"
|
||||
android:top="-2dp">
|
||||
<shape android:shape="rectangle" >
|
||||
<stroke
|
||||
android:width="1px"
|
||||
android:color="@color/color_9970ff" />
|
||||
|
||||
<solid android:color="#00FFFFFF" />
|
||||
|
||||
<padding
|
||||
android:bottom="5dp"
|
||||
android:left="5dp"
|
||||
android:right="5dp"
|
||||
android:top="5dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</layer-list>
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
<item
|
||||
android:bottom="1dp"
|
||||
android:left="-2dp"
|
||||
android:right="-2dp"
|
||||
android:top="-2dp">
|
||||
<shape android:shape="rectangle" >
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/color_b3909090" />
|
||||
|
||||
<solid android:color="#00FFFFFF" />
|
||||
|
||||
<padding
|
||||
android:bottom="5dp"
|
||||
android:left="5dp"
|
||||
android:right="5dp"
|
||||
android:top="5dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</layer-list>
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/btn_radio_select_normal" android:state_selected="false" />
|
||||
<item android:drawable="@drawable/btn_radio_select_selected" android:state_selected="true" />
|
||||
</selector>
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/btn_select_normal" android:state_selected="false" />
|
||||
<item android:drawable="@drawable/btn_select_checked" android:state_selected="true" />
|
||||
</selector>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<animation-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/loading_1" android:duration="500" />
|
||||
<item android:drawable="@drawable/loading_2" android:duration="500" />
|
||||
<item android:drawable="@drawable/loading_3" android:duration="500" />
|
||||
<item android:drawable="@drawable/loading_4" android:duration="500" />
|
||||
<item android:drawable="@drawable/loading_5" android:duration="500" />
|
||||
</animation-list>
|
|
@ -0,0 +1,114 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<include
|
||||
android:id="@+id/toolbar"
|
||||
layout="@layout/detail_toolbar" />
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/toolbar">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="26.7dp"
|
||||
android:layout_marginTop="40dp"
|
||||
android:fontFamily="@font/gmarket_sans_bold"
|
||||
android:gravity="center"
|
||||
android:lineSpacingExtra="6dp"
|
||||
android:text="회원가입한 이메일 주소로\n임시 비밀번호를 보내드립니다."
|
||||
android:textColor="@color/color_eeeeee"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="26.7dp"
|
||||
android:layout_marginTop="40dp"
|
||||
android:fontFamily="@font/gmarket_sans_medium"
|
||||
android:gravity="center"
|
||||
android:lineSpacingExtra="6dp"
|
||||
android:text="임시 비밀번호로 로그인 후, 마이페이지 > 프로필 설정에서\n비밀번호를 변경하고 이용하세요."
|
||||
android:textColor="@color/color_909090"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="26.7dp"
|
||||
android:layout_marginTop="40dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="6.7dp"
|
||||
android:fontFamily="@font/gmarket_sans_medium"
|
||||
android:text="이메일"
|
||||
android:textColor="@color/color_eeeeee"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_email"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/edittext_underline"
|
||||
android:fontFamily="@font/gmarket_sans_medium"
|
||||
android:hint="이메일 주소를 입력하세요"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="textWebEmailAddress"
|
||||
android:paddingHorizontal="6.7dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:textColor="@color/color_eeeeee"
|
||||
android:textColorHint="@color/color_777777"
|
||||
android:textCursorDrawable="@drawable/edit_text_cursor"
|
||||
android:textSize="13.3sp"
|
||||
android:theme="@style/EditTextStyle"
|
||||
tools:ignore="LabelFor" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_find_password"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="13.3dp"
|
||||
android:layout_marginTop="60dp"
|
||||
android:background="@drawable/bg_round_corner_6_7_9970ff"
|
||||
android:fontFamily="@font/gmarket_sans_bold"
|
||||
android:gravity="center"
|
||||
android:paddingVertical="16dp"
|
||||
android:text="임시 비밀번호 받기"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="15sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_service_center"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="93dp"
|
||||
android:background="@drawable/bg_round_corner_8_transparent_9970ff"
|
||||
android:drawablePadding="13.3dp"
|
||||
android:paddingHorizontal="18.7dp"
|
||||
android:paddingVertical="10.7dp"
|
||||
android:text="고객센터로 문의하기"
|
||||
android:textColor="@color/color_9970ff"
|
||||
app:drawableStartCompat="@drawable/ic_headphones_purple" />
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -0,0 +1,168 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/black">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_toolbar"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="51.7dp"
|
||||
android:background="@color/black"
|
||||
android:fontFamily="@font/gmarket_sans_bold"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingHorizontal="16.7dp"
|
||||
android:textColor="@color/color_eeeeee"
|
||||
android:textSize="18.3sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="바로, 상담 가능한 요즘친구" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="26.7dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="6.7dp"
|
||||
android:fontFamily="@font/gmarket_sans_medium"
|
||||
android:text="이메일"
|
||||
android:textColor="@color/color_eeeeee"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_email"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/edittext_underline"
|
||||
android:fontFamily="@font/gmarket_sans_medium"
|
||||
android:hint="이메일 주소를 입력하세요"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="textWebEmailAddress"
|
||||
android:paddingHorizontal="6.7dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:textColor="@color/color_eeeeee"
|
||||
android:textColorHint="@color/color_777777"
|
||||
android:textCursorDrawable="@drawable/edit_text_cursor"
|
||||
android:textSize="13.3sp"
|
||||
android:theme="@style/EditTextStyle"
|
||||
tools:ignore="LabelFor" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="26.7dp"
|
||||
android:layout_marginTop="33.3dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="6.7dp"
|
||||
android:fontFamily="@font/gmarket_sans_medium"
|
||||
android:text="비밀번호"
|
||||
android:textColor="@color/color_eeeeee"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_password"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/edittext_underline"
|
||||
android:fontFamily="@font/gmarket_sans_medium"
|
||||
android:hint="비밀번호를 입력하세요"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="textWebPassword"
|
||||
android:paddingHorizontal="6.7dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:textColor="@color/color_eeeeee"
|
||||
android:textColorHint="@color/color_777777"
|
||||
android:textCursorDrawable="@drawable/edit_text_cursor"
|
||||
android:textSize="13.3sp"
|
||||
android:theme="@style/EditTextStyle"
|
||||
tools:ignore="LabelFor" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_visible_password"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:drawablePadding="13.3dp"
|
||||
android:fontFamily="@font/gmarket_sans_medium"
|
||||
android:gravity="center_vertical"
|
||||
android:text="비밀번호 표시"
|
||||
android:textColor="@color/color_eeeeee"
|
||||
android:textSize="13.3sp"
|
||||
app:drawableStartCompat="@drawable/ic_select" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_login"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="13.3dp"
|
||||
android:layout_marginTop="40dp"
|
||||
android:background="@drawable/bg_round_corner_6_7_9970ff"
|
||||
android:fontFamily="@font/gmarket_sans_bold"
|
||||
android:gravity="center"
|
||||
android:paddingVertical="16dp"
|
||||
android:text="로그인"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="15sp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="40dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_forgot_password"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/gmarket_sans_medium"
|
||||
android:text="비밀번호 재설정"
|
||||
android:textColor="@color/color_bbbbbb"
|
||||
android:textSize="13.3sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="4dp"
|
||||
android:fontFamily="@font/gmarket_sans_medium"
|
||||
android:text="|"
|
||||
android:textColor="@color/color_bbbbbb"
|
||||
android:textSize="13.3sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_sign_up"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/gmarket_sans_medium"
|
||||
android:text="회원가입"
|
||||
android:textColor="@color/color_bbbbbb"
|
||||
android:textSize="13.3sp" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -0,0 +1,448 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/black"
|
||||
android:orientation="vertical">
|
||||
|
||||
<include
|
||||
android:id="@+id/toolbar"
|
||||
layout="@layout/detail_toolbar" />
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/toolbar">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/ll_step_1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="13.3dp"
|
||||
android:layout_marginTop="13.3dp"
|
||||
android:background="@drawable/bg_round_corner_6_7_222222"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="13.3dp"
|
||||
android:paddingVertical="20dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="6.7dp"
|
||||
android:fontFamily="@font/gmarket_sans_medium"
|
||||
android:text="이메일"
|
||||
android:textColor="@color/color_eeeeee"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_email"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/edittext_underline"
|
||||
android:fontFamily="@font/gmarket_sans_medium"
|
||||
android:hint="이메일 주소를 입력하세요"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="textWebEmailAddress"
|
||||
android:paddingHorizontal="6.7dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:textColor="@color/color_eeeeee"
|
||||
android:textColorHint="@color/color_777777"
|
||||
android:textCursorDrawable="@drawable/edit_text_cursor"
|
||||
android:textSize="13.3sp"
|
||||
android:theme="@style/EditTextStyle"
|
||||
tools:ignore="LabelFor" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="26.7dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="6.7dp"
|
||||
android:fontFamily="@font/gmarket_sans_medium"
|
||||
android:text="비밀번호"
|
||||
android:textColor="@color/color_eeeeee"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_password"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/edittext_underline"
|
||||
android:fontFamily="@font/gmarket_sans_medium"
|
||||
android:hint="비밀번호 (영문, 숫자 포함 8~20자)"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="textWebPassword"
|
||||
android:paddingHorizontal="6.7dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:textColor="@color/color_eeeeee"
|
||||
android:textColorHint="@color/color_777777"
|
||||
android:textCursorDrawable="@drawable/edit_text_cursor"
|
||||
android:textSize="13.3sp"
|
||||
android:theme="@style/EditTextStyle"
|
||||
tools:ignore="LabelFor" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="26.7dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="6.7dp"
|
||||
android:fontFamily="@font/gmarket_sans_medium"
|
||||
android:text="비밀번호 확인"
|
||||
android:textColor="@color/color_eeeeee"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_password_re"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/edittext_underline"
|
||||
android:fontFamily="@font/gmarket_sans_medium"
|
||||
android:hint="비밀번호를 다시 입력해 주세요."
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="textWebPassword"
|
||||
android:paddingHorizontal="6.7dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:textColor="@color/color_eeeeee"
|
||||
android:textColorHint="@color/color_777777"
|
||||
android:textCursorDrawable="@drawable/edit_text_cursor"
|
||||
android:textSize="13.3sp"
|
||||
android:theme="@style/EditTextStyle"
|
||||
tools:ignore="LabelFor" />
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="13.3dp"
|
||||
android:layout_marginTop="13.3dp"
|
||||
android:background="@drawable/bg_round_corner_6_7_222222"
|
||||
android:orientation="vertical"
|
||||
android:padding="13.3dp">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginStart="6.7dp"
|
||||
android:fontFamily="@font/gmarket_sans_bold"
|
||||
android:text="약관 동의"
|
||||
android:textColor="@color/color_eeeeee"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:background="@color/color_909090" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginStart="6.7dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_terms_of_service"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/gmarket_sans_medium"
|
||||
android:text="이용약관"
|
||||
android:textColor="@color/color_eeeeee"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="6.7dp"
|
||||
android:fontFamily="@font/gmarket_sans_medium"
|
||||
android:text="(필수)"
|
||||
android:textColor="@color/color_9970ff"
|
||||
android:textSize="12sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_terms_of_service"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginEnd="6.7dp"
|
||||
android:contentDescription="@null"
|
||||
android:src="@drawable/ic_select" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:background="@color/color_909090" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginStart="6.7dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_privacy_policy"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/gmarket_sans_medium"
|
||||
android:text="개인정보수집 및 이용동의"
|
||||
android:textColor="@color/color_eeeeee"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="6.7dp"
|
||||
android:fontFamily="@font/gmarket_sans_medium"
|
||||
android:text="(필수)"
|
||||
android:textColor="@color/color_9970ff"
|
||||
android:textSize="12sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_privacy_policy"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginEnd="6.7dp"
|
||||
android:contentDescription="@null"
|
||||
android:src="@drawable/ic_select" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:background="@color/color_909090" />
|
||||
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/ll_step_2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="96.7dp"
|
||||
android:layout_height="116.8dp"
|
||||
android:layout_marginTop="13.3dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_profile"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="116.8dp"
|
||||
android:adjustViewBounds="true"
|
||||
android:background="@color/color_3e3358"
|
||||
android:contentDescription="@null"
|
||||
android:src="@drawable/ic_logo" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_photo_picker"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:background="@drawable/bg_round_corner_33_3_9970ff"
|
||||
android:contentDescription="@null"
|
||||
android:padding="10dp"
|
||||
android:src="@drawable/ic_camera" />
|
||||
</RelativeLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="13.3dp"
|
||||
android:layout_marginTop="13.3dp"
|
||||
android:background="@drawable/bg_round_corner_6_7_222222"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="13.3dp"
|
||||
android:paddingTop="20dp"
|
||||
android:paddingBottom="0dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="6.7dp"
|
||||
android:fontFamily="@font/gmarket_sans_medium"
|
||||
android:text="닉네임 (최대 12자)"
|
||||
android:textColor="@color/color_eeeeee"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_nickname"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/edittext_underline"
|
||||
android:fontFamily="@font/gmarket_sans_medium"
|
||||
android:hint="닉네임"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="textWebEditText"
|
||||
android:maxLength="12"
|
||||
android:paddingHorizontal="6.7dp"
|
||||
android:textColor="@color/color_eeeeee"
|
||||
android:textColorHint="@color/color_777777"
|
||||
android:textCursorDrawable="@drawable/edit_text_cursor"
|
||||
android:textSize="13.3sp"
|
||||
android:theme="@style/EditTextStyle"
|
||||
tools:ignore="LabelFor" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16.7dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="6.7dp"
|
||||
android:fontFamily="@font/gmarket_sans_bold"
|
||||
android:text="성별"
|
||||
android:textColor="@color/color_eeeeee"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:paddingHorizontal="6.7dp"
|
||||
android:paddingVertical="13.3dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_female"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:button="@null"
|
||||
android:drawablePadding="13.3dp"
|
||||
android:fontFamily="@font/gmarket_sans_medium"
|
||||
android:text="여자"
|
||||
android:textColor="@color/color_eeeeee"
|
||||
android:textSize="13.3sp"
|
||||
app:drawableStartCompat="@drawable/ic_radio_button_select" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_male"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:button="@null"
|
||||
android:drawablePadding="13.3dp"
|
||||
android:fontFamily="@font/gmarket_sans_medium"
|
||||
android:text="남자"
|
||||
android:textColor="@color/color_eeeeee"
|
||||
android:textSize="13.3sp"
|
||||
app:drawableStartCompat="@drawable/ic_radio_button_select" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_none"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:button="@null"
|
||||
android:drawablePadding="13.3dp"
|
||||
android:fontFamily="@font/gmarket_sans_medium"
|
||||
android:text="공개 안 함"
|
||||
android:textColor="@color/color_eeeeee"
|
||||
android:textSize="13.3sp"
|
||||
app:drawableStartCompat="@drawable/ic_radio_button_select" />
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="13.3dp"
|
||||
android:layout_marginTop="26.7dp"
|
||||
android:background="@drawable/bg_round_corner_16_7_222222"
|
||||
android:paddingHorizontal="13.3dp"
|
||||
android:paddingVertical="13.7dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_sign_up"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_round_corner_10_9970ff"
|
||||
android:fontFamily="@font/gmarket_sans_bold"
|
||||
android:gravity="center"
|
||||
android:paddingVertical="16dp"
|
||||
android:text="회원가입"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="18.3sp" />
|
||||
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/black">
|
||||
|
||||
<include
|
||||
android:id="@+id/toolbar"
|
||||
layout="@layout/detail_toolbar" />
|
||||
|
||||
<WebView
|
||||
android:id="@+id/web_view"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_margin="13.3dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/toolbar" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="51.7dp"
|
||||
android:background="@color/black"
|
||||
android:paddingHorizontal="13.3dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_back"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:drawablePadding="6.7dp"
|
||||
android:fontFamily="@font/gmarket_sans_bold"
|
||||
android:textColor="@color/color_eeeeee"
|
||||
android:textSize="18.3sp"
|
||||
app:drawableStartCompat="@drawable/ic_back"
|
||||
tools:ignore="RelativeOverlap"
|
||||
tools:text="소다라이브" />
|
||||
|
||||
</RelativeLayout>
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_loading"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:drawablePadding="10dp"
|
||||
android:fontFamily="@font/gmarket_sans_medium"
|
||||
android:textSize="13sp"
|
||||
app:drawableTopCompat="@drawable/loading"
|
||||
tools:text="로딩중 입니다..." />
|
||||
|
||||
</LinearLayout>
|
After Width: | Height: | Size: 9.3 KiB |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 5.8 KiB |
After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 7.6 KiB |
|
@ -4,4 +4,12 @@
|
|||
<color name="white">#FFFFFFFF</color>
|
||||
|
||||
<color name="color_9970ff">#9970FF</color>
|
||||
<color name="color_eeeeee">#EEEEEE</color>
|
||||
<color name="color_777777">#777777</color>
|
||||
<color name="color_bbbbbb">#BBBBBB</color>
|
||||
<color name="color_222222">#222222</color>
|
||||
<color name="color_909090">#909090</color>
|
||||
<color name="color_3e3358">#3E3358</color>
|
||||
|
||||
<color name="color_b3909090">#B3909090</color>
|
||||
</resources>
|
||||
|
|
|
@ -17,4 +17,9 @@
|
|||
<item name="android:statusBarColor">@color/black</item>
|
||||
<item name="android:navigationBarColor">@color/black</item>
|
||||
</style>
|
||||
|
||||
<style name="EditTextStyle" parent="Theme.AppCompat.Light">
|
||||
<item name="colorControlNormal">@color/color_9970ff</item>
|
||||
<item name="colorControlActivated">@color/color_9970ff</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
|
|
@ -10,6 +10,7 @@ dependencyResolutionManagement {
|
|||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
maven { url 'https://jitpack.io' }
|
||||
}
|
||||
}
|
||||
rootProject.name = "SodaLive"
|
||||
|
|