Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/#10 홈 화면 구현 #36

Merged
merged 59 commits into from
Jan 7, 2024
Merged
Show file tree
Hide file tree
Changes from 58 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
f854667
#26 네트워크 기초 작업 진행
JJJoonngg Oct 18, 2023
cb9ab61
feat: Hilt로 DI 라이브러리 전환 후, 홈 화면 네비게이션 이동 로직 구현. #9
bamin0422 Oct 18, 2023
5f647ca
feat: 스토리 recyclerView UI 구현 및 emotion 이미지 추가 #9
bamin0422 Oct 21, 2023
61c2816
refactor: 스토리 recyclerView 명명 변경 #10
bamin0422 Oct 21, 2023
df51ab0
feat: 홈 프래그먼트 기본 레이아웃 배치 완료 #10
bamin0422 Oct 22, 2023
d77d56c
feat: 가족별 감정 리스트 RecyclerView 더미데이터 적용 #10
bamin0422 Oct 23, 2023
aff6ac8
feat: D-day RecyclerView로 구현 #10
bamin0422 Oct 23, 2023
47b02dd
feat: ViewPager 스크롤과 유사한 형태로 아이템 노출되도록 ScrollListener 추가 #10
bamin0422 Oct 24, 2023
7f016d4
feat: 가족사진 RecyclerView 추가 #10
bamin0422 Oct 24, 2023
adc78d5
feat: 가족사진 RecyclerView 추가 #10
bamin0422 Oct 24, 2023
1229b39
feat: 서로에게 한마디 RecyclerView 구현 #10
bamin0422 Oct 24, 2023
8ca2caf
fix: 애니메이션 이슈로 인한 overScrollModel never 값으로 수정
bamin0422 Oct 25, 2023
f4a4f7e
feat: emotion 선택 화면 View 구성
bamin0422 Nov 1, 2023
6e14a0f
fix: HomeFragment 디자인 수정 #10
bamin0422 Nov 2, 2023
ac99d21
feat: HomeFragment 화면별 statusBar color 조정 #10
bamin0422 Nov 2, 2023
8dabdab
fix: 디자인 수정 - 말풍선 margin 값 수정 item #10
bamin0422 Nov 2, 2023
a290fdc
feat: NoticeActivity, EmotionActivity 구성 및 디자인 수정 #10
bamin0422 Nov 2, 2023
52c1d29
feat: EmotionActivity 구현 #33
bamin0422 Nov 8, 2023
75258ec
fix: 감정 선택 로직 수정 (감정 없음도 포함) #33
bamin0422 Nov 8, 2023
6ca6b75
fix: 감정 선택 로직 수정 (감정 없음도 포함 재수정) #33
bamin0422 Nov 8, 2023
3eba04c
fix: recyclerview overScrollMode never로 설정 #33
bamin0422 Nov 8, 2023
695e481
feat: 가족사진 RecyclerView 멀티 뷰타입으로 구현 #32
bamin0422 Nov 9, 2023
fa47ecc
Merge pull request #38 from TeamOwori/feat/#32-PhotoRecyclerView-기능-구현
bamin0422 Nov 9, 2023
21e1f5e
Merge pull request #37 from TeamOwori/feat/#33-EmotionActivity-구현
bamin0422 Nov 9, 2023
018d854
feat: 서로에게 한마디 삭제, 수정, 작성, 수정취소, 수정완료 기능 구현 #32
bamin0422 Dec 5, 2023
bf2effa
feat: 서로에게 한마디 문자열 추가 #32
bamin0422 Dec 5, 2023
aedadf2
feat: 서로에게 한마디 로직 개선 #32
bamin0422 Dec 12, 2023
d6d4093
feat: 서로에게 한마디 editText 정렬 개선 #32
bamin0422 Dec 12, 2023
739c0a4
feat: 서로에게 한마디 editText imeOptions 설정 #32
bamin0422 Dec 12, 2023
6cd5b19
feat: 서로에게 Dialog 기능 구현 #32
bamin0422 Dec 12, 2023
2ab5d6d
feat: Dialog xml 수정 #32
bamin0422 Dec 13, 2023
9dcf219
feat: NoticeActivity UI 구현 #32
bamin0422 Dec 13, 2023
04ba3e5
feat: MyPage 화면 및 편집화면 UI 구현 #32
bamin0422 Dec 16, 2023
294e276
fix: dialogFragment 매직넘버 상수화 #32
bamin0422 Dec 19, 2023
4150e1e
fix: dialogFragment resize 로직 개선 #32
bamin0422 Dec 19, 2023
d94108c
fix: 불필요한 언더바 제거 #32
bamin0422 Dec 19, 2023
f491b28
fix: 토스트 메시지 strings.xml로 분리 #32
bamin0422 Dec 19, 2023
997acc6
fix: = with로 수정 #32
bamin0422 Dec 19, 2023
dc2ff41
fix: string 변수들 strings.xml로 이동 #32
bamin0422 Dec 19, 2023
0c2c295
fix: 변수 재명명 #32
bamin0422 Dec 19, 2023
b42fd08
fix: 줄바꿈 수정 #32
bamin0422 Dec 19, 2023
c81ea3d
fix: TODO 리스트 정리 #32
bamin0422 Dec 19, 2023
4a03a2f
fix: ViewHolder Adapter 외부로 이동 #32
bamin0422 Dec 19, 2023
0344d53
Merge branch 'develop' into feat/#10-홈-화면-구현
bamin0422 Dec 19, 2023
d1082d7
fix: myPage 이미지 url mock 데이터 변경 #32
bamin0422 Dec 19, 2023
3bf0615
Merge remote-tracking branch 'origin/feat/#10-홈-화면-구현' into feat/#10-…
bamin0422 Dec 19, 2023
499634b
feat: MyPage 편집 화면 닉네임 생년월일 입력 화면 구현 #32
bamin0422 Dec 20, 2023
80ca345
feat: MyColor 선택 기능 구현 #32
bamin0422 Dec 23, 2023
eb30b93
feat: 채팅 기능 제거 #32
bamin0422 Dec 23, 2023
b064b14
feat: 마이페이지 편집 기능 구현 #10
bamin0422 Jan 1, 2024
f5b4fbf
Merge branch #26 into feat/#10-홈-화면-구현
bamin0422 Jan 1, 2024
f67a719
feat: BaseUseCase 및 OworiResponse 구현 #10
bamin0422 Jan 1, 2024
28bd569
feat: 설정 화면 및 가족 그룹명 변경 화면 구현 #10
bamin0422 Jan 1, 2024
7ac79fb
feat: 설정 화면 가족 그룹 명 추가 #10
bamin0422 Jan 1, 2024
8376e76
fix: 홈화면 수정 #10
bamin0422 Jan 3, 2024
c65f3f5
fix: 불필요 코드 삭제 #10
bamin0422 Jan 3, 2024
a41f69e
feat: AuthApi 로그인 api 추가 #10
bamin0422 Jan 3, 2024
83aff20
fix: 불필요 코드 삭제 #10
bamin0422 Jan 3, 2024
6d32027
fix: 코드 리뷰 반영 #10
bamin0422 Jan 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,12 @@ dependencies {

implementation(libs.kotlinx.coroutines.android)
implementation(libs.kotlinx.serialization.json)

implementation(libs.okhttp.logging)
implementation(libs.retrofit)
implementation(libs.retrofit.kotlin.serialization)
implementation(libs.retrofit.converter.gson)
implementation(libs.retrofit.converter.scalars)

implementation(libs.coroutines.core)
implementation(libs.fragment.ktx)
Expand All @@ -91,7 +94,7 @@ dependencies {
implementation(libs.play.service.auth)
implementation(libs.dagger.hilt)
kapt(libs.dagger.hilt.compiler)

implementation(libs.coil)
implementation(libs.kakao.login)
}

Expand Down
25 changes: 25 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,31 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".presenter.main.MainActivity"
android:exported="true"
android:screenOrientation="portrait" />

<activity android:name=".presenter.emotion.EmotionActivity"
android:exported="true"
android:screenOrientation="portrait" />

<activity android:name=".presenter.main.home.notice.NoticeActivity"
android:exported="true"
android:screenOrientation="portrait" />

<activity android:name=".presenter.main.home.mypage.MyPageActivity"
android:exported="true"
android:screenOrientation="portrait" />

<activity android:name=".presenter.main.home.settings.SettingsActivity"
android:exported="true"
android:screenOrientation="portrait" />

<activity android:name=".presenter.main.home.familyname.FamilyNameActivity"
android:exported="true"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustPan|adjustUnspecified"/>

<activity
android:name="com.kakao.sdk.auth.AuthCodeHandlerActivity"
android:exported="true">
Expand Down
5 changes: 0 additions & 5 deletions app/src/main/java/com/owori/android/auth/data/AuthProvider.kt

This file was deleted.

This file was deleted.

7 changes: 7 additions & 0 deletions app/src/main/java/com/owori/android/core/AppConfig.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.owori.android.core


object AppConfig {
const val TAG_DEBUG = "TAG_DEBUG"

}
61 changes: 61 additions & 0 deletions app/src/main/java/com/owori/android/core/BaseDialogFragment.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.owori.android.core

import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import androidx.fragment.app.DialogFragment
import com.owori.android.databinding.DialogBaseBinding

class BaseDialogFragment(
private val title: String,
private val contents: String,
private val positiveButtonText: String? = null,
private val cancelButtonText: String? = null,
private val onClickPositiveButton: () -> Unit = {}
) : DialogFragment() {
private var _binding: DialogBaseBinding? = null
private val binding get() = _binding!!

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = DialogBaseBinding.inflate(inflater, container, false)
val view = binding.root
dialog?.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))

with(binding) {
titleTextview.text = title
contentsTextview.text = contents

positiveButtonText?.let { positiveText.text = it }
cancelButtonText?.let { cancelText.text = it }

cancelButton.setOnClickListener {
dialog?.dismiss()
}

positiveButton.setOnClickListener {
onClickPositiveButton()
dialog?.dismiss()
}
}

return view
}

override fun onResume() {
super.onResume()
[email protected]?.window?.setLayout(WRAP_CONTENT, WRAP_CONTENT)
}

override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
36 changes: 33 additions & 3 deletions app/src/main/java/com/owori/android/core/OworiApplication.kt
Original file line number Diff line number Diff line change
@@ -1,14 +1,44 @@
package com.owori.android.core

import android.annotation.SuppressLint
import android.app.Application
import com.kakao.sdk.common.KakaoSdk
import android.content.Context
import androidx.annotation.StringRes
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import com.owori.android.R
import com.owori.android.module.NetworkConnectionChecker
import com.kakao.sdk.common.KakaoSdk
import dagger.hilt.android.HiltAndroidApp

@HiltAndroidApp
class OworiApplication: Application() {
class OworiApplication : Application(), DefaultLifecycleObserver {
override fun onCreate() {
super.onCreate()
super<Application>.onCreate()
context = applicationContext
networkConnectionChecker = NetworkConnectionChecker(context)
KakaoSdk.init(this, getString(R.string.kakao_login_key))
}

override fun onStop(owner: LifecycleOwner) {
networkConnectionChecker.unregister()
super.onStop(owner)
}

override fun onStart(owner: LifecycleOwner) {
networkConnectionChecker.register()
super.onStart(owner)
}

companion object {
@SuppressLint("StaticFieldLeak")
private lateinit var context: Context

fun getString(@StringRes stringResId: Int): String {
return context.getString(stringResId)
}

private lateinit var networkConnectionChecker: NetworkConnectionChecker
fun isOnline() = networkConnectionChecker.isOnline()
}
}
56 changes: 56 additions & 0 deletions app/src/main/java/com/owori/android/core/di/NetworkModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.owori.android.core.di

import com.owori.android.R
import com.owori.android.core.OworiApplication
import com.owori.android.data.api.auth.AuthApi
import com.owori.android.module.HttpRequestInterceptor
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.converter.scalars.ScalarsConverterFactory
import javax.inject.Singleton

/*
* Created by JJJoonngg
*/

@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
const val NETWORK_EXCEPTION_OFFLINE_CASE = "network status is offline"
const val NETWORK_EXCEPTION_BODY_IS_NULL = "result body is null"

@Provides
@Singleton
fun provideOKHttpClient(): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(HttpRequestInterceptor())
.retryOnConnectionFailure(false)
.build()
}

@Provides
@Singleton
fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
return Retrofit.Builder()
.client(okHttpClient)
.baseUrl(OworiApplication.getString(R.string.base_url))
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build()
}

@Provides
@Singleton
fun provideAuthApi(retrofit: Retrofit): AuthApi {
return retrofit.buildService()
}

private inline fun <reified T> Retrofit.buildService(): T {
return this.create(T::class.java)
}
}
22 changes: 22 additions & 0 deletions app/src/main/java/com/owori/android/data/api/auth/AuthApi.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.owori.android.data.api.auth

import com.owori.android.data.model.SignUpRequest
import com.owori.android.data.model.SignUpResponse
import com.owori.android.module.DataResult
import retrofit2.http.Body
import retrofit2.http.POST

/*
* Created by JJJoonngg
*/

interface AuthApi {
@POST("/members/kakao")
fun kakaoLogin(@Body data: SignUpRequest) : DataResult<SignUpResponse>

@POST("/members/google")
fun googleLogin(@Body data: SignUpRequest) : DataResult<SignUpResponse>

@POST("/members/apple")
fun appleLogin(@Body data: SignUpRequest) : DataResult<SignUpResponse>
}
13 changes: 13 additions & 0 deletions app/src/main/java/com/owori/android/data/model/SignUpRequest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.owori.android.data.model

import com.google.gson.annotations.SerializedName

data class SignUpRequest(
val token: String,
@SerializedName("auth_provider")
val authProvider: AuthProvider
) {
enum class AuthProvider {
GOOGLE, KAKAO, APPLE
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

별도의 enum class로 빼주는게 추후에 다른 곳에서 사용시 편할 수 있슴다.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

별도로 분리할께요!

}
19 changes: 19 additions & 0 deletions app/src/main/java/com/owori/android/data/model/SignUpResponse.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.owori.android.data.model

import com.google.gson.annotations.SerializedName

data class SignUpResponse(
@SerializedName("member_name")
val memberId: String,
@SerializedName("is_service_member")
val isMember: Boolean,
@SerializedName("jwt_token")
val token: Token,
) {
data class Token (
@SerializedName("access_token")
val accessToken: String,
@SerializedName("refresh_token")
val refreshToken: String,
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Token이라는 단어는 여기서만 사용되면 상관이 없지만 좀 더 범용적으로 사용되는 단어이니
별도의 data class로 빼고 SignUpResponseToken 과 같은 specific한 네이밍을 하면 좋슴다.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 좋네요! 수정하겠습니다!!

}
41 changes: 41 additions & 0 deletions app/src/main/java/com/owori/android/module/DataResult.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.owori.android.module

/*
* Created by JJJoonngg
*/

sealed class DataResult<out T> {
data class Success<T>(val data: T) : DataResult<T>()
data class Fail(val statusCode: Int, val message: String) : DataResult<Nothing>()
data class Error(val exception: Exception) : DataResult<Nothing>()
}

inline fun <T> DataResult<T>.onSuccess(action: (T) -> Unit): DataResult<T> {
if (this is DataResult.Success) {
action(data)
}
return this
}

inline fun <T> DataResult<T>.onFail(resultCode: (Int) -> Unit): DataResult<T> {
if (this is DataResult.Fail) {
resultCode(this.statusCode)
}
return this
}

inline fun <T> DataResult<T>.onError(action: (Exception) -> Unit): DataResult<T> {
if (this is DataResult.Fail) {
action(IllegalArgumentException("code : ${this.statusCode}, message : ${this.message}"))
} else if (this is DataResult.Error) {
action(this.exception)
}
return this
}

inline fun <T> DataResult<T>.onException(action: (Exception) -> Unit): DataResult<T> {
if (this is DataResult.Error) {
action(this.exception)
}
return this
}
41 changes: 41 additions & 0 deletions app/src/main/java/com/owori/android/module/HandleApi.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.owori.android.module

import com.owori.android.core.OworiApplication
import com.owori.android.core.di.NetworkModule
import retrofit2.Response

/*
* Created by JJJoonngg
*/

suspend fun <T : Any, R : Any> handleApi(
execute: suspend () -> Response<T>,
mapper: (T) -> R
): DataResult<R> {
if (OworiApplication.isOnline().not()) {
return DataResult.Error(Exception(NetworkModule.NETWORK_EXCEPTION_OFFLINE_CASE))
}

return try {
val response = execute()
val body = response.body()
if (response.isSuccessful) {
body?.let {
DataResult.Success(mapper(it))
} ?: run {
throw NullPointerException(NetworkModule.NETWORK_EXCEPTION_BODY_IS_NULL)
}
} else {
getFailDataResult(body, response)
}
} catch (e: Exception) {
DataResult.Error(e)
}
}


private fun <T : Any> getFailDataResult(body: T?, response: Response<T>) = body?.let {
DataResult.Fail(statusCode = response.code(), message = it.toString())
} ?: run {
DataResult.Fail(statusCode = response.code(), message = response.message())
}
Loading