Skip to content

Commit 64d2168

Browse files
committed
Merge branch 'develop' into release
2 parents d1effc2 + fa40640 commit 64d2168

82 files changed

Lines changed: 3602 additions & 614 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

core/ui/src/main/java/com/yapp/ndgl/core/ui/designsystem/NDGLInputModal.kt

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -57,24 +57,14 @@ fun NDGLInputModal(
5757
textAlign: TextAlign = TextAlign.Start,
5858
) {
5959
val focusRequester = remember { FocusRequester() }
60-
var textFieldValue by remember(value) {
60+
var textFieldValue by remember {
6161
mutableStateOf(TextFieldValue(value, selection = TextRange(value.length)))
6262
}
6363

6464
LaunchedEffect(Unit) {
6565
focusRequester.requestFocus()
6666
}
6767

68-
// value가 변경되면 textFieldValue 업데이트
69-
LaunchedEffect(value) {
70-
if (textFieldValue.text != value) {
71-
textFieldValue = TextFieldValue(
72-
text = value,
73-
selection = TextRange(value.length),
74-
)
75-
}
76-
}
77-
7868
Dialog(onDismissRequest = onDismissRequest) {
7969
Surface(
8070
modifier = modifier.wrapContentHeight(),
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package com.yapp.ndgl.core.ui.designsystem
2+
3+
import androidx.compose.foundation.background
4+
import androidx.compose.foundation.layout.Row
5+
import androidx.compose.foundation.layout.fillMaxWidth
6+
import androidx.compose.foundation.layout.padding
7+
import androidx.compose.foundation.shape.RoundedCornerShape
8+
import androidx.compose.material3.SnackbarData
9+
import androidx.compose.material3.SnackbarDuration
10+
import androidx.compose.material3.SnackbarVisuals
11+
import androidx.compose.material3.Text
12+
import androidx.compose.runtime.Composable
13+
import androidx.compose.ui.Alignment
14+
import androidx.compose.ui.Modifier
15+
import androidx.compose.ui.draw.clip
16+
import androidx.compose.ui.graphics.Color
17+
import androidx.compose.ui.text.style.TextOverflow
18+
import androidx.compose.ui.tooling.preview.Preview
19+
import androidx.compose.ui.unit.dp
20+
import com.yapp.ndgl.core.ui.theme.NDGLTheme
21+
import com.yapp.ndgl.core.ui.util.dropShadow
22+
23+
@Composable
24+
fun NDGLSnackbar(
25+
modifier: Modifier = Modifier,
26+
snackbarData: SnackbarData,
27+
) {
28+
val message = snackbarData.visuals.message
29+
30+
Row(
31+
verticalAlignment = Alignment.CenterVertically,
32+
modifier = modifier
33+
.fillMaxWidth()
34+
.padding(horizontal = 24.dp)
35+
.dropShadow(shape = RoundedCornerShape(8.dp), color = Color.Black.copy(alpha = 0.12f), offsetY = 3.dp, blur = 8.dp)
36+
.clip(RoundedCornerShape(8.dp))
37+
.background(NDGLTheme.colors.black500)
38+
.padding(vertical = 14.dp, horizontal = 20.dp),
39+
) {
40+
Text(
41+
text = message,
42+
maxLines = 2,
43+
overflow = TextOverflow.Ellipsis,
44+
style = NDGLTheme.typography.bodyMdSemiBold,
45+
color = NDGLTheme.colors.white,
46+
)
47+
}
48+
}
49+
50+
@Preview(showBackground = true)
51+
@Composable
52+
private fun NDGLSnackbarPreview() {
53+
NDGLTheme {
54+
NDGLSnackbar(
55+
snackbarData = object : SnackbarData {
56+
override val visuals: SnackbarVisuals = object : SnackbarVisuals {
57+
override val actionLabel = null
58+
override val duration = SnackbarDuration.Short
59+
override val message = "교통수단이 변경되었습니다"
60+
override val withDismissAction = false
61+
}
62+
63+
override fun dismiss() { /* No-op */ }
64+
override fun performAction() { /* No-op */ }
65+
},
66+
)
67+
}
68+
}

core/ui/src/main/res/drawable/ic_140_no_schedule_calendar.xml renamed to core/ui/src/main/res/drawable/img_no_schedule_calendar.xml

File renamed without changes.
File renamed without changes.

core/ui/src/main/res/values/strings.xml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,12 @@
6262
<string name="start_time_setting">여행 시작 시간 설정</string>
6363
<string name="edit_travel">편집하기</string>
6464
<string name="edit_done">편집 완료</string>
65-
<string name="add_schedule">일정 추가하기</string>
65+
<string name="add_schedule_button_text">일정 추가하기</string>
6666
<string name="select_all">전체 선택</string>
6767
<string name="delete_selected">선택 삭제</string>
68-
<string name="no_schedule_message">아직 %d일차 일정이 없어요</string>
68+
<string name="no_schedule_message">일정이 없어요</string>
69+
<string name="no_schedule_message_detail">아직 %d일차 일정이 없어요</string>
70+
<string name="add_schedule_button_text_no_schedule">%d일차 첫 일정 추가하기</string>
6971

7072
<!-- Dialog - Cancel Edit -->
7173
<string name="cancel_edit_dialog_title">편집 중단</string>

data/core/src/main/java/com/yapp/ndgl/data/core/di/NetworkModule.kt

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFact
44
import com.yapp.ndgl.data.core.BuildConfig
55
import com.yapp.ndgl.data.core.adapter.NDGLCallAdapterFactory
66
import com.yapp.ndgl.data.core.authenticator.NDGLAuthenticator
7+
import com.yapp.ndgl.data.core.interceptor.AndroidCredentialInterceptor
78
import com.yapp.ndgl.data.core.interceptor.ApiKeyInterceptor
9+
import com.yapp.ndgl.data.core.interceptor.ApiKeyQueryInterceptor
810
import com.yapp.ndgl.data.core.interceptor.NDGLInterceptor
911
import com.yapp.ndgl.data.core.interceptor.RouteInterceptor
1012
import dagger.Module
@@ -121,6 +123,28 @@ object NetworkModule {
121123
.addInterceptor(httpLoggingInterceptor)
122124
.build()
123125
}
126+
127+
@WeatherClient
128+
@Singleton
129+
@Provides
130+
fun provideWeatherOkHttpClient(
131+
@WeatherApiKey apiKey: String,
132+
androidCredentialInterceptor: AndroidCredentialInterceptor,
133+
httpLoggingInterceptor: HttpLoggingInterceptor,
134+
): OkHttpClient = OkHttpClient.Builder()
135+
.addInterceptor(ApiKeyQueryInterceptor(apiKey))
136+
.addInterceptor(androidCredentialInterceptor)
137+
.addInterceptor(httpLoggingInterceptor)
138+
.build()
139+
140+
@ExchangeRateClient
141+
@Singleton
142+
@Provides
143+
fun provideExchangeRateOkHttpClient(
144+
httpLoggingInterceptor: HttpLoggingInterceptor,
145+
): OkHttpClient = OkHttpClient.Builder()
146+
.addInterceptor(httpLoggingInterceptor)
147+
.build()
124148
}
125149

126150
@Qualifier
@@ -146,3 +170,23 @@ annotation class RouteBaseUrl
146170
@Qualifier
147171
@Retention(AnnotationRetention.BINARY)
148172
annotation class RouteClient
173+
174+
@Qualifier
175+
@Retention(AnnotationRetention.BINARY)
176+
annotation class WeatherApiKey
177+
178+
@Qualifier
179+
@Retention(AnnotationRetention.BINARY)
180+
annotation class WeatherClient
181+
182+
@Qualifier
183+
@Retention(AnnotationRetention.BINARY)
184+
annotation class GeocodingClient
185+
186+
@Qualifier
187+
@Retention(AnnotationRetention.BINARY)
188+
annotation class ExchangeRateApiKey
189+
190+
@Qualifier
191+
@Retention(AnnotationRetention.BINARY)
192+
annotation class ExchangeRateClient
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.yapp.ndgl.data.core.interceptor
2+
3+
import android.content.Context
4+
import android.content.pm.PackageManager
5+
import dagger.hilt.android.qualifiers.ApplicationContext
6+
import okhttp3.Interceptor
7+
import okhttp3.Response
8+
import java.security.MessageDigest
9+
import javax.inject.Inject
10+
11+
@Suppress("NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS")
12+
class AndroidCredentialInterceptor @Inject constructor(
13+
@ApplicationContext private val context: Context,
14+
) : Interceptor {
15+
private val packageName: String = context.packageName
16+
private val sha1Cert: String = computeSha1(context)
17+
18+
override fun intercept(chain: Interceptor.Chain): Response {
19+
val newRequest = chain.request().newBuilder()
20+
.addHeader("X-Android-Package", packageName)
21+
.addHeader("X-Android-Cert", sha1Cert)
22+
.build()
23+
return chain.proceed(newRequest)
24+
}
25+
26+
companion object {
27+
private fun computeSha1(context: Context): String = try {
28+
val signatures = context.packageManager
29+
.getPackageInfo(context.packageName, PackageManager.GET_SIGNING_CERTIFICATES)
30+
.signingInfo?.apkContentsSigners
31+
MessageDigest.getInstance("SHA-1")
32+
.digest(signatures?.getOrNull(0)?.toByteArray())
33+
.joinToString("") { "%02x".format(it) }
34+
} catch (_: Exception) {
35+
""
36+
}
37+
}
38+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.yapp.ndgl.data.core.interceptor
2+
3+
import okhttp3.Interceptor
4+
import okhttp3.Response
5+
6+
class ApiKeyQueryInterceptor(private val apiKey: String) : Interceptor {
7+
override fun intercept(chain: Interceptor.Chain): Response {
8+
val newUrl = chain.request().url.newBuilder()
9+
.addQueryParameter("key", apiKey)
10+
.build()
11+
return chain.proceed(chain.request().newBuilder().url(newUrl).build())
12+
}
13+
}

data/travel/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ android {
1313
}
1414
buildConfigField("String", "PLACE_API_KEY", "\"${localProperties.getProperty("PLACE_API_KEY", "")}\"")
1515
buildConfigField("String", "ROUTE_API_KEY", "\"${localProperties.getProperty("ROUTE_API_KEY", "")}\"")
16+
buildConfigField("String", "WEATHER_API_KEY", "\"${localProperties.getProperty("WEATHER_API_KEY", "")}\"")
17+
buildConfigField("String", "EXCHANGE_RATE_API_KEY", "\"${localProperties.getProperty("EXCHANGE_RATE_API_KEY", "")}\"")
1618
}
1719

1820
buildFeatures {
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.yapp.ndgl.data.travel.api
2+
3+
import com.yapp.ndgl.data.travel.model.ExchangeRateResponse
4+
import retrofit2.http.GET
5+
import retrofit2.http.Path
6+
7+
interface ExchangeRateApi {
8+
@GET("v6/{apiKey}/latest/{base}")
9+
suspend fun getLatestRate(
10+
@Path("apiKey") apiKey: String,
11+
@Path("base") base: String,
12+
): ExchangeRateResponse
13+
}

0 commit comments

Comments
 (0)