diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 2330c379..e6ae5dc9 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -24,8 +24,8 @@ android { applicationId = "com.kuit.findu" minSdk = 28 targetSdk = 35 - versionCode = 12 - versionName = "1.0.11" + versionCode = 14 + versionName = "1.0.13" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" buildConfigField("String", "GPT_KEY", properties["GPT_KEY"].toString()) diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 481bb434..97f999fc 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -5,17 +5,155 @@ # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file +# Keep line numbers for debugging +-keepattributes SourceFile,LineNumberTable +-renamesourcefileattribute SourceFile + +# Keep annotations +-keepattributes *Annotation* +-keepattributes Signature +-keepattributes Exceptions + +# ========== Retrofit & OkHttp ========== +-dontwarn okhttp3.** +-dontwarn okio.** +-dontwarn javax.annotation.** +-keepclasseswithmembers class * { + @retrofit2.http.* ; +} +-keepclassmembers,allowshrinking,allowobfuscation interface * { + @retrofit2.http.* ; +} +-if interface * { @retrofit2.http.* ; } +-keep,allowobfuscation interface <1> + +# ========== Kotlinx Serialization ========== +-keepattributes *Annotation*, InnerClasses +-dontnote kotlinx.serialization.AnnotationsKt +-keepclassmembers class kotlinx.serialization.json.** { + *** Companion; +} +-keepclasseswithmembers class kotlinx.serialization.json.** { + kotlinx.serialization.KSerializer serializer(...); +} +-keep,includedescriptorclasses class com.kuit.findu.**$$serializer { *; } +-keepclassmembers class com.kuit.findu.** { + *** Companion; +} +-keepclasseswithmembers class com.kuit.findu.** { + kotlinx.serialization.KSerializer serializer(...); +} + +# Keep all data classes and their properties +-keep class com.kuit.findu.data.** { *; } +-keep class com.kuit.findu.domain.** { *; } + +# ========== Hilt ========== +-dontwarn com.google.errorprone.annotations.** +-keep class dagger.** { *; } +-keep class javax.inject.** { *; } +-keep class * extends dagger.hilt.android.internal.managers.ViewComponentManager$FragmentContextWrapper { *; } +-keep class * implements dagger.hilt.internal.GeneratedComponent { *; } +-keep class * implements dagger.hilt.internal.GeneratesRootInput { *; } +-keep class dagger.hilt.** { *; } +-keep class javax.inject.** { *; } +-keep class * extends dagger.hilt.android.lifecycle.HiltViewModel { *; } + +# ========== Glide ========== +-keep public class * implements com.bumptech.glide.module.GlideModule +-keep class * extends com.bumptech.glide.module.AppGlideModule { + (...); +} +-keep public enum com.bumptech.glide.load.ImageHeaderParser$** { + **[] $VALUES; + public *; +} +-keep class com.bumptech.glide.load.data.ParcelFileDescriptorRewinder$InternalRewinder { + *** rewind(); +} + +# ========== Naver Map SDK ========== +-keep class com.naver.maps.** { *; } +-keep interface com.naver.maps.** { *; } +-dontwarn com.naver.maps.** + +# ========== CameraX ========== +-keep class androidx.camera.** { *; } +-dontwarn androidx.camera.** + +# ========== Kakao SDK ========== +-keep class com.kakao.sdk.**.model.* { ; } +-keep class * extends com.google.gson.TypeAdapter +-keep class com.kakao.sdk.auth.** { *; } +-keep class com.kakao.sdk.user.** { *; } +-keep class com.kakao.sdk.common.** { *; } + +# ========== Firebase ========== +-keep class com.google.firebase.** { *; } +-keep class com.google.android.gms.** { *; } +-dontwarn com.google.firebase.** +-dontwarn com.google.android.gms.** + +# ========== Jetpack Compose ========== +-keep class androidx.compose.** { *; } +-keep class androidx.compose.runtime.** { *; } +-keep class androidx.compose.ui.** { *; } +-dontwarn androidx.compose.** + +# Keep ViewModels +-keep class * extends androidx.lifecycle.ViewModel { + (); +} +-keep class * extends androidx.lifecycle.AndroidViewModel { + (android.app.Application); +} + +# ========== AndroidX Navigation ========== +-keep class androidx.navigation.fragment.NavHostFragment { *; } +-keepnames class * extends android.os.Parcelable +-keepnames class * extends java.io.Serializable + +# ========== Material Components ========== +-keep class com.google.android.material.** { *; } +-dontwarn com.google.android.material.** + +# ========== ViewBinding & DataBinding ========== +-keep class * implements androidx.viewbinding.ViewBinding { + public static * inflate(android.view.LayoutInflater); + public static * inflate(android.view.LayoutInflater, android.view.ViewGroup, boolean); + public static * bind(android.view.View); +} + +# ========== Lottie ========== +-keep class com.airbnb.lottie.** { *; } +-dontwarn com.airbnb.lottie.** + +# ========== WebView ========== +-keepclassmembers class * { + @android.webkit.JavascriptInterface ; +} + +# ========== Kotlin ========== +-keep class kotlin.** { *; } +-keep class kotlin.Metadata { *; } +-dontwarn kotlin.** +-keepclassmembers class **$WhenMappings { + ; +} +-keepclassmembers class kotlin.Metadata { + public ; +} +-assumenosideeffects class kotlin.jvm.internal.Intrinsics { + static void checkParameterIsNotNull(java.lang.Object, java.lang.String); +} + +# ========== Coroutines ========== +-keepnames class kotlinx.coroutines.internal.MainDispatcherFactory {} +-keepnames class kotlinx.coroutines.CoroutineExceptionHandler {} +-keepclassmembernames class kotlinx.** { + volatile ; +} + +# ========== R8 Full Mode ========== +-allowaccessmodification +-repackageclasses diff --git a/app/src/main/java/com/kuit/findu/data/datalocal/datasource/DeviceLocalDataSource.kt b/app/src/main/java/com/kuit/findu/data/datalocal/datasource/DeviceLocalDataSource.kt index 4a454114..c5603986 100644 --- a/app/src/main/java/com/kuit/findu/data/datalocal/datasource/DeviceLocalDataSource.kt +++ b/app/src/main/java/com/kuit/findu/data/datalocal/datasource/DeviceLocalDataSource.kt @@ -3,6 +3,7 @@ package com.kuit.findu.data.datalocal.datasource interface DeviceLocalDataSource { var deviceId: String var nickname: String + var isGuestLogin: Boolean fun clear() } \ No newline at end of file diff --git a/app/src/main/java/com/kuit/findu/data/datalocal/datasourceimpl/DeviceLocalDataSourceImpl.kt b/app/src/main/java/com/kuit/findu/data/datalocal/datasourceimpl/DeviceLocalDataSourceImpl.kt index 09885d8e..7f55fde1 100644 --- a/app/src/main/java/com/kuit/findu/data/datalocal/datasourceimpl/DeviceLocalDataSourceImpl.kt +++ b/app/src/main/java/com/kuit/findu/data/datalocal/datasourceimpl/DeviceLocalDataSourceImpl.kt @@ -23,6 +23,10 @@ class DeviceLocalDataSourceImpl @Inject constructor( get() = sharedPreferences.getString(NICKNAME, INITIAL_VALUE).toString() set(value) = sharedPreferences.edit { putString(NICKNAME, value) } + override var isGuestLogin: Boolean + get() = sharedPreferences.getBoolean(IS_GUEST_LOGIN, false) + set(value) = sharedPreferences.edit { putBoolean(IS_GUEST_LOGIN, value) } + override fun clear() { val currentDeviceId = deviceId sharedPreferences.edit { @@ -34,6 +38,7 @@ class DeviceLocalDataSourceImpl @Inject constructor( const val PREFERENCES_NAME = "device_preferences" const val DEVICE_ID = "deviceId" const val NICKNAME = "nickname" + const val IS_GUEST_LOGIN = "isGuestLogin" const val INITIAL_VALUE = "" } diff --git a/app/src/main/java/com/kuit/findu/data/dataremote/datasourceimpl/AuthRemoteDataSourceImpl.kt b/app/src/main/java/com/kuit/findu/data/dataremote/datasourceimpl/AuthRemoteDataSourceImpl.kt index 13b6c354..efa9a5bf 100644 --- a/app/src/main/java/com/kuit/findu/data/dataremote/datasourceimpl/AuthRemoteDataSourceImpl.kt +++ b/app/src/main/java/com/kuit/findu/data/dataremote/datasourceimpl/AuthRemoteDataSourceImpl.kt @@ -6,24 +6,23 @@ import com.kuit.findu.data.dataremote.model.base.NullableBaseResponse import com.kuit.findu.data.dataremote.model.request.CheckNicknameRequestDto import com.kuit.findu.data.dataremote.model.request.GuestLoginRequestDto import com.kuit.findu.data.dataremote.model.request.LoginRequestDto +import com.kuit.findu.data.dataremote.model.request.PostUserRequestDto import com.kuit.findu.data.dataremote.model.response.CheckNicknameResponseDto import com.kuit.findu.data.dataremote.model.response.auth.GuestLoginResponseDto import com.kuit.findu.data.dataremote.model.response.auth.LoginResponseDto import com.kuit.findu.data.dataremote.model.response.auth.UserInfoDto import com.kuit.findu.data.dataremote.service.AuthService -import com.kuit.findu.data.mapper.torequest.toImageMultipart -import com.kuit.findu.data.mapper.torequest.toPlainTextRequestBody import java.io.File import javax.inject.Inject class AuthRemoteDataSourceImpl @Inject constructor( - private val authService: AuthService + private val authService: AuthService, ) : AuthRemoteDataSource { override suspend fun postLogin(loginRequestDto: LoginRequestDto): NullableBaseResponse = - authService.postLogin(loginRequestDto=loginRequestDto) + authService.postLogin(loginRequestDto = loginRequestDto) override suspend fun postGuestLogin(guestLoginRequestDto: GuestLoginRequestDto): NullableBaseResponse = - authService.postGuestLogin(guestLoginRequestDto=guestLoginRequestDto) + authService.postGuestLogin(guestLoginRequestDto = guestLoginRequestDto) override suspend fun postCheckNickname(nickname: String): BaseResponse = authService.postCheckNickname(CheckNicknameRequestDto(nickname)) @@ -33,12 +32,8 @@ class AuthRemoteDataSourceImpl @Inject constructor( defaultImageName: String?, nickname: String, kakaoId: Long, - deviceId: String + deviceId: String, ): NullableBaseResponse = authService.postSignup( - profileImage = profileImageFile?.toImageMultipart("profileImage"), - defaultImageName = defaultImageName?.toPlainTextRequestBody(), - nickname = nickname.toPlainTextRequestBody(), - kakaoId = kakaoId.toString().toPlainTextRequestBody(), - deviceId = deviceId.toPlainTextRequestBody() + PostUserRequestDto(nickname, kakaoId, deviceId) ) } \ No newline at end of file diff --git a/app/src/main/java/com/kuit/findu/data/dataremote/exception/ApiNotFoundException.kt b/app/src/main/java/com/kuit/findu/data/dataremote/exception/ApiNotFoundException.kt new file mode 100644 index 00000000..6f466462 --- /dev/null +++ b/app/src/main/java/com/kuit/findu/data/dataremote/exception/ApiNotFoundException.kt @@ -0,0 +1,5 @@ +package com.kuit.findu.data.dataremote.exception + +class ApiNotFoundException( + message: String, +) : Exception(message) diff --git a/app/src/main/java/com/kuit/findu/data/dataremote/model/request/PostUserRequestDto.kt b/app/src/main/java/com/kuit/findu/data/dataremote/model/request/PostUserRequestDto.kt new file mode 100644 index 00000000..4e482c53 --- /dev/null +++ b/app/src/main/java/com/kuit/findu/data/dataremote/model/request/PostUserRequestDto.kt @@ -0,0 +1,14 @@ +package com.kuit.findu.data.dataremote.model.request + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class PostUserRequestDto( + @SerialName("nickname") + val nickname: String, + @SerialName("kakaoId") + val kakaoId: Long, + @SerialName("deviceId") + val deviceId: String, +) diff --git a/app/src/main/java/com/kuit/findu/data/dataremote/model/request/TokenReissueRequestDto.kt b/app/src/main/java/com/kuit/findu/data/dataremote/model/request/TokenReissueRequestDto.kt new file mode 100644 index 00000000..8ecdf15c --- /dev/null +++ b/app/src/main/java/com/kuit/findu/data/dataremote/model/request/TokenReissueRequestDto.kt @@ -0,0 +1,10 @@ +package com.kuit.findu.data.dataremote.model.request + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class TokenReissueRequestDto( + @SerialName("refreshToken") + val refreshToken: String, +) diff --git a/app/src/main/java/com/kuit/findu/data/dataremote/model/response/auth/GuestLoginResponseDto.kt b/app/src/main/java/com/kuit/findu/data/dataremote/model/response/auth/GuestLoginResponseDto.kt index 3a973259..dd648a71 100644 --- a/app/src/main/java/com/kuit/findu/data/dataremote/model/response/auth/GuestLoginResponseDto.kt +++ b/app/src/main/java/com/kuit/findu/data/dataremote/model/response/auth/GuestLoginResponseDto.kt @@ -8,5 +8,7 @@ data class GuestLoginResponseDto( @SerialName("userId") val userId: Long, @SerialName("accessToken") - val accessToken: String + val accessToken: String, + @SerialName("refreshToken") + val refreshToken: String ) diff --git a/app/src/main/java/com/kuit/findu/data/dataremote/model/response/auth/LoginResponseDto.kt b/app/src/main/java/com/kuit/findu/data/dataremote/model/response/auth/LoginResponseDto.kt index b8ee05f0..1eb6d610 100644 --- a/app/src/main/java/com/kuit/findu/data/dataremote/model/response/auth/LoginResponseDto.kt +++ b/app/src/main/java/com/kuit/findu/data/dataremote/model/response/auth/LoginResponseDto.kt @@ -18,5 +18,7 @@ data class UserInfoDto( @SerialName("nickname") val nickname: String, @SerialName("accessToken") - val accessToken: String + val accessToken: String, + @SerialName("refreshToken") + val refreshToken: String, ) \ No newline at end of file diff --git a/app/src/main/java/com/kuit/findu/data/dataremote/model/response/auth/TokenReissueResponseDto.kt b/app/src/main/java/com/kuit/findu/data/dataremote/model/response/auth/TokenReissueResponseDto.kt new file mode 100644 index 00000000..ff3a4cd6 --- /dev/null +++ b/app/src/main/java/com/kuit/findu/data/dataremote/model/response/auth/TokenReissueResponseDto.kt @@ -0,0 +1,12 @@ +package com.kuit.findu.data.dataremote.model.response.auth + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class TokenReissueResponseDto( + @SerialName("accessToken") + val accessToken: String, + @SerialName("refreshToken") + val refreshToken: String, +) diff --git a/app/src/main/java/com/kuit/findu/data/dataremote/model/response/my/MyNickNameResponseDto.kt b/app/src/main/java/com/kuit/findu/data/dataremote/model/response/my/MyNickNameResponseDto.kt index b8371af3..4d71e780 100644 --- a/app/src/main/java/com/kuit/findu/data/dataremote/model/response/my/MyNickNameResponseDto.kt +++ b/app/src/main/java/com/kuit/findu/data/dataremote/model/response/my/MyNickNameResponseDto.kt @@ -9,5 +9,5 @@ data class MyNickNameResponseDto( @SerialName("nickname") val nickname: String, @SerialName("profileImage") - val profileImage : String, + val profileImage : String? = null, ) \ No newline at end of file diff --git a/app/src/main/java/com/kuit/findu/data/dataremote/model/response/search/DetailMissingResponseDto.kt b/app/src/main/java/com/kuit/findu/data/dataremote/model/response/search/DetailMissingResponseDto.kt index dd277f58..418e475a 100644 --- a/app/src/main/java/com/kuit/findu/data/dataremote/model/response/search/DetailMissingResponseDto.kt +++ b/app/src/main/java/com/kuit/findu/data/dataremote/model/response/search/DetailMissingResponseDto.kt @@ -16,8 +16,8 @@ data class DetailMissingResponseDto( @SerialName("significant") val significant: String, @SerialName("missingLocation") val missingLocation: String, @SerialName("missingAddress") val missingAddress: String, - @SerialName("latitude") val latitude: Double, - @SerialName("longitude") val longitude: Double, + @SerialName("latitude") val latitude: Double?, + @SerialName("longitude") val longitude: Double?, @SerialName("reporterName") val reporterName: String, @SerialName("reporterTel") val reporterTel: String, @SerialName("interest") val interest: Boolean diff --git a/app/src/main/java/com/kuit/findu/data/dataremote/model/response/search/DetailProtectResponseDto.kt b/app/src/main/java/com/kuit/findu/data/dataremote/model/response/search/DetailProtectResponseDto.kt index ddce9e30..0a634282 100644 --- a/app/src/main/java/com/kuit/findu/data/dataremote/model/response/search/DetailProtectResponseDto.kt +++ b/app/src/main/java/com/kuit/findu/data/dataremote/model/response/search/DetailProtectResponseDto.kt @@ -28,9 +28,9 @@ data class DetailProtectResponseDto( @SerialName("careAddr") val careAddr: String, @SerialName("latitude") - val latitude: Double, + val latitude: Double?, @SerialName("longitude") - val longitude: Double, + val longitude: Double?, @SerialName("careTel") val careTel: String, @SerialName("foundDate") diff --git a/app/src/main/java/com/kuit/findu/data/dataremote/model/response/search/DetailWitnessResponseDto.kt b/app/src/main/java/com/kuit/findu/data/dataremote/model/response/search/DetailWitnessResponseDto.kt index d2ee4a00..49f4ee25 100644 --- a/app/src/main/java/com/kuit/findu/data/dataremote/model/response/search/DetailWitnessResponseDto.kt +++ b/app/src/main/java/com/kuit/findu/data/dataremote/model/response/search/DetailWitnessResponseDto.kt @@ -12,8 +12,8 @@ data class DetailWitnessResponseDto( @SerialName("significant") val significant: String, @SerialName("witnessLocation") val witnessLocation: String, @SerialName("witnessAddress") val witnessAddress: String, - @SerialName("latitude") val latitude: Double, - @SerialName("longitude") val longitude: Double, + @SerialName("latitude") val latitude: Double?, + @SerialName("longitude") val longitude: Double?, @SerialName("reporterInfo") val reporterInfo: String, @SerialName("witnessDate") val witnessDate: String, @SerialName("interest") val interest: Boolean diff --git a/app/src/main/java/com/kuit/findu/data/dataremote/model/response/search/SearchResponseDto.kt b/app/src/main/java/com/kuit/findu/data/dataremote/model/response/search/SearchResponseDto.kt index 6fc1c066..0938da87 100644 --- a/app/src/main/java/com/kuit/findu/data/dataremote/model/response/search/SearchResponseDto.kt +++ b/app/src/main/java/com/kuit/findu/data/dataremote/model/response/search/SearchResponseDto.kt @@ -8,9 +8,9 @@ data class SearchResponseDto( @SerialName("cards") val cards: List, @SerialName("lastId") - val lastId : Long?, + val lastId: Long?, @SerialName("isLast") - val isLast : Boolean + val isLast: Boolean, ) @Serializable @@ -28,5 +28,7 @@ data class SearchAnimalCard( @SerialName("location") val location: String, @SerialName("interest") - val interest: Boolean = false + val interest: Boolean = false, + @SerialName("createdAt") + val createdAt: String, ) diff --git a/app/src/main/java/com/kuit/findu/data/dataremote/service/AuthService.kt b/app/src/main/java/com/kuit/findu/data/dataremote/service/AuthService.kt index 1142f17f..91822f8e 100644 --- a/app/src/main/java/com/kuit/findu/data/dataremote/service/AuthService.kt +++ b/app/src/main/java/com/kuit/findu/data/dataremote/service/AuthService.kt @@ -5,6 +5,7 @@ import com.kuit.findu.data.dataremote.model.base.NullableBaseResponse import com.kuit.findu.data.dataremote.model.request.CheckNicknameRequestDto import com.kuit.findu.data.dataremote.model.request.GuestLoginRequestDto import com.kuit.findu.data.dataremote.model.request.LoginRequestDto +import com.kuit.findu.data.dataremote.model.request.PostUserRequestDto import com.kuit.findu.data.dataremote.model.response.CheckNicknameResponseDto import com.kuit.findu.data.dataremote.model.response.auth.GuestLoginResponseDto import com.kuit.findu.data.dataremote.model.response.auth.LoginResponseDto @@ -12,36 +13,28 @@ import com.kuit.findu.data.dataremote.model.response.auth.UserInfoDto import com.kuit.findu.data.dataremote.util.ApiConstraints.API import com.kuit.findu.data.dataremote.util.ApiConstraints.AUTH import com.kuit.findu.data.dataremote.util.ApiConstraints.VERSION -import okhttp3.MultipartBody -import okhttp3.RequestBody import retrofit2.http.Body import retrofit2.http.Multipart import retrofit2.http.POST -import retrofit2.http.Part interface AuthService { @POST("/$API/$VERSION/$AUTH/login/kakao") suspend fun postLogin( - @Body loginRequestDto: LoginRequestDto + @Body loginRequestDto: LoginRequestDto, ): NullableBaseResponse @POST("/$API/$VERSION/$AUTH/login/guest") suspend fun postGuestLogin( - @Body guestLoginRequestDto: GuestLoginRequestDto + @Body guestLoginRequestDto: GuestLoginRequestDto, ): NullableBaseResponse @POST("/$API/$VERSION/users/check/duplicate-nickname") suspend fun postCheckNickname( - @Body checkNicknameRequestDto: CheckNicknameRequestDto + @Body checkNicknameRequestDto: CheckNicknameRequestDto, ): BaseResponse - @Multipart @POST("/$API/$VERSION/users") suspend fun postSignup( - @Part profileImage: MultipartBody.Part?, - @Part("defaultProfileImageName") defaultImageName: RequestBody?, - @Part("nickname") nickname: RequestBody, - @Part("kakaoId") kakaoId: RequestBody, - @Part("deviceId") deviceId: RequestBody + @Body postUserRequestDto: PostUserRequestDto, ): NullableBaseResponse } \ No newline at end of file diff --git a/app/src/main/java/com/kuit/findu/data/dataremote/service/ReissueService.kt b/app/src/main/java/com/kuit/findu/data/dataremote/service/ReissueService.kt new file mode 100644 index 00000000..4182837f --- /dev/null +++ b/app/src/main/java/com/kuit/findu/data/dataremote/service/ReissueService.kt @@ -0,0 +1,18 @@ +package com.kuit.findu.data.dataremote.service + +import com.kuit.findu.data.dataremote.model.base.BaseResponse +import com.kuit.findu.data.dataremote.model.request.TokenReissueRequestDto +import com.kuit.findu.data.dataremote.model.response.auth.TokenReissueResponseDto +import com.kuit.findu.data.dataremote.util.ApiConstraints.API +import com.kuit.findu.data.dataremote.util.ApiConstraints.AUTH +import com.kuit.findu.data.dataremote.util.ApiConstraints.VERSION +import retrofit2.http.Body +import retrofit2.http.POST + +interface ReissueService { + + @POST("/$API/$VERSION/$AUTH/reissue/token") + suspend fun postReissueToken( + @Body tokenReissueRequestDto: TokenReissueRequestDto, + ): BaseResponse +} \ No newline at end of file diff --git a/app/src/main/java/com/kuit/findu/data/dataremote/util/ApiResponseHandler.kt b/app/src/main/java/com/kuit/findu/data/dataremote/util/ApiResponseHandler.kt index 80c14513..5e066fdc 100644 --- a/app/src/main/java/com/kuit/findu/data/dataremote/util/ApiResponseHandler.kt +++ b/app/src/main/java/com/kuit/findu/data/dataremote/util/ApiResponseHandler.kt @@ -1,5 +1,6 @@ package com.kuit.findu.data.dataremote.util +import com.kuit.findu.data.dataremote.exception.ApiNotFoundException import com.kuit.findu.data.dataremote.model.base.BaseResponse import com.kuit.findu.data.dataremote.model.base.NullableBaseResponse @@ -28,8 +29,13 @@ fun NullableBaseResponse.handleBaseResponse(): Result = Result.success(this.data) } + in 400..499 -> { // 클라이언트 에러 - Result.failure(Exception("Client error : ${this.message}")) + if (this.code == 404) { + Result.failure(ApiNotFoundException("Client error : ${this.message}")) + } else { + Result.failure(Exception("Client error : ${this.message}")) + } } in 500..599 -> { // 서버 에러 diff --git a/app/src/main/java/com/kuit/findu/data/dataremote/util/AuthAuthenticator.kt b/app/src/main/java/com/kuit/findu/data/dataremote/util/AuthAuthenticator.kt index 63d2f5b9..ae9c3111 100644 --- a/app/src/main/java/com/kuit/findu/data/dataremote/util/AuthAuthenticator.kt +++ b/app/src/main/java/com/kuit/findu/data/dataremote/util/AuthAuthenticator.kt @@ -1,34 +1,53 @@ package com.kuit.findu.data.dataremote.util -import android.content.Context -import android.content.Intent import com.kuit.findu.data.datalocal.datasource.TokenLocalDataSource -import com.kuit.findu.presentation.ui.login.LoginActivity -import dagger.hilt.android.qualifiers.ApplicationContext -import okhttp3.Interceptor +import com.kuit.findu.data.dataremote.model.request.TokenReissueRequestDto +import com.kuit.findu.data.dataremote.service.ReissueService +import kotlinx.coroutines.runBlocking +import okhttp3.Authenticator +import okhttp3.Request import okhttp3.Response +import okhttp3.Route import javax.inject.Inject class AuthAuthenticator @Inject constructor( private val tokenLocalDataSource: TokenLocalDataSource, - @ApplicationContext private val context: Context -) : Interceptor { - override fun intercept(chain: Interceptor.Chain): Response { - val request = chain.request() - val response = chain.proceed(request) - + private val reissueService: ReissueService, +) : Authenticator { + override fun authenticate(route: Route?, response: Response): Request? { // 401 Unauthorized 에러 감지 if (response.code == 401) { - // 토큰 삭제 - tokenLocalDataSource.clearToken() + return try { + val refreshToken = tokenLocalDataSource.refreshToken + + // 토큰 재발급 요청 + val reissueResponse = runBlocking { + reissueService.postReissueToken( + TokenReissueRequestDto(refreshToken) + ) + } + + if (reissueResponse.success) { + // 새로운 토큰으로 저장 + tokenLocalDataSource.accessToken = reissueResponse.data.accessToken + tokenLocalDataSource.refreshToken = reissueResponse.data.refreshToken - // 로그인 화면으로 이동 - val intent = Intent(context, LoginActivity::class.java).apply { - flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + // 새로운 토큰으로 원래 요청 재시도 + response.request.newBuilder() + .header("Authorization", "Bearer ${reissueResponse.data.accessToken}") + .build() + } else { + // 토큰 재발급 실패 - 로그인 화면으로 이동 + tokenLocalDataSource.clearToken() + null + } + } catch (e: Exception) { + // 토큰 재발급 중 오류 발생 - 로그인 화면으로 이동 + tokenLocalDataSource.clearToken() + null } - context.startActivity(intent) } - return response + return null } } diff --git a/app/src/main/java/com/kuit/findu/data/dataremote/util/ErrorTrackingInterceptor.kt b/app/src/main/java/com/kuit/findu/data/dataremote/util/ErrorTrackingInterceptor.kt index 4dcf5036..2e882522 100644 --- a/app/src/main/java/com/kuit/findu/data/dataremote/util/ErrorTrackingInterceptor.kt +++ b/app/src/main/java/com/kuit/findu/data/dataremote/util/ErrorTrackingInterceptor.kt @@ -41,7 +41,7 @@ class ErrorTrackingInterceptor @Inject constructor( key("api_status", code) } - if (code in 500..599) { + if (code in 400..599) { CoroutineScope(Dispatchers.IO).launch { discordLogger.logServerError( code = code, diff --git a/app/src/main/java/com/kuit/findu/data/mapper/todomain/GuestLoginResponseDtoMapper.kt b/app/src/main/java/com/kuit/findu/data/mapper/todomain/GuestLoginResponseDtoMapper.kt index 0b8a3c75..86231319 100644 --- a/app/src/main/java/com/kuit/findu/data/mapper/todomain/GuestLoginResponseDtoMapper.kt +++ b/app/src/main/java/com/kuit/findu/data/mapper/todomain/GuestLoginResponseDtoMapper.kt @@ -6,5 +6,6 @@ import com.kuit.findu.domain.model.GuestLoginData fun GuestLoginResponseDto.toDomain(): GuestLoginData = GuestLoginData( userId = this.userId, - accessToken = this.accessToken + accessToken = this.accessToken, + refreshToken = this.refreshToken ) \ No newline at end of file diff --git a/app/src/main/java/com/kuit/findu/data/mapper/todomain/SearchResponseDtoMapper.kt b/app/src/main/java/com/kuit/findu/data/mapper/todomain/SearchResponseDtoMapper.kt index 4f571fd9..25f9c087 100644 --- a/app/src/main/java/com/kuit/findu/data/mapper/todomain/SearchResponseDtoMapper.kt +++ b/app/src/main/java/com/kuit/findu/data/mapper/todomain/SearchResponseDtoMapper.kt @@ -26,6 +26,7 @@ fun SearchAnimalCard.toDomain(): SearchAnimal { date = this.date, location = this.location, interest = this.interest, + createdAt = this.createdAt, ) } diff --git a/app/src/main/java/com/kuit/findu/data/mapper/todomain/UserInfoDtoMapper.kt b/app/src/main/java/com/kuit/findu/data/mapper/todomain/UserInfoDtoMapper.kt index 644287b7..ecce2eef 100644 --- a/app/src/main/java/com/kuit/findu/data/mapper/todomain/UserInfoDtoMapper.kt +++ b/app/src/main/java/com/kuit/findu/data/mapper/todomain/UserInfoDtoMapper.kt @@ -7,5 +7,6 @@ fun UserInfoDto.toDomain(): UserInfo = UserInfo( userId = this.userId, nickname = this.nickname, - accessToken = this.accessToken + accessToken = this.accessToken, + refreshToken = this.refreshToken ) \ No newline at end of file diff --git a/app/src/main/java/com/kuit/findu/data/mapper/todomain/my/MyProfileMapper.kt b/app/src/main/java/com/kuit/findu/data/mapper/todomain/my/MyProfileMapper.kt index 935fa68d..2d33148c 100644 --- a/app/src/main/java/com/kuit/findu/data/mapper/todomain/my/MyProfileMapper.kt +++ b/app/src/main/java/com/kuit/findu/data/mapper/todomain/my/MyProfileMapper.kt @@ -6,5 +6,5 @@ import com.kuit.findu.domain.model.my.MyProfileData fun MyNickNameResponseDto.toDomain(): MyProfileData = MyProfileData( nickname = nickname, - profileImage = profileImage + profileImage = profileImage ?: "" ) \ No newline at end of file diff --git a/app/src/main/java/com/kuit/findu/data/repositoryimpl/UserInfoRepositoryImpl.kt b/app/src/main/java/com/kuit/findu/data/repositoryimpl/UserInfoRepositoryImpl.kt index 44d402e0..83bbc326 100644 --- a/app/src/main/java/com/kuit/findu/data/repositoryimpl/UserInfoRepositoryImpl.kt +++ b/app/src/main/java/com/kuit/findu/data/repositoryimpl/UserInfoRepositoryImpl.kt @@ -5,7 +5,7 @@ import com.kuit.findu.domain.repository.UserInfoRepository import javax.inject.Inject class UserInfoRepositoryImpl @Inject constructor( - private val deviceLocalDataSource: DeviceLocalDataSource + private val deviceLocalDataSource: DeviceLocalDataSource, ) : UserInfoRepository { override fun getDeviceId(): String = deviceLocalDataSource.deviceId @@ -19,5 +19,11 @@ class UserInfoRepositoryImpl @Inject constructor( deviceLocalDataSource.nickname = nickname } + override fun getIsGuestLogin(): Boolean = deviceLocalDataSource.isGuestLogin + + override fun setIsGuestLogin(isGuest: Boolean) { + deviceLocalDataSource.isGuestLogin = isGuest + } + override fun clear() = deviceLocalDataSource.clear() } \ No newline at end of file diff --git a/app/src/main/java/com/kuit/findu/di/NetworkModule.kt b/app/src/main/java/com/kuit/findu/di/NetworkModule.kt index 7a938353..cb7ff881 100644 --- a/app/src/main/java/com/kuit/findu/di/NetworkModule.kt +++ b/app/src/main/java/com/kuit/findu/di/NetworkModule.kt @@ -6,10 +6,12 @@ import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFact import com.kuit.findu.BuildConfig import com.kuit.findu.BuildConfig.DEBUG import com.kuit.findu.data.datalocal.datasource.TokenLocalDataSource +import com.kuit.findu.data.dataremote.service.ReissueService import com.kuit.findu.data.dataremote.util.AuthAuthenticator import com.kuit.findu.data.dataremote.util.AuthInterceptor import com.kuit.findu.data.dataremote.util.DiscordLogger import com.kuit.findu.data.dataremote.util.ErrorTrackingInterceptor +import com.kuit.findu.di.qualifier.ReissueRetrofit import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -54,7 +56,7 @@ object NetworkModule { addInterceptor(authInterceptor) if (DEBUG) addInterceptor(loggingInterceptor) else addInterceptor(errorTrackingInterceptor) - addInterceptor(authAuthenticator) + authenticator(authAuthenticator) }.build() @Provides @@ -77,24 +79,23 @@ object NetworkModule { @Singleton fun provideAuthAuthenticator( tokenLocalDataSource: TokenLocalDataSource, - @ApplicationContext context: Context, + reissueService: ReissueService, ): AuthAuthenticator { - return AuthAuthenticator(tokenLocalDataSource, context) + return AuthAuthenticator(tokenLocalDataSource, reissueService) } @Provides @Singleton fun provideErrorTrackingInterceptor( firebaseCrashlytics: FirebaseCrashlytics, - discordLogger: DiscordLogger + discordLogger: DiscordLogger, ): ErrorTrackingInterceptor { return ErrorTrackingInterceptor( - firebaseCrashlytics= firebaseCrashlytics, + firebaseCrashlytics = firebaseCrashlytics, discordLogger = discordLogger ) } - @ExperimentalSerializationApi @Provides @Singleton fun providesRetrofit( @@ -108,4 +109,24 @@ object NetworkModule { json.asConverterFactory(requireNotNull("application/json".toMediaTypeOrNull())) ) .build() + + @Provides + @ReissueRetrofit + @Singleton + fun providesReissueRetrofit( + loggingInterceptor: HttpLoggingInterceptor, + json: Json, + ): Retrofit { + val authOkHttpClient = OkHttpClient.Builder().apply { + readTimeout(20, TimeUnit.SECONDS) + if (DEBUG) addInterceptor(loggingInterceptor) + }.build() + return Retrofit.Builder() + .baseUrl(BuildConfig.BASE_URL) + .client(authOkHttpClient) + .addConverterFactory( + json.asConverterFactory(requireNotNull("application/json".toMediaTypeOrNull())) + ) + .build() + } } diff --git a/app/src/main/java/com/kuit/findu/di/ServiceModule.kt b/app/src/main/java/com/kuit/findu/di/ServiceModule.kt index 469be617..e2b4d870 100644 --- a/app/src/main/java/com/kuit/findu/di/ServiceModule.kt +++ b/app/src/main/java/com/kuit/findu/di/ServiceModule.kt @@ -12,8 +12,10 @@ import com.kuit.findu.data.dataremote.service.InquiryService import com.kuit.findu.data.dataremote.service.InterestService import com.kuit.findu.data.dataremote.service.MyService import com.kuit.findu.data.dataremote.service.NaverService +import com.kuit.findu.data.dataremote.service.ReissueService import com.kuit.findu.data.dataremote.service.ReportService import com.kuit.findu.data.dataremote.service.SearchService +import com.kuit.findu.di.qualifier.ReissueRetrofit import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -115,4 +117,9 @@ object ServiceModule { @Singleton fun provideInquiryService(retrofit: Retrofit): InquiryService = retrofit.create(InquiryService::class.java) + + @Provides + @Singleton + fun provideReissueService(@ReissueRetrofit reissueRetrofit: Retrofit): ReissueService = + reissueRetrofit.create(ReissueService::class.java) } \ No newline at end of file diff --git a/app/src/main/java/com/kuit/findu/di/UseCaseModule.kt b/app/src/main/java/com/kuit/findu/di/UseCaseModule.kt index 484a284b..f180ea38 100644 --- a/app/src/main/java/com/kuit/findu/di/UseCaseModule.kt +++ b/app/src/main/java/com/kuit/findu/di/UseCaseModule.kt @@ -14,9 +14,11 @@ import com.kuit.findu.domain.repository.report.ReportRepository import com.kuit.findu.domain.usecase.GetBreedDataUseCase import com.kuit.findu.domain.usecase.GetBreedValidationUseCase import com.kuit.findu.domain.usecase.GetDetailSearchUseCase +import com.kuit.findu.domain.usecase.GetIsGuestLoginUseCase import com.kuit.findu.domain.usecase.GetNicknameUseCase import com.kuit.findu.domain.usecase.GetSearchUseCase import com.kuit.findu.domain.usecase.PostAiDetectionUseCase +import com.kuit.findu.domain.usecase.SetIsGuestLoginUseCase import com.kuit.findu.domain.usecase.SetNicknameUseCase import com.kuit.findu.domain.usecase.auth.PostCheckNicknameUseCase import com.kuit.findu.domain.usecase.auth.PostGuestLoginUseCase @@ -290,4 +292,16 @@ object UseCaseModule { fun provideGetSigunguUseCase( informationRepository: InformationRepository, ): GetSigunguUseCase = GetSigunguUseCase(informationRepository) + + @Provides + @Singleton + fun provideSetIsGuestLoginUseCase( + userInfoRepository: UserInfoRepository, + ): SetIsGuestLoginUseCase = SetIsGuestLoginUseCase(userInfoRepository) + + @Provides + @Singleton + fun provideGetIsGuestLoginUseCase( + userInfoRepository: UserInfoRepository, + ): GetIsGuestLoginUseCase = GetIsGuestLoginUseCase(userInfoRepository) } \ No newline at end of file diff --git a/app/src/main/java/com/kuit/findu/di/qualifier/Qualifier.kt b/app/src/main/java/com/kuit/findu/di/qualifier/Qualifier.kt index 89d424b9..29ddb2fe 100644 --- a/app/src/main/java/com/kuit/findu/di/qualifier/Qualifier.kt +++ b/app/src/main/java/com/kuit/findu/di/qualifier/Qualifier.kt @@ -8,4 +8,8 @@ annotation class TokenPrefs @Qualifier @Retention(AnnotationRetention.BINARY) -annotation class DeviceIdPrefs \ No newline at end of file +annotation class DeviceIdPrefs + +@Qualifier +@Retention(AnnotationRetention.BINARY) +annotation class ReissueRetrofit \ No newline at end of file diff --git a/app/src/main/java/com/kuit/findu/domain/model/AuthData.kt b/app/src/main/java/com/kuit/findu/domain/model/AuthData.kt index 8de51cf1..4516eb2f 100644 --- a/app/src/main/java/com/kuit/findu/domain/model/AuthData.kt +++ b/app/src/main/java/com/kuit/findu/domain/model/AuthData.kt @@ -2,21 +2,23 @@ package com.kuit.findu.domain.model data class LoginInfo( val kakaoId: Long, - val deviceId: String + val deviceId: String, ) data class LoginData( val isFirstLogin: Boolean, - val userInfo: UserInfo? + val userInfo: UserInfo?, ) data class GuestLoginData( val userId: Long, - val accessToken: String + val accessToken: String, + val refreshToken: String, ) data class UserInfo( val userId: Long, val nickname: String, - val accessToken: String + val accessToken: String, + val refreshToken: String, ) \ No newline at end of file diff --git a/app/src/main/java/com/kuit/findu/domain/model/search/DetailMissingData.kt b/app/src/main/java/com/kuit/findu/domain/model/search/DetailMissingData.kt index d80dd763..58e1a69e 100644 --- a/app/src/main/java/com/kuit/findu/domain/model/search/DetailMissingData.kt +++ b/app/src/main/java/com/kuit/findu/domain/model/search/DetailMissingData.kt @@ -13,8 +13,8 @@ data class DetailMissingData( val significant: String, val missingLocation: String, val missingAddress: String, - val latitude: Double, - val longitude: Double, + val latitude: Double?, + val longitude: Double?, val reporterName: String, val reporterTel: String, val interest: Boolean diff --git a/app/src/main/java/com/kuit/findu/domain/model/search/DetailProtectData.kt b/app/src/main/java/com/kuit/findu/domain/model/search/DetailProtectData.kt index 38dd3e3b..a3355eb6 100644 --- a/app/src/main/java/com/kuit/findu/domain/model/search/DetailProtectData.kt +++ b/app/src/main/java/com/kuit/findu/domain/model/search/DetailProtectData.kt @@ -14,8 +14,8 @@ data class DetailProtectData( val significant: String, val careName: String, val careAddr: String, - val latitude: Double, - val longitude: Double, + val latitude: Double?, + val longitude: Double?, val careTel: String, val foundDate: String, val foundLocation: String, diff --git a/app/src/main/java/com/kuit/findu/domain/model/search/DetailWitnessData.kt b/app/src/main/java/com/kuit/findu/domain/model/search/DetailWitnessData.kt index 3e31427d..3723a636 100644 --- a/app/src/main/java/com/kuit/findu/domain/model/search/DetailWitnessData.kt +++ b/app/src/main/java/com/kuit/findu/domain/model/search/DetailWitnessData.kt @@ -10,8 +10,8 @@ data class DetailWitnessData( val significant: String, val witnessLocation: String, val witnessAddress: String, - val latitude: Double, - val longitude: Double, + val latitude: Double?, + val longitude: Double?, val reporterInfo: String, val witnessDate: String, val interest: Boolean diff --git a/app/src/main/java/com/kuit/findu/domain/model/search/SearchData.kt b/app/src/main/java/com/kuit/findu/domain/model/search/SearchData.kt index bec3a7d4..203f2ba8 100644 --- a/app/src/main/java/com/kuit/findu/domain/model/search/SearchData.kt +++ b/app/src/main/java/com/kuit/findu/domain/model/search/SearchData.kt @@ -5,7 +5,7 @@ import java.io.Serializable data class SearchData( val cards: List, val lastId: Long, - val isLast: Boolean + val isLast: Boolean, ) : Serializable data class SearchAnimal( @@ -15,7 +15,9 @@ data class SearchAnimal( val tag: SearchStatus, val date: String, val location: String, - val interest: Boolean + val interest: Boolean, + val createdAt: String, + ) : Serializable fun String.toSearchStatus(): SearchStatus { diff --git a/app/src/main/java/com/kuit/findu/domain/repository/UserInfoRepository.kt b/app/src/main/java/com/kuit/findu/domain/repository/UserInfoRepository.kt index d4f5c8e2..547978db 100644 --- a/app/src/main/java/com/kuit/findu/domain/repository/UserInfoRepository.kt +++ b/app/src/main/java/com/kuit/findu/domain/repository/UserInfoRepository.kt @@ -5,5 +5,7 @@ interface UserInfoRepository { fun setDeviceId(deviceId: String) fun getNickname(): String fun setNickname(nickname: String) + fun getIsGuestLogin(): Boolean + fun setIsGuestLogin(isGuest: Boolean) fun clear() } \ No newline at end of file diff --git a/app/src/main/java/com/kuit/findu/domain/usecase/GetIsGuestLoginUseCase.kt b/app/src/main/java/com/kuit/findu/domain/usecase/GetIsGuestLoginUseCase.kt new file mode 100644 index 00000000..bd028c2d --- /dev/null +++ b/app/src/main/java/com/kuit/findu/domain/usecase/GetIsGuestLoginUseCase.kt @@ -0,0 +1,12 @@ +package com.kuit.findu.domain.usecase + +import com.kuit.findu.domain.repository.UserInfoRepository +import javax.inject.Inject + +class GetIsGuestLoginUseCase @Inject constructor( + private val userInfoRepository: UserInfoRepository +) { + operator fun invoke(): Boolean { + return userInfoRepository.getIsGuestLogin() + } +} diff --git a/app/src/main/java/com/kuit/findu/domain/usecase/SetIsGuestLoginUseCase.kt b/app/src/main/java/com/kuit/findu/domain/usecase/SetIsGuestLoginUseCase.kt new file mode 100644 index 00000000..f3ac1051 --- /dev/null +++ b/app/src/main/java/com/kuit/findu/domain/usecase/SetIsGuestLoginUseCase.kt @@ -0,0 +1,12 @@ +package com.kuit.findu.domain.usecase + +import com.kuit.findu.domain.repository.UserInfoRepository +import javax.inject.Inject + +class SetIsGuestLoginUseCase @Inject constructor( + private val userInfoRepository: UserInfoRepository +) { + suspend operator fun invoke(isGuest: Boolean) { + userInfoRepository.setIsGuestLogin(isGuest) + } +} diff --git a/app/src/main/java/com/kuit/findu/domain/usecase/auth/PostSignupUseCase.kt b/app/src/main/java/com/kuit/findu/domain/usecase/auth/PostSignupUseCase.kt index e065a309..71557784 100644 --- a/app/src/main/java/com/kuit/findu/domain/usecase/auth/PostSignupUseCase.kt +++ b/app/src/main/java/com/kuit/findu/domain/usecase/auth/PostSignupUseCase.kt @@ -22,6 +22,8 @@ class PostSignupUseCase( nickname = nickname, kakaoId = kakaoId, deviceId = userInfoRepository.getDeviceId() - ) + ).onSuccess { + userInfoRepository.setIsGuestLogin(false) + } } \ No newline at end of file diff --git a/app/src/main/java/com/kuit/findu/presentation/ui/home/component/HomeScrollToTopButton.kt b/app/src/main/java/com/kuit/findu/presentation/ui/home/component/HomeScrollToTopButton.kt deleted file mode 100644 index 58e2cfcd..00000000 --- a/app/src/main/java/com/kuit/findu/presentation/ui/home/component/HomeScrollToTopButton.kt +++ /dev/null @@ -1,41 +0,0 @@ -package com.kuit.findu.presentation.ui.home.component - -import androidx.compose.foundation.background -import androidx.compose.foundation.border -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import com.kuit.findu.R -import com.kuit.findu.presentation.ui.base.BaseVectorIcon -import com.kuit.findu.presentation.util.extension.noRippleClickable -import com.kuit.findu.ui.theme.FindUTheme - -@Composable -fun HomeScrollToTopButton(onClick: () -> Unit, modifier: Modifier = Modifier) { - Box( - modifier = modifier - .background(shape = CircleShape, color = FindUTheme.colors.white) - .border( - width = 1.dp, - color = FindUTheme.colors.gray3, - shape = CircleShape - ) - .padding(11.dp) - .noRippleClickable(onClick), contentAlignment = Alignment.Center - ) { - BaseVectorIcon(vectorResource = R.drawable.ic_arrow_top_white_24, tint = FindUTheme.colors.gray4) - } -} - -@Preview -@Composable -private fun HomeScrollToTopButtonPreview() { - HomeScrollToTopButton( - onClick = {} - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/kuit/findu/presentation/ui/home/composeview/HomeScreen.kt b/app/src/main/java/com/kuit/findu/presentation/ui/home/composeview/HomeScreen.kt index 6ccdf2d9..214686a4 100644 --- a/app/src/main/java/com/kuit/findu/presentation/ui/home/composeview/HomeScreen.kt +++ b/app/src/main/java/com/kuit/findu/presentation/ui/home/composeview/HomeScreen.kt @@ -15,13 +15,10 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview @@ -38,7 +35,6 @@ import com.kuit.findu.presentation.ui.home.component.HomeProtectAnimalList import com.kuit.findu.presentation.ui.home.component.HomeReportCard import com.kuit.findu.presentation.ui.home.component.HomeReportDialog import com.kuit.findu.presentation.ui.home.component.HomeReportedAnimalList -import com.kuit.findu.presentation.ui.home.component.HomeScrollToTopButton import com.kuit.findu.presentation.ui.home.component.HomeTopBar import com.kuit.findu.presentation.ui.home.component.HomeWebLinkList import com.kuit.findu.presentation.ui.home.viewmodel.HomeUiState @@ -46,7 +42,6 @@ import com.kuit.findu.ui.theme.FindUTheme import com.google.accompanist.pager.ExperimentalPagerApi import com.google.accompanist.pager.rememberPagerState import kotlinx.coroutines.delay -import kotlinx.coroutines.launch @OptIn(ExperimentalPagerApi::class) @@ -72,17 +67,6 @@ fun HomeScreen( val bannerList = HomeBannerType.entries val pagerState = rememberPagerState() val listState = rememberLazyListState() - val coroutineScope = rememberCoroutineScope() - - val isLastItemVisible = remember { - derivedStateOf { - val layoutInfo = listState.layoutInfo - val totalItemsCount = layoutInfo.totalItemsCount - val lastVisibleItem = layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: -1 - - lastVisibleItem == totalItemsCount - 1 - } - } LaunchedEffect(pagerState) { while (true) { @@ -186,18 +170,6 @@ fun HomeScreen( } } } - if (isLastItemVisible.value) { - HomeScrollToTopButton( - onClick = { - coroutineScope.launch { - listState.animateScrollToItem(0) - } - }, - modifier = Modifier - .align(Alignment.BottomEnd) - .padding(bottom = 60.dp, end = 20.dp) - ) - } if (uiState.isReportDialogVisible) { HomeReportDialog( onDismissRequest = onReportDialogDismiss, @@ -237,4 +209,4 @@ private fun HomeScreenPreview() { navigateToHomeExtra = {}, ) } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/kuit/findu/presentation/ui/home/viewmodel/HomeViewModel.kt b/app/src/main/java/com/kuit/findu/presentation/ui/home/viewmodel/HomeViewModel.kt index ce7d67dc..c4cda3eb 100644 --- a/app/src/main/java/com/kuit/findu/presentation/ui/home/viewmodel/HomeViewModel.kt +++ b/app/src/main/java/com/kuit/findu/presentation/ui/home/viewmodel/HomeViewModel.kt @@ -7,6 +7,7 @@ import com.kuit.findu.data.dataremote.util.AuthenticationException import com.kuit.findu.domain.model.HomeData import com.kuit.findu.domain.model.ProtectAnimal import com.kuit.findu.domain.model.ReportAnimal +import com.kuit.findu.domain.usecase.GetIsGuestLoginUseCase import com.kuit.findu.domain.usecase.GetNicknameUseCase import com.kuit.findu.domain.usecase.home.GetHomeUseCase import com.kuit.findu.presentation.type.HomeReportDurationType @@ -32,7 +33,6 @@ data class HomeUiState( val nickname: String = "", val isRefreshing: Boolean = false, val bannerCurrentPage: Int = 0, - val isScrollToTopVisible: Boolean = false, val isReportDialogVisible: Boolean = false, val locationPermission: Boolean = false, ) { @@ -62,7 +62,6 @@ sealed class HomeUiEvent { data class OnBannerPageChanged(val page: Int) : HomeUiEvent() - data class OnScrollPositionChanged(val firstVisibleItemIndex: Int) : HomeUiEvent() data class SetLocationPermission(val locationPermission: Boolean) : HomeUiEvent() data object SetUserNickname : HomeUiEvent() @@ -87,6 +86,7 @@ sealed class HomeUiEffect { class HomeViewModel @Inject constructor( private val homeUseCase: GetHomeUseCase, private val getNicknameUseCase: GetNicknameUseCase, + private val getIsGuestLoginUseCase: GetIsGuestLoginUseCase, ) : ViewModel() { private val _uiState = MutableStateFlow(HomeUiState()) @@ -109,12 +109,17 @@ class HomeViewModel @Inject constructor( is HomeUiEvent.ClearError -> clearError() is HomeUiEvent.OnBannerPageChanged -> updateBannerPage(event.page) - is HomeUiEvent.OnScrollPositionChanged -> updateScrollToTopVisibility(event.firstVisibleItemIndex) is HomeUiEvent.OnAlarmButtonClick -> alarmButtonClicked() is HomeUiEvent.OnHomeReportDurationClick -> changeReportDuration(event.duration) is HomeUiEvent.OnReportDialogClick -> { - _uiState.update { it.copy(isReportDialogVisible = true) } + if (getIsGuestLoginUseCase()) { + viewModelScope.launch { + _uiEffect.send(HomeUiEffect.ShowToast("로그인 이후에 제보해주세요!")) + } + } else { + _uiState.update { it.copy(isReportDialogVisible = true) } + } } is HomeUiEvent.OnReportDialogDismiss -> { @@ -241,13 +246,21 @@ class HomeViewModel @Inject constructor( fun navigateToLostReport() { viewModelScope.launch { - _uiEffect.send(HomeUiEffect.NavigateToLostReport) + if (getIsGuestLoginUseCase()) { + _uiEffect.send(HomeUiEffect.ShowToast("로그인 이후에 제보해주세요!")) + } else { + _uiEffect.send(HomeUiEffect.NavigateToLostReport) + } } } fun navigateToFindReport() { viewModelScope.launch { - _uiEffect.send(HomeUiEffect.NavigateToFindReport) + if (getIsGuestLoginUseCase()) { + _uiEffect.send(HomeUiEffect.ShowToast("로그인 이후에 제보해주세요!")) + } else { + _uiEffect.send(HomeUiEffect.NavigateToFindReport) + } } } @@ -261,9 +274,4 @@ class HomeViewModel @Inject constructor( _uiState.update { it.copy(bannerCurrentPage = page) } } - private fun updateScrollToTopVisibility(firstVisibleItemIndex: Int) { - val isVisible = firstVisibleItemIndex > 2 - _uiState.update { it.copy(isScrollToTopVisible = isVisible) } - } - -} \ No newline at end of file +} diff --git a/app/src/main/java/com/kuit/findu/presentation/ui/login/composeview/LoginScreen.kt b/app/src/main/java/com/kuit/findu/presentation/ui/login/composeview/LoginScreen.kt index b3ea8688..c06aee10 100644 --- a/app/src/main/java/com/kuit/findu/presentation/ui/login/composeview/LoginScreen.kt +++ b/app/src/main/java/com/kuit/findu/presentation/ui/login/composeview/LoginScreen.kt @@ -2,7 +2,6 @@ package com.kuit.findu.presentation.ui.login.composeview import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -10,6 +9,7 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.material.Icon import androidx.compose.material.Text +import androidx.compose.material.TextButton import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -17,11 +17,11 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.kuit.findu.R import com.kuit.findu.presentation.util.extension.noRippleClickable -import com.kuit.findu.presentation.util.extension.roundedBackgroundWithPadding import com.kuit.findu.ui.theme.FindUTheme @Composable @@ -65,29 +65,27 @@ fun LoginScreen( contentDescription = null, tint = Color.Unspecified, ) - Spacer(modifier = Modifier.height(53.dp)) -// Icon( -// painter = painterResource(R.drawable.img_kakao_login), -// contentDescription = null, -// modifier = Modifier.noRippleClickable(), -// tint = Color.Unspecified -// ) - Spacer(modifier = Modifier.height(50.dp)) - Spacer(modifier = Modifier.height(15.dp)) - Text( - text = stringResource(R.string.login_without_signup), - textAlign = TextAlign.Center, - style = FindUTheme.typography.body1SemiBold16, - color = FindUTheme.colors.gray5, - modifier = Modifier - .fillMaxWidth() - .roundedBackgroundWithPadding( - backgroundColor = FindUTheme.colors.gray2, - cornerRadius = 8.dp, - padding = PaddingValues(vertical = 15.dp) - ) - .noRippleClickable(withoutSignUpButtonClicked) + Spacer(modifier = Modifier.height(100.dp)) + Icon( + painter = painterResource(R.drawable.img_kakao_login), + contentDescription = null, + modifier = Modifier.noRippleClickable(onClick = kakaoLoginButtonClicked), + tint = Color.Unspecified ) + Spacer(modifier = Modifier.height(15.dp)) + + TextButton( + onClick = withoutSignUpButtonClicked, + ) { + Text( + text = stringResource(R.string.login_without_signup), + textAlign = TextAlign.Center, + style = FindUTheme.typography.body1SemiBold16, + color = FindUTheme.colors.gray5, + textDecoration = TextDecoration.Underline, + modifier = Modifier + ) + } Spacer(modifier = Modifier.weight(1.5f)) } diff --git a/app/src/main/java/com/kuit/findu/presentation/ui/login/viewmodel/LoginViewModel.kt b/app/src/main/java/com/kuit/findu/presentation/ui/login/viewmodel/LoginViewModel.kt index 8480dd4d..b883ffe2 100644 --- a/app/src/main/java/com/kuit/findu/presentation/ui/login/viewmodel/LoginViewModel.kt +++ b/app/src/main/java/com/kuit/findu/presentation/ui/login/viewmodel/LoginViewModel.kt @@ -5,10 +5,13 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.kuit.findu.analytics.AnalyticsHelper import com.kuit.findu.analytics.logUserSignIn +import com.kuit.findu.data.dataremote.exception.ApiNotFoundException +import com.kuit.findu.domain.usecase.SetIsGuestLoginUseCase import com.kuit.findu.domain.usecase.SetNicknameUseCase import com.kuit.findu.domain.usecase.auth.PostGuestLoginUseCase import com.kuit.findu.domain.usecase.auth.PostLoginUseCase import com.kuit.findu.domain.usecase.token.SetAccessTokenUseCase +import com.kuit.findu.domain.usecase.token.SetRefreshTokenUseCase import com.kuit.findu.presentation.util.Nickname.GUEST_NAME import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableSharedFlow @@ -21,7 +24,9 @@ class LoginViewModel @Inject constructor( private val loginUseCase: PostLoginUseCase, private val guestLoginUseCase: PostGuestLoginUseCase, private val setAccessTokenUseCase: SetAccessTokenUseCase, + private val setRefreshTokenUseCase: SetRefreshTokenUseCase, private val setNicknameUseCase: SetNicknameUseCase, + private val setIsGuestLoginUseCase: SetIsGuestLoginUseCase, private val analyticsHelper: AnalyticsHelper, ) : ViewModel() { private val _startMainActivity = MutableSharedFlow() @@ -31,6 +36,9 @@ class LoginViewModel @Inject constructor( private val _startOnboardingActivity = MutableSharedFlow() val startOnboardingActivity: SharedFlow = _startOnboardingActivity + private val _errorMessage = MutableSharedFlow() + val errorMessage: SharedFlow = _errorMessage + fun postLogin(kakaoId: Long) { viewModelScope.launch { loginUseCase.postLogin(kakaoId = kakaoId).onSuccess { loginData -> @@ -41,7 +49,9 @@ class LoginViewModel @Inject constructor( userName = loginData.userInfo?.nickname ?: "Null Nickname", type = "kakao" ) setAccessTokenUseCase(accessToken = loginData.userInfo!!.accessToken) + setRefreshTokenUseCase(refreshToken = loginData.userInfo.refreshToken) setNicknameUseCase(nickname = loginData.userInfo.nickname) + setIsGuestLoginUseCase(isGuest = false) startMainActivity() } }.onFailure { e -> @@ -56,11 +66,18 @@ class LoginViewModel @Inject constructor( .onSuccess { loginData -> analyticsHelper.logUserSignIn(userName = GUEST_NAME, type = "Guest") setAccessTokenUseCase(accessToken = loginData.accessToken) + setRefreshTokenUseCase(refreshToken = loginData.refreshToken) setNicknameUseCase(nickname = GUEST_NAME) + setIsGuestLoginUseCase(isGuest = true) onSuccess() startMainActivity() } .onFailure { e -> + if(e is ApiNotFoundException) { + viewModelScope.launch { + _errorMessage.emit("가입된 계정이 있습니다.\n카카오 계정으로 로그인해주세요.") + } + } Log.d("http", "Error Message: : $e") } } @@ -78,6 +95,5 @@ class LoginViewModel @Inject constructor( _startOnboardingActivity.emit(kakaoId) } } - } diff --git a/app/src/main/java/com/kuit/findu/presentation/ui/main/MainActivity.kt b/app/src/main/java/com/kuit/findu/presentation/ui/main/MainActivity.kt index f1cc619f..77a38283 100644 --- a/app/src/main/java/com/kuit/findu/presentation/ui/main/MainActivity.kt +++ b/app/src/main/java/com/kuit/findu/presentation/ui/main/MainActivity.kt @@ -49,7 +49,6 @@ class MainActivity : AppCompatActivity() { binding.bnvMain.visibility = when (destination.id) { R.id.fragment_home, R.id.fragment_search, R.id.fragment_info, R.id.fragment_my -> View.VISIBLE - R.id.fragment_search_detail_witness, R.id.fragment_search_detail_disappear, R.id.fragment_search_detail_protecting -> View.VISIBLE else -> View.GONE } } diff --git a/app/src/main/java/com/kuit/findu/presentation/ui/my/MyFragment.kt b/app/src/main/java/com/kuit/findu/presentation/ui/my/MyFragment.kt index 5db589ab..1648b277 100644 --- a/app/src/main/java/com/kuit/findu/presentation/ui/my/MyFragment.kt +++ b/app/src/main/java/com/kuit/findu/presentation/ui/my/MyFragment.kt @@ -122,7 +122,11 @@ class MyFragment : Fragment() { } btnMyReportHistory.setOnClickListener { - findNavController().navigate(R.id.action_fragment_my_to_fragment_my_report_history) + if (myViewModel.isGuest()) { + Toast.makeText(requireContext(), "로그인 이후에 이용해주세요!", Toast.LENGTH_SHORT).show() + } else { + findNavController().navigate(R.id.action_fragment_my_to_fragment_my_report_history) + } } clMyRecentHistory.setOnClickListener { @@ -175,7 +179,6 @@ class MyFragment : Fragment() { context = requireContext(), onWithdrawalClick = { myViewModel.deleteUserData() - myViewModel.clearToken() with(requireActivity()) { startActivity(Intent(requireContext(), LoginActivity::class.java)) finish() diff --git a/app/src/main/java/com/kuit/findu/presentation/ui/my/viewmodel/MyViewModel.kt b/app/src/main/java/com/kuit/findu/presentation/ui/my/viewmodel/MyViewModel.kt index 977b43d8..35217ad5 100644 --- a/app/src/main/java/com/kuit/findu/presentation/ui/my/viewmodel/MyViewModel.kt +++ b/app/src/main/java/com/kuit/findu/presentation/ui/my/viewmodel/MyViewModel.kt @@ -7,6 +7,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.kuit.findu.domain.model.my.MyProfileData import com.kuit.findu.domain.model.my.MyProfileImageUpdate +import com.kuit.findu.domain.usecase.GetIsGuestLoginUseCase import com.kuit.findu.domain.usecase.interest.DeleteInterestAnimalUseCase import com.kuit.findu.domain.usecase.interest.PostInterestAnimalUseCase import com.kuit.findu.domain.usecase.my.DeleteUserUseCase @@ -46,6 +47,7 @@ class MyViewModel @Inject constructor( private val deleteReportUseCase: DeleteReportUseCase, private val patchProfileImageUseCase: PatchProfileImageUseCase, private val clearTokenUseCase: ClearTokenUseCase, + private val getIsGuestLoginUseCase: GetIsGuestLoginUseCase, ) : ViewModel() { private val _interestAnimals = MutableStateFlow>(emptyList()) @@ -128,6 +130,7 @@ class MyViewModel @Inject constructor( viewModelScope.launch { deleteUserUseCase().fold( onSuccess = { + clearToken() _deleteUserMessage.value = "회원 탈퇴가 완료되었습니다." }, onFailure = { @@ -250,4 +253,6 @@ class MyViewModel @Inject constructor( } } + fun isGuest(): Boolean = getIsGuestLoginUseCase() + } \ No newline at end of file diff --git a/app/src/main/java/com/kuit/findu/presentation/ui/onboarding/component/OnboardingNickname.kt b/app/src/main/java/com/kuit/findu/presentation/ui/onboarding/component/OnboardingNickname.kt index ade59e9e..a012a7e9 100644 --- a/app/src/main/java/com/kuit/findu/presentation/ui/onboarding/component/OnboardingNickname.kt +++ b/app/src/main/java/com/kuit/findu/presentation/ui/onboarding/component/OnboardingNickname.kt @@ -59,7 +59,7 @@ fun OnboardingNickname( .fillMaxWidth() ) { // NOTE: 페이지 아이콘 첫/끝 요소 여백 불균형으로 임시 여백 삽입 (디자인 수정 시 제거 예정) - BaseVectorIcon(vectorResource = R.drawable.ic_onboarding_page_last, modifier = Modifier.padding(start = 4.dp)) +// BaseVectorIcon(vectorResource = R.drawable.ic_onboarding_page_last, modifier = Modifier.padding(start = 4.dp)) Spacer(modifier = Modifier.height(40.dp)) Text( text = stringResource(R.string.onboarding_nickname_title_first), diff --git a/app/src/main/java/com/kuit/findu/presentation/ui/onboarding/composeview/OnboardingScreen.kt b/app/src/main/java/com/kuit/findu/presentation/ui/onboarding/composeview/OnboardingScreen.kt index 982332c8..ca936cd7 100644 --- a/app/src/main/java/com/kuit/findu/presentation/ui/onboarding/composeview/OnboardingScreen.kt +++ b/app/src/main/java/com/kuit/findu/presentation/ui/onboarding/composeview/OnboardingScreen.kt @@ -56,14 +56,15 @@ fun OnboardingScreen( focusManager.clearFocus() } ) { - BaseVectorIcon( - vectorResource = R.drawable.ic_arrow_back_24, - modifier = Modifier - .padding(horizontal = 20.dp, vertical = 18.dp) - .noRippleClickable(backButtonClicked) - ) +// BaseVectorIcon( +// vectorResource = R.drawable.ic_arrow_back_24, +// modifier = Modifier +// .padding(horizontal = 20.dp, vertical = 18.dp) +// .noRippleClickable(backButtonClicked) +// ) + Spacer(modifier = Modifier.height(110.dp)) Spacer(modifier = Modifier.height(12.dp)) - when (uiState.pageState) { + /*when (uiState.pageState) { 1 -> OnboardingProfile( modifier = Modifier.padding(horizontal = 20.dp), defaultProfileType = uiState.defaultProfileType, @@ -73,7 +74,7 @@ fun OnboardingScreen( clearProfileImage = clearProfileImage ) - 2 -> OnboardingNickname( + 2 -> */OnboardingNickname( modifier = Modifier.padding(horizontal = 20.dp), nicknameValueChanged = { nickname -> nicknameValueChanged(nickname) @@ -86,7 +87,7 @@ fun OnboardingScreen( }, focusChanged = { focusChanged(it) } ) - } +// } Spacer(modifier = Modifier.weight(1f)) OnboardingButton( enabled = isNextButtonEnabled, diff --git a/app/src/main/java/com/kuit/findu/presentation/ui/onboarding/viewmodel/OnboardingViewModel.kt b/app/src/main/java/com/kuit/findu/presentation/ui/onboarding/viewmodel/OnboardingViewModel.kt index 779f3b49..6cab8657 100644 --- a/app/src/main/java/com/kuit/findu/presentation/ui/onboarding/viewmodel/OnboardingViewModel.kt +++ b/app/src/main/java/com/kuit/findu/presentation/ui/onboarding/viewmodel/OnboardingViewModel.kt @@ -7,8 +7,11 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.kuit.findu.analytics.AnalyticsHelper import com.kuit.findu.analytics.logUserSignUp +import com.kuit.findu.domain.usecase.SetIsGuestLoginUseCase import com.kuit.findu.domain.usecase.auth.PostCheckNicknameUseCase import com.kuit.findu.domain.usecase.auth.PostSignupUseCase +import com.kuit.findu.domain.usecase.token.SetAccessTokenUseCase +import com.kuit.findu.domain.usecase.token.SetRefreshTokenUseCase import com.kuit.findu.presentation.type.DefaultProfileType import com.kuit.findu.presentation.type.NicknameValidType import dagger.hilt.android.lifecycle.HiltViewModel @@ -24,7 +27,7 @@ import java.io.File import javax.inject.Inject data class OnboardingUiState( - val pageState: Int = 1, + val pageState: Int = 2, val kakaoId: Long = -1L, val profileImageUri: Uri? = null, val defaultProfileType: DefaultProfileType = DefaultProfileType.DEFAULT, @@ -37,6 +40,9 @@ class OnboardingViewModel @Inject constructor( @ApplicationContext private val context: Context, private val postCheckNicknameUseCase: PostCheckNicknameUseCase, private val postSignupUseCase: PostSignupUseCase, + private val setAccessTokenUseCase: SetAccessTokenUseCase, + private val setRefreshTokenUseCase: SetRefreshTokenUseCase, + private val setIsGuestLoginUseCase: SetIsGuestLoginUseCase, private val analyticsHelper: AnalyticsHelper, ) : ViewModel() { private val _uiState = MutableStateFlow(OnboardingUiState()) @@ -122,8 +128,11 @@ class OnboardingViewModel @Inject constructor( defaultImageName = uiState.value.defaultProfileType.string, nickname = uiState.value.nickname, kakaoId = uiState.value.kakaoId - ).onSuccess { + ).onSuccess { data -> analyticsHelper.logUserSignUp(userName = uiState.value.nickname) + setAccessTokenUseCase(data.accessToken) + setRefreshTokenUseCase(data.refreshToken) + setIsGuestLoginUseCase(false) startMainActivity() }.onFailure { e -> Log.d("http", "Error Message: : $e") diff --git a/app/src/main/java/com/kuit/findu/presentation/ui/search/adapter/SearchListAdapter.kt b/app/src/main/java/com/kuit/findu/presentation/ui/search/adapter/SearchListAdapter.kt index bdead7d3..68b3910f 100644 --- a/app/src/main/java/com/kuit/findu/presentation/ui/search/adapter/SearchListAdapter.kt +++ b/app/src/main/java/com/kuit/findu/presentation/ui/search/adapter/SearchListAdapter.kt @@ -19,7 +19,7 @@ sealed class SearchListItem { } class SearchListAdapter( - private val listener: SearchListListener + private val listener: SearchListListener, ) : ListAdapter(DIFF) { companion object { @@ -28,16 +28,23 @@ class SearchListAdapter( const val VIEW_TYPE_GRID = 1 private val DIFF = object : DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: SearchListItem, newItem: SearchListItem): Boolean { + override fun areItemsTheSame( + oldItem: SearchListItem, + newItem: SearchListItem, + ): Boolean { return when { oldItem is SearchListItem.Header && newItem is SearchListItem.Header -> true oldItem is SearchListItem.Content && newItem is SearchListItem.Content -> oldItem.data.reportId == newItem.data.reportId + else -> false } } - override fun areContentsTheSame(oldItem: SearchListItem, newItem: SearchListItem): Boolean { + override fun areContentsTheSame( + oldItem: SearchListItem, + newItem: SearchListItem, + ): Boolean { return oldItem == newItem } } @@ -65,10 +72,12 @@ class SearchListAdapter( ItemSearchHeaderBinding.inflate(inflater, parent, false), listener ) + VIEW_TYPE_GRID -> BaseVH.GridVH( ItemSearchGridContentBinding.inflate(inflater, parent, false), listener ) + else -> BaseVH.HorizontalVH( SearchHorizontalContentItemBinding.inflate(inflater, parent, false), listener @@ -87,7 +96,7 @@ class SearchListAdapter( sealed class BaseVH(bindingRoot: ViewGroup) : RecyclerView.ViewHolder(bindingRoot) { class HeaderVH( private val binding: ItemSearchHeaderBinding, - private val listener: SearchListListener + private val listener: SearchListListener, ) : BaseVH(binding.root as ViewGroup) { private var isGrid = false fun bind() = with(binding) { @@ -106,13 +115,15 @@ class SearchListAdapter( class HorizontalVH( private val binding: SearchHorizontalContentItemBinding, - private val listener: SearchListListener + private val listener: SearchListListener, ) : BaseVH(binding.root as ViewGroup) { fun bind(item: SearchRv) = with(binding) { tvSearchContentName.text = item.name - tvSearchContentDate.text = item.date + tvSearchFoundDate.text = item.date + tvSearchContentDate.text = item.createdAt tvSearchContentAddress.text = item.location tvSearchContentStatus.text = item.tag.text + tvSearchFoundDateTitle.text = item.tag.dateTag tvSearchContentStatus.setTextColor(root.context.getColor(item.tag.textColor)) tvSearchContentStatus.setBackgroundResource(item.tag.backgroundRes) updateBookmarkIcon(item.isBookmark) @@ -143,13 +154,15 @@ class SearchListAdapter( class GridVH( private val binding: ItemSearchGridContentBinding, - private val listener: SearchListListener + private val listener: SearchListListener, ) : BaseVH(binding.root as ViewGroup) { fun bind(item: SearchRv) = with(binding) { tvSearchContentName.text = item.name - tvSearchContentDate.text = item.date + tvSearchFoundDate.text = item.date + tvSearchContentDate.text = item.createdAt tvSearchContentAddress.text = item.location tvSearchContentStatus.text = item.tag.text + tvSearchFoundDateTitle.text = item.tag.dateTag tvSearchContentStatus.setTextColor(root.context.getColor(item.tag.textColor)) tvSearchContentStatus.setBackgroundResource(item.tag.backgroundRes) updateBookmarkIcon(item.isBookmark) diff --git a/app/src/main/java/com/kuit/findu/presentation/ui/search/detail/SearchDisappearDetailFragment.kt b/app/src/main/java/com/kuit/findu/presentation/ui/search/detail/SearchDisappearDetailFragment.kt index e8e81a2f..401ce5de 100644 --- a/app/src/main/java/com/kuit/findu/presentation/ui/search/detail/SearchDisappearDetailFragment.kt +++ b/app/src/main/java/com/kuit/findu/presentation/ui/search/detail/SearchDisappearDetailFragment.kt @@ -97,7 +97,7 @@ class SearchDisappearDetailFragment : Fragment() { position = location this.map = map icon = OverlayImage.fromResource(R.drawable.ic_search_map_marker) - height = 23 + height = 40 } } @@ -145,7 +145,14 @@ class SearchDisappearDetailFragment : Fragment() { if (data.imageUrls.isNotEmpty()) { initViewPager(data.imageUrls) } - setupMap(data.latitude, data.longitude) + + // latitude, longitude가 null이면 지도 숨기기 + if (data.latitude != null && data.longitude != null) { + clSearchMap.visibility = View.VISIBLE + setupMap(data.latitude, data.longitude) + } else { + clSearchMap.visibility = View.GONE + } } } diff --git a/app/src/main/java/com/kuit/findu/presentation/ui/search/detail/SearchProtectingDetailFragment.kt b/app/src/main/java/com/kuit/findu/presentation/ui/search/detail/SearchProtectingDetailFragment.kt index 4390ef68..12347495 100644 --- a/app/src/main/java/com/kuit/findu/presentation/ui/search/detail/SearchProtectingDetailFragment.kt +++ b/app/src/main/java/com/kuit/findu/presentation/ui/search/detail/SearchProtectingDetailFragment.kt @@ -139,19 +139,26 @@ class SearchProtectingDetailFragment : Fragment() { tvValueFoundDate.text = data.foundDate tvValueHairColor.text = data.furColor tvSpecialNote.text = data.significant - tvShelterLocation.text = data.careAddr tvValueShelterName.text = data.careName tvValueNotiDate.text = data.noticeDuration tvValueNotiNum.text = data.noticeNumber tvValueShelterPhoneNumber.text = data.careTel tvValueJurisdiction.text = data.authority - tvValueProtectLocation.text = data.foundLocation.ifBlank { data.careAddr } + tvValueProtectLocation.text = data.careAddr + tvValueFoundLocation.text = data.foundLocation initTagView(data.tag) if (data.imageUrls.isNotEmpty()) { initViewPager(data.imageUrls) } - setupMap(data.latitude, data.longitude) + + // latitude, longitude가 null이면 지도 숨기기 + if (data.latitude != null && data.longitude != null) { + clSearchMap.visibility = View.VISIBLE + setupMap(data.latitude, data.longitude) + } else { + clSearchMap.visibility = View.GONE + } } } diff --git a/app/src/main/java/com/kuit/findu/presentation/ui/search/detail/SearchWitnessDetailFragment.kt b/app/src/main/java/com/kuit/findu/presentation/ui/search/detail/SearchWitnessDetailFragment.kt index 08f77d81..4b788efe 100644 --- a/app/src/main/java/com/kuit/findu/presentation/ui/search/detail/SearchWitnessDetailFragment.kt +++ b/app/src/main/java/com/kuit/findu/presentation/ui/search/detail/SearchWitnessDetailFragment.kt @@ -139,7 +139,14 @@ class SearchWitnessDetailFragment : Fragment() { if (data.imageUrls.isNotEmpty()) { initViewPager(data.imageUrls) } - setupMap(data.latitude, data.longitude) + + // latitude, longitude가 null이면 지도 숨기기 + if (data.latitude != null && data.longitude != null) { + clSearchMap.visibility = View.VISIBLE + setupMap(data.latitude, data.longitude) + } else { + clSearchMap.visibility = View.GONE + } } } diff --git a/app/src/main/java/com/kuit/findu/presentation/ui/search/model/SearchRv.kt b/app/src/main/java/com/kuit/findu/presentation/ui/search/model/SearchRv.kt index f50d5d55..b33e3954 100644 --- a/app/src/main/java/com/kuit/findu/presentation/ui/search/model/SearchRv.kt +++ b/app/src/main/java/com/kuit/findu/presentation/ui/search/model/SearchRv.kt @@ -9,5 +9,6 @@ data class SearchRv( val location : String, var isBookmark : Boolean, var tag : SearchRvTag, - val reportId: Long + val reportId: Long, + val createdAt: String, ) : Serializable diff --git a/app/src/main/java/com/kuit/findu/presentation/ui/search/model/SearchRvTag.kt b/app/src/main/java/com/kuit/findu/presentation/ui/search/model/SearchRvTag.kt index e9bcad0f..34b3a2cb 100644 --- a/app/src/main/java/com/kuit/findu/presentation/ui/search/model/SearchRvTag.kt +++ b/app/src/main/java/com/kuit/findu/presentation/ui/search/model/SearchRvTag.kt @@ -6,10 +6,11 @@ import java.io.Serializable enum class SearchRvTag( val text: String, val textColor: Int, - val backgroundRes: Int + val backgroundRes: Int, + val dateTag: String ) : Serializable { - PROTECTING("보호중", R.color.green1, R.drawable.bg_search_protecting_tag), - WITNESS("목격신고", R.color.blue1, R.drawable.bg_search_report_tag), - MISSING("실종신고", R.color.red1, R.drawable.bg_search_lost_tag), - UNKNOWN("기본",R.color.white, R.drawable.bg_search_protecting_tag) + PROTECTING("보호중", R.color.green1, R.drawable.bg_search_protecting_tag, "발견날짜: "), + WITNESS("목격신고", R.color.blue1, R.drawable.bg_search_report_tag, "신고날짜: "), + MISSING("실종신고", R.color.red1, R.drawable.bg_search_lost_tag, "신고날짜: "), + UNKNOWN("기본",R.color.white, R.drawable.bg_search_protecting_tag, "") } \ No newline at end of file diff --git a/app/src/main/java/com/kuit/findu/presentation/ui/search/tablayout/SearchAllFragment.kt b/app/src/main/java/com/kuit/findu/presentation/ui/search/tablayout/SearchAllFragment.kt index 2356bb9c..1ff3d2ed 100644 --- a/app/src/main/java/com/kuit/findu/presentation/ui/search/tablayout/SearchAllFragment.kt +++ b/app/src/main/java/com/kuit/findu/presentation/ui/search/tablayout/SearchAllFragment.kt @@ -119,7 +119,8 @@ class SearchAllFragment : Fragment(), SearchListListener { location = item.location, isBookmark = item.interest, tag = item.tag.toSearchRvTag(), - reportId = item.reportId + reportId = item.reportId, + createdAt = item.createdAt, ) } diff --git a/app/src/main/java/com/kuit/findu/presentation/ui/search/tablayout/SearchReportFragment.kt b/app/src/main/java/com/kuit/findu/presentation/ui/search/tablayout/SearchReportFragment.kt index 4cb79c7e..0503fae3 100644 --- a/app/src/main/java/com/kuit/findu/presentation/ui/search/tablayout/SearchReportFragment.kt +++ b/app/src/main/java/com/kuit/findu/presentation/ui/search/tablayout/SearchReportFragment.kt @@ -118,7 +118,8 @@ class SearchReportFragment : Fragment(), SearchListListener { location = item.location, isBookmark = item.interest, tag = item.tag.toSearchRvTag(), - reportId = item.reportId + reportId = item.reportId, + createdAt = item.createdAt, ) } diff --git a/app/src/main/java/com/kuit/findu/presentation/ui/search/tablayout/SearchRescueFragment.kt b/app/src/main/java/com/kuit/findu/presentation/ui/search/tablayout/SearchRescueFragment.kt index 4c887848..7c66fc40 100644 --- a/app/src/main/java/com/kuit/findu/presentation/ui/search/tablayout/SearchRescueFragment.kt +++ b/app/src/main/java/com/kuit/findu/presentation/ui/search/tablayout/SearchRescueFragment.kt @@ -119,7 +119,8 @@ class SearchRescueFragment : Fragment(), SearchListListener { location = item.location, isBookmark = item.interest, tag = item.tag.toSearchRvTag(), - reportId = item.reportId + reportId = item.reportId, + createdAt = item.createdAt, ) } diff --git a/app/src/main/res/drawable/bg_radius_20.xml b/app/src/main/res/drawable/bg_radius_20.xml index 38bfa96b..c00ca712 100644 --- a/app/src/main/res/drawable/bg_radius_20.xml +++ b/app/src/main/res/drawable/bg_radius_20.xml @@ -1,7 +1,6 @@ - diff --git a/app/src/main/res/drawable/ic_location_pin.xml b/app/src/main/res/drawable/ic_location_pin.xml index 2632f9f2..ac47d0d5 100644 --- a/app/src/main/res/drawable/ic_location_pin.xml +++ b/app/src/main/res/drawable/ic_location_pin.xml @@ -5,7 +5,7 @@ android:viewportHeight="31"> - + diff --git a/app/src/main/res/drawable/ic_search_call.xml b/app/src/main/res/drawable/ic_search_call.xml index 75a6ade6..33b12b32 100644 --- a/app/src/main/res/drawable/ic_search_call.xml +++ b/app/src/main/res/drawable/ic_search_call.xml @@ -1,5 +1,5 @@ - + diff --git a/app/src/main/res/drawable/ic_search_detail_location.xml b/app/src/main/res/drawable/ic_search_detail_location.xml index e904dd97..1d218585 100644 --- a/app/src/main/res/drawable/ic_search_detail_location.xml +++ b/app/src/main/res/drawable/ic_search_detail_location.xml @@ -5,6 +5,6 @@ android:viewportHeight="16"> diff --git a/app/src/main/res/drawable/ic_search_fill_bookmark.xml b/app/src/main/res/drawable/ic_search_fill_bookmark.xml index fd5fceb0..da7dbabc 100644 --- a/app/src/main/res/drawable/ic_search_fill_bookmark.xml +++ b/app/src/main/res/drawable/ic_search_fill_bookmark.xml @@ -7,7 +7,7 @@ android:pathData="M15.792,19.875L8.5,14.667L1.208,19.875V3.208C1.208,2.656 1.428,2.126 1.819,1.735C2.209,1.344 2.739,1.125 3.292,1.125H13.708C14.261,1.125 14.791,1.344 15.182,1.735C15.572,2.126 15.792,2.656 15.792,3.208V19.875Z" android:strokeLineJoin="round" android:strokeWidth="2" - android:fillColor="#FFA938" - android:strokeColor="#FFA938" + android:fillColor="#FF9000" + android:strokeColor="#FF9000" android:strokeLineCap="round"/> diff --git a/app/src/main/res/drawable/ic_search_phone.xml b/app/src/main/res/drawable/ic_search_phone.xml index dad1fd63..c29ea3ae 100644 --- a/app/src/main/res/drawable/ic_search_phone.xml +++ b/app/src/main/res/drawable/ic_search_phone.xml @@ -7,7 +7,7 @@ android:pathData="M16.22,14.642C16.22,14.642 15.254,15.591 15.017,15.869C14.632,16.28 14.178,16.474 13.583,16.474C13.525,16.474 13.464,16.474 13.407,16.47C12.274,16.398 11.22,15.956 10.43,15.579C8.27,14.536 6.373,13.054 4.797,11.176C3.496,9.611 2.626,8.164 2.049,6.61C1.694,5.662 1.565,4.923 1.622,4.226C1.66,3.78 1.832,3.411 2.149,3.095L3.45,1.796C3.637,1.621 3.835,1.525 4.03,1.525C4.27,1.525 4.465,1.67 4.587,1.792C4.591,1.796 4.595,1.8 4.599,1.803C4.832,2.021 5.053,2.245 5.286,2.485C5.404,2.607 5.526,2.729 5.648,2.855L6.69,3.894C7.095,4.298 7.095,4.671 6.69,5.075C6.579,5.185 6.473,5.296 6.362,5.403C6.041,5.73 6.293,5.479 5.961,5.776C5.953,5.783 5.946,5.787 5.942,5.795C5.614,6.122 5.675,6.442 5.744,6.659C5.747,6.671 5.751,6.682 5.755,6.694C6.026,7.349 6.408,7.966 6.988,8.701L6.992,8.705C8.045,10 9.155,11.009 10.38,11.782C10.537,11.881 10.697,11.961 10.85,12.037C10.987,12.106 11.117,12.17 11.228,12.239C11.243,12.247 11.258,12.258 11.274,12.266C11.403,12.33 11.525,12.361 11.651,12.361C11.968,12.361 12.167,12.163 12.231,12.098L12.979,11.352C13.109,11.222 13.315,11.066 13.556,11.066C13.792,11.066 13.987,11.215 14.105,11.344C14.109,11.348 14.109,11.348 14.113,11.352L16.216,13.45C16.609,13.839 16.22,14.642 16.22,14.642Z" android:strokeLineJoin="round" android:strokeWidth="2" - android:fillColor="#FFA938" - android:strokeColor="#FFA938" + android:fillColor="#FF9000" + android:strokeColor="#FF9000" android:strokeLineCap="round"/> diff --git a/app/src/main/res/drawable/icon_home_find_report.xml b/app/src/main/res/drawable/icon_home_find_report.xml index 4d7be56d..1e06d36e 100644 --- a/app/src/main/res/drawable/icon_home_find_report.xml +++ b/app/src/main/res/drawable/icon_home_find_report.xml @@ -8,6 +8,6 @@ android:strokeLineJoin="round" android:strokeWidth="2" android:fillColor="#00000000" - android:strokeColor="#FFA938" + android:strokeColor="#FF9000" android:strokeLineCap="round"/> diff --git a/app/src/main/res/drawable/icon_report_location_map_orange.xml b/app/src/main/res/drawable/icon_report_location_map_orange.xml index 6d3c8575..04588514 100644 --- a/app/src/main/res/drawable/icon_report_location_map_orange.xml +++ b/app/src/main/res/drawable/icon_report_location_map_orange.xml @@ -5,6 +5,6 @@ android:viewportHeight="17"> diff --git a/app/src/main/res/drawable/search_detail_phone.xml b/app/src/main/res/drawable/search_detail_phone.xml index dad1fd63..c29ea3ae 100644 --- a/app/src/main/res/drawable/search_detail_phone.xml +++ b/app/src/main/res/drawable/search_detail_phone.xml @@ -7,7 +7,7 @@ android:pathData="M16.22,14.642C16.22,14.642 15.254,15.591 15.017,15.869C14.632,16.28 14.178,16.474 13.583,16.474C13.525,16.474 13.464,16.474 13.407,16.47C12.274,16.398 11.22,15.956 10.43,15.579C8.27,14.536 6.373,13.054 4.797,11.176C3.496,9.611 2.626,8.164 2.049,6.61C1.694,5.662 1.565,4.923 1.622,4.226C1.66,3.78 1.832,3.411 2.149,3.095L3.45,1.796C3.637,1.621 3.835,1.525 4.03,1.525C4.27,1.525 4.465,1.67 4.587,1.792C4.591,1.796 4.595,1.8 4.599,1.803C4.832,2.021 5.053,2.245 5.286,2.485C5.404,2.607 5.526,2.729 5.648,2.855L6.69,3.894C7.095,4.298 7.095,4.671 6.69,5.075C6.579,5.185 6.473,5.296 6.362,5.403C6.041,5.73 6.293,5.479 5.961,5.776C5.953,5.783 5.946,5.787 5.942,5.795C5.614,6.122 5.675,6.442 5.744,6.659C5.747,6.671 5.751,6.682 5.755,6.694C6.026,7.349 6.408,7.966 6.988,8.701L6.992,8.705C8.045,10 9.155,11.009 10.38,11.782C10.537,11.881 10.697,11.961 10.85,12.037C10.987,12.106 11.117,12.17 11.228,12.239C11.243,12.247 11.258,12.258 11.274,12.266C11.403,12.33 11.525,12.361 11.651,12.361C11.968,12.361 12.167,12.163 12.231,12.098L12.979,11.352C13.109,11.222 13.315,11.066 13.556,11.066C13.792,11.066 13.987,11.215 14.105,11.344C14.109,11.348 14.109,11.348 14.113,11.352L16.216,13.45C16.609,13.839 16.22,14.642 16.22,14.642Z" android:strokeLineJoin="round" android:strokeWidth="2" - android:fillColor="#FFA938" - android:strokeColor="#FFA938" + android:fillColor="#FF9000" + android:strokeColor="#FF9000" android:strokeLineCap="round"/> diff --git a/app/src/main/res/layout/activity_splash.xml b/app/src/main/res/layout/activity_splash.xml index 24818020..e534a37a 100644 --- a/app/src/main/res/layout/activity_splash.xml +++ b/app/src/main/res/layout/activity_splash.xml @@ -5,7 +5,7 @@ android:id="@+id/splash" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/main_color" + android:background="@color/main_color_deprecated" tools:context=".presentation.ui.splash.SplashActivity"> diff --git a/app/src/main/res/layout/fragment_search_detail_protecting.xml b/app/src/main/res/layout/fragment_search_detail_protecting.xml index e43971ee..dd2085c9 100644 --- a/app/src/main/res/layout/fragment_search_detail_protecting.xml +++ b/app/src/main/res/layout/fragment_search_detail_protecting.xml @@ -322,25 +322,38 @@ app:layout_constraintStart_toEndOf="@id/tv_value_shelter_name" app:layout_constraintTop_toTopOf="@id/tv_value_shelter_name" /> + + + tools:layout_editor_absoluteX="0dp" + tools:layout_editor_absoluteY="0dp" /> + tools:layout_editor_absoluteX="0dp" + tools:layout_editor_absoluteY="0dp"> + + + + + + + app:layout_constraintTop_toBottomOf="@id/tv_search_found_date"> @@ -69,6 +69,15 @@ app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" /> + + + + + + app:layout_constraintTop_toBottomOf="@id/tv_search_found_date"> - #FFA938 + #FFA938 + #FF9000 #FFEED9 #EF4346 diff --git a/app/src/main/res/values/ic_launcher_background.xml b/app/src/main/res/values/ic_launcher_background.xml index 3dae097e..4bd1fb3c 100644 --- a/app/src/main/res/values/ic_launcher_background.xml +++ b/app/src/main/res/values/ic_launcher_background.xml @@ -1,4 +1,4 @@ - #FFA938 + #FF9000 \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f55120c8..8b910071 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -14,7 +14,7 @@ 누군가의 세상이 됩니다. 유기동물 관련 어플리케이션 가입없이 시작하기 - 가입없이 찾아유 실행 + 찾아유에 오신 것을 환영합니다!