diff --git a/app/build.gradle b/app/build.gradle index fb2c926..9c4bc74 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -7,7 +7,7 @@ plugins { Properties properties = new Properties() -properties.load(project.rootProject. file ('local.properties').newDataInputStream()) +properties.load(project.rootProject.file('local.properties').newDataInputStream()) android { namespace 'org.android.go.sopt' @@ -46,6 +46,9 @@ android { } dependencies { + implementation 'com.jakewharton.timber:timber:4.7.1' + implementation 'androidx.activity:activity-ktx:1.4.0' + implementation 'androidx.fragment:fragment-ktx:1.4.0' implementation 'androidx.core:core-ktx:1.9.0' implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'com.google.android.material:material:1.8.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b85d997..1366046 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -7,6 +7,7 @@ () val image: LiveData get() = _image @@ -21,7 +20,7 @@ class GalleryViewModel: ViewModel() { fun uploadProfileImage() { if (image.value == null) { /* 아직 사진이 등록되지 않았다는 로직 처리 */ } else { - imageService.uploadImage(image.value!!.toFormData()).enqueue( + service.uploadImage(image.value!!.toFormData()).enqueue( object : Callback { override fun onResponse(call: Call, response: Response) { if (response.isSuccessful) diff --git a/app/src/main/java/org/android/go/sopt/home/HomeFragment.kt b/app/src/main/java/org/android/go/sopt/home/HomeFragment.kt index e1f2795..628a588 100644 --- a/app/src/main/java/org/android/go/sopt/home/HomeFragment.kt +++ b/app/src/main/java/org/android/go/sopt/home/HomeFragment.kt @@ -5,24 +5,24 @@ import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.Toast +import androidx.activity.viewModels import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.ConcatAdapter import androidx.recyclerview.widget.LinearLayoutManager import kotlinx.coroutines.launch import org.android.go.sopt.HomeServicePool -import org.android.go.sopt.R +import org.android.go.sopt.SoptServicePool import org.android.go.sopt.databinding.FragmentHomeBinding -import org.android.go.sopt.model.ResponseHome -import retrofit2.Call -import retrofit2.Response +import org.android.go.sopt.sign.LoginViewModel class HomeFragment : Fragment() { private var _binding: FragmentHomeBinding? = null private val binding: FragmentHomeBinding get() = requireNotNull(_binding) { "앗 binding이 null 이다!" } - private val homeService = HomeServicePool.homeService + + private val viewModel: HomeViewModel by viewModels() override fun onCreateView( @@ -40,40 +40,10 @@ class HomeFragment : Fragment() { binding.rvHome.adapter = adapter binding.rvHome.layoutManager = LinearLayoutManager(context) - - - lifecycleScope.launch { - kotlin.runCatching { - homeService.listuser() - }.fold( - { - homeadapter.submitList(it) - }, { - Log.d("aaa", "서버통신 실패") - } - ) + viewModel.followList.observe(viewLifecycleOwner){followList -> + homeadapter.submitList(followList) } - -// -// homeService.listuser().enqueue(object : retrofit2.Callback { -// override fun onResponse( -// call: Call, -// response: Response, -// ) { -// if (response.isSuccessful) { -// response.body()?.data?.let { -// homeadapter.submitList(it) -// Log.d("listlist", it.toString()) -// } -// } -// } -// -// override fun onFailure(call: Call, t: Throwable) { -// -// } -// -// }) } override fun onDestroyView() { diff --git a/app/src/main/java/org/android/go/sopt/home/HomeViewModel.kt b/app/src/main/java/org/android/go/sopt/home/HomeViewModel.kt new file mode 100644 index 0000000..5a61e41 --- /dev/null +++ b/app/src/main/java/org/android/go/sopt/home/HomeViewModel.kt @@ -0,0 +1,37 @@ +package org.android.go.sopt.home + +import android.util.Log +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.launch +import org.android.go.sopt.HomeServicePool.homeService +import org.android.go.sopt.model.ResponseHome +import timber.log.Timber + +class HomeViewModel : ViewModel() { + + private val _followList = MutableLiveData>() + val followList: LiveData> = _followList + + init { + fetchFollowList() + } + + private fun fetchFollowList() { + viewModelScope.launch { + kotlin.runCatching { + homeService.listuser() + }.fold( + { + _followList.value = it.data.toList() + }, { + Timber.e(it.message) + + } + ) + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/org/android/go/sopt/remote/SignApiFactory.kt b/app/src/main/java/org/android/go/sopt/remote/ApiFactory.kt similarity index 65% rename from app/src/main/java/org/android/go/sopt/remote/SignApiFactory.kt rename to app/src/main/java/org/android/go/sopt/remote/ApiFactory.kt index b3bde27..ef1e296 100644 --- a/app/src/main/java/org/android/go/sopt/remote/SignApiFactory.kt +++ b/app/src/main/java/org/android/go/sopt/remote/ApiFactory.kt @@ -10,27 +10,21 @@ import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor import org.json.JSONArray import org.json.JSONObject -import org.android.go.sopt.remote.AuthInterceptor import retrofit2.Retrofit -object SignApiFactory { +object ApiFactory { - private val client by lazy { - OkHttpClient.Builder().addInterceptor(HttpLoggingInterceptor().apply { - level = - if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.BODY else HttpLoggingInterceptor.Level.NONE - }).addInterceptor(AuthInterceptor()).build() - - OkHttpClient.Builder() - .addInterceptor(AuthInterceptor()) - .addInterceptor( - HttpLoggingInterceptor().apply { - level = - if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.BODY else HttpLoggingInterceptor.Level.NONE - } - ).build() - - } +// private val client by lazy { +// OkHttpClient.Builder() +// .addInterceptor(AuthInterceptor()) +// .addInterceptor( +// HttpLoggingInterceptor().apply { +// level = +// if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.BODY else HttpLoggingInterceptor.Level.NONE +// } +// ).build() +// +// } val retrofit: Retrofit by lazy { Retrofit.Builder() @@ -45,8 +39,10 @@ object SignApiFactory { when { message.isJsonObject() -> Log.d("Retrofit2", JSONObject(message).toString(4)) + message.isJsonArray() -> Log.d("Retrofit2", JSONArray(message).toString(4)) + else -> { Log.d("Retrofit2", "CONNECTION INFO -> $message") } @@ -66,8 +62,7 @@ object SignApiFactory { inline fun create(): T = retrofit.create(T::class.java) } -object SignServicePool { - val signService = SignApiFactory.create() - - val imageService = SignApiFactory.create() +object SoptServicePool { + val signService = ApiFactory.create() + val imageService = ApiFactory.create() } diff --git a/app/src/main/java/org/android/go/sopt/remote/AuthInterceptor.kt b/app/src/main/java/org/android/go/sopt/remote/AuthInterceptor.kt index d4a7d71..158d9b8 100644 --- a/app/src/main/java/org/android/go/sopt/remote/AuthInterceptor.kt +++ b/app/src/main/java/org/android/go/sopt/remote/AuthInterceptor.kt @@ -1,7 +1,13 @@ package org.android.go.sopt.remote +import android.util.Log import okhttp3.Interceptor import okhttp3.Response +import okhttp3.logging.HttpLoggingInterceptor +import org.android.go.sopt.ApiFactory.isJsonArray +import org.android.go.sopt.ApiFactory.isJsonObject +import org.json.JSONArray +import org.json.JSONObject class AuthInterceptor : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { @@ -11,4 +17,7 @@ class AuthInterceptor : Interceptor { .build() return chain.proceed(headerRequest) } + + } + diff --git a/app/src/main/java/org/android/go/sopt/remote/HomeApiFactory.kt b/app/src/main/java/org/android/go/sopt/remote/HomeApiFactory.kt index 23a5999..0064e27 100644 --- a/app/src/main/java/org/android/go/sopt/remote/HomeApiFactory.kt +++ b/app/src/main/java/org/android/go/sopt/remote/HomeApiFactory.kt @@ -4,6 +4,7 @@ package org.android.go.sopt import android.util.Log import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory import kotlinx.serialization.json.Json +import okhttp3.Interceptor import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor @@ -14,32 +15,41 @@ import retrofit2.Retrofit object HomeApiFactory { -// private val client by lazy { -// OkHttpClient.Builder().addInterceptor(HttpLoggingInterceptor().apply { -// level = -// if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.BODY else HttpLoggingInterceptor.Level.NONE -// }).addInterceptor(AuthInterceptor()).build() -// -// OkHttpClient.Builder() -// .addInterceptor(AuthInterceptor()) -// .addInterceptor( -// HttpLoggingInterceptor().apply { -// level = -// if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.BODY else HttpLoggingInterceptor.Level.NONE -// } -// ).build() -// } - val retrofit: Retrofit by lazy { Retrofit.Builder() .baseUrl(BuildConfig.REQRES_BASE_URL) .addConverterFactory(Json.asConverterFactory("application/json".toMediaType())) + .client(client) .build() + } + private fun getLogOkHttpClient(): Interceptor { + + val interceptor = HttpLoggingInterceptor { message -> + when { + message.isJsonObject() -> + Log.d("Retrofit2", JSONObject(message).toString(4)) + message.isJsonArray() -> + Log.d("Retrofit2", JSONArray(message).toString(4)) + else -> { + Log.d("Retrofit2", "CONNECTION INFO -> $message") + } + } + } + interceptor.level = HttpLoggingInterceptor.Level.BODY + return interceptor } + fun String?.isJsonObject(): Boolean = this?.startsWith("{") == true && this.endsWith("}") + fun String?.isJsonArray(): Boolean = this?.startsWith("[") == true && this.endsWith("]") + + private val client = OkHttpClient.Builder() + .addInterceptor(getLogOkHttpClient()) + .build() + inline fun create(): T = retrofit.create(T::class.java) + } object HomeServicePool { diff --git a/app/src/main/java/org/android/go/sopt/remote/Service.kt b/app/src/main/java/org/android/go/sopt/remote/Service.kt index b777b3d..0f636c5 100644 --- a/app/src/main/java/org/android/go/sopt/remote/Service.kt +++ b/app/src/main/java/org/android/go/sopt/remote/Service.kt @@ -7,21 +7,21 @@ import retrofit2.http.* interface SignService { @POST("sign-up") - fun signup( + suspend fun signup( @Body request: RequestSignUpDto - ): Call + ): ResponseSignUpDto @POST("sign-in") - fun signin( + suspend fun signin( @Body request: RequestLogin - ): Call + ): ResponseLogin } interface HomeService { @GET("/api/users") suspend fun listuser( @Query("page") page: Int = 2 - ): List + ): ResponseHome } interface ImageService { diff --git a/app/src/main/java/org/android/go/sopt/sign/LoginActivity.kt b/app/src/main/java/org/android/go/sopt/sign/LoginActivity.kt index e6045eb..8b1fb52 100644 --- a/app/src/main/java/org/android/go/sopt/sign/LoginActivity.kt +++ b/app/src/main/java/org/android/go/sopt/sign/LoginActivity.kt @@ -3,17 +3,16 @@ package org.android.go.sopt.sign import android.app.Activity import android.content.Intent import android.os.Bundle -import android.util.Log +import android.widget.Toast import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity -import com.google.android.gms.tasks.OnCompleteListener import com.google.android.material.snackbar.Snackbar -import com.google.firebase.messaging.FirebaseMessaging import org.android.go.sopt.R import org.android.go.sopt.databinding.ActivityLoginBinding import org.android.go.sopt.home.HomeActivity +import timber.log.Timber class LoginActivity : AppCompatActivity() { @@ -21,15 +20,14 @@ class LoginActivity : AppCompatActivity() { private val binding by lazy { ActivityLoginBinding.inflate(layoutInflater) } // LiveData가 저장되어 있는 ViewModel - private val viewModel: LoginViewModel by viewModels() + private val viewModel: LoginViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { + Timber.plant(Timber.DebugTree()) super.onCreate(savedInstanceState) - //binding = ActivityLoginBinding.inflate(layoutInflater) - setContentView(binding.root) - getFcmToken() + setContentView(binding.root) resultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> @@ -49,13 +47,18 @@ class LoginActivity : AppCompatActivity() { } viewModel.signInResult.observe(this) { signInResult -> - startActivity( - Intent( - this@LoginActivity, - HomeActivity::class.java - + if (signInResult) { + Toast.makeText(this, "로그인 성공", Toast.LENGTH_SHORT).show() + startActivity( + Intent( + this@LoginActivity, + HomeActivity::class.java + ) ) - ) + } + else{ + Toast.makeText(this, "로그인 실패", Toast.LENGTH_SHORT).show() + } } @@ -71,18 +74,6 @@ class LoginActivity : AppCompatActivity() { } } - private fun getFcmToken() { - FirebaseMessaging.getInstance().token.addOnCompleteListener(OnCompleteListener{ task-> - if (!task.isSuccessful) { - Log.w("tag", "Fetching FCM registration token failed", task.exception) - return@OnCompleteListener - } - // Get new FCM registration token - val token = task.result - // Log - Log.d("tag", "token is $token") - }) - } } diff --git a/app/src/main/java/org/android/go/sopt/sign/LoginViewModel.kt b/app/src/main/java/org/android/go/sopt/sign/LoginViewModel.kt index 1d5a23f..becc443 100644 --- a/app/src/main/java/org/android/go/sopt/sign/LoginViewModel.kt +++ b/app/src/main/java/org/android/go/sopt/sign/LoginViewModel.kt @@ -1,45 +1,36 @@ package org.android.go.sopt.sign -import android.util.Log import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.launch import org.android.go.sopt.RequestLogin -import org.android.go.sopt.ResponseLogin -import org.android.go.sopt.SignServicePool.signService -import retrofit2.Call -import retrofit2.Callback -import retrofit2.Response +import org.android.go.sopt.SoptServicePool.signService class LoginViewModel : ViewModel() { - private val _signInResult: MutableLiveData = MutableLiveData() - val signInResult: LiveData = _signInResult + private val _signInResult: MutableLiveData = MutableLiveData() + val signInResult: LiveData = _signInResult - fun signIn(id: String, password: String) { - signService.signin( - RequestLogin( - id, - password - ) - ).enqueue(object : Callback { - override fun onResponse( - call: Call, - response: Response - ) { - if (response.isSuccessful) { - _signInResult.value = response.body() - } - else { - Log.d("ffffff",response.body().toString()) - - } - } - override fun onFailure(call: Call, t: Throwable) { - - } + fun signIn(id: String, password: String) { - }) + viewModelScope.launch { + kotlin.runCatching { + signService.signin( + RequestLogin( + id, + password + ) + ) + }.fold( + { + _signInResult.value = true + },{ + _signInResult.value = false + } + ) + } } } \ No newline at end of file diff --git a/app/src/main/java/org/android/go/sopt/sign/SignupActivity.kt b/app/src/main/java/org/android/go/sopt/sign/SignupActivity.kt index d4e3c14..e714a87 100644 --- a/app/src/main/java/org/android/go/sopt/sign/SignupActivity.kt +++ b/app/src/main/java/org/android/go/sopt/sign/SignupActivity.kt @@ -3,28 +3,18 @@ package org.android.go.sopt.sign import android.content.Intent import android.os.Bundle -import android.text.Editable -import android.text.TextWatcher -import android.util.Log import android.view.View -import android.widget.Toast import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity -import androidx.core.view.isInvisible -import org.android.go.sopt.R -import org.android.go.sopt.RequestSignUpDto -import org.android.go.sopt.ResponseSignUpDto -import org.android.go.sopt.SignServicePool -import org.android.go.sopt.databinding.ActivityLoginBinding +import com.google.android.material.snackbar.Snackbar import org.android.go.sopt.databinding.ActivitySignupBinding -import retrofit2.Call -import retrofit2.Response +import timber.log.Timber class SignupActivity : AppCompatActivity() { private val binding by lazy { ActivitySignupBinding.inflate(layoutInflater) } // LiveData가 저장되어 있는 ViewModel - private val viewModel: SignupViewModel by viewModels() + private val viewModel: SignupViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { @@ -49,7 +39,7 @@ class SignupActivity : AppCompatActivity() { binding.tvPwWarning.visibility = View.VISIBLE } - viewModel.checksignup.observe(this){ isFormValid -> + viewModel.checksignup.observe(this) { isFormValid -> binding.btnSignupEnd.isEnabled = isFormValid } @@ -64,17 +54,23 @@ class SignupActivity : AppCompatActivity() { } viewModel.signUpResult.observe(this) { signupResult -> - startActivity( - Intent( + + if (signupResult){ + val intent = Intent( this@SignupActivity, LoginActivity::class.java ) - ) + setResult(RESULT_OK, intent) + finish() + } + else{ + Snackbar.make( + binding.root, "회원가입 실패", Snackbar.LENGTH_SHORT + ).show() + } + } } - - - } diff --git a/app/src/main/java/org/android/go/sopt/sign/SignupViewModel.kt b/app/src/main/java/org/android/go/sopt/sign/SignupViewModel.kt index 76ade23..ee3f9a0 100644 --- a/app/src/main/java/org/android/go/sopt/sign/SignupViewModel.kt +++ b/app/src/main/java/org/android/go/sopt/sign/SignupViewModel.kt @@ -1,29 +1,28 @@ package org.android.go.sopt.sign -import android.util.Log import androidx.lifecycle.LiveData import androidx.lifecycle.MediatorLiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.launch import org.android.go.sopt.RequestSignUpDto -import org.android.go.sopt.ResponseSignUpDto -import org.android.go.sopt.SignServicePool.signService -import retrofit2.Call -import retrofit2.Callback -import retrofit2.Response +import org.android.go.sopt.SoptServicePool.signService +import timber.log.Timber + class SignupViewModel : ViewModel() { + private val _signUpResult: MutableLiveData = MutableLiveData() + val signUpResult: LiveData = _signUpResult - private val _signUpResult: MutableLiveData = MutableLiveData() - val signUpResult: LiveData = _signUpResult val id = MutableLiveData() val pw = MutableLiveData() val name = MutableLiveData() val hobby = MutableLiveData() - private val _checksignup : MediatorLiveData = MediatorLiveData() - val checksignup : LiveData = _checksignup + private val _checksignup: MediatorLiveData = MediatorLiveData() + val checksignup: LiveData = _checksignup init { setupFormValidation() @@ -40,51 +39,45 @@ class SignupViewModel : ViewModel() { val isFormValid = canUserSignUp() _checksignup.value = isFormValid } - + private fun canUserSignUp(): Boolean { return ValidId(id.value) && ValidPw(pw.value) && id.value?.isNotBlank() == true && pw.value?.isNotBlank() == true && name.value?.isNotBlank() == true && hobby.value?.isNotBlank() == true } - fun signUp(id: String, password: String, name : String, skill : String) { - signService.signup( - RequestSignUpDto( - id, - password, - name, - skill - ) - ).enqueue(object : Callback { - override fun onResponse( - call: Call, - response: Response - ) { - if (response.isSuccessful) { - _signUpResult.value = response.body() - } - else { - Log.d("ffffff",response.body().toString()) - + fun signUp(id: String, password: String, name: String, skill: String) { + + viewModelScope.launch { + kotlin.runCatching { + signService.signup( + RequestSignUpDto( + id, + password, + name, + skill + ) + ) + }.fold( + { + _signUpResult.value = true + Timber.d(it.message) + + }, + { + _signUpResult.value = false } - } - - override fun onFailure(call: Call, t: Throwable) { - - } - - }) + ) + } } - fun ValidId(id: String?): Boolean { + fun ValidId(id: String?): Boolean { return id.isNullOrEmpty() || id.matches(Regex("(?=.*[a-zA-Z])(?=.*[0-9])[a-zA-Z0-9]{6,10}")) } - fun ValidPw(pw: String?): Boolean { + fun ValidPw(pw: String?): Boolean { return pw.isNullOrEmpty() || pw.matches(Regex("(?=.*[a-zA-Z])(?=.*[0-9])(?=.*[!@#%^&*()])[a-zA-Z0-9!@#%^&*()]{6,12}")) } - - } \ No newline at end of file diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml index 19e256f..b892754 100644 --- a/app/src/main/res/layout/activity_login.xml +++ b/app/src/main/res/layout/activity_login.xml @@ -66,6 +66,17 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/tv_PW" /> + +