diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index eebe3d7c..a7e57eed 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,6 +28,9 @@ jobs: path: ~/.sonar/cache key: ${{ runner.os }}-sonar restore-keys: ${{ runner.os }}-sonar + - run: echo $LOCAL_PROPERTIES_FILE | base64 -d > local.properties + env: + LOCAL_PROPERTIES_FILE: ${{ secrets.LOCAL_PROPERTIES_FILE }} - name: Build and analyze uses: gradle/gradle-build-action@v2 env: diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 177498e7..26221764 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -22,6 +22,9 @@ jobs: - run: echo $DEBUG_KEYSTORE_FILE | base64 -d > debug.keystore env: DEBUG_KEYSTORE_FILE: ${{ secrets.DEBUG_KEYSTORE_FILE }} + - run: echo $LOCAL_PROPERTIES_FILE | base64 -d > local.properties + env: + LOCAL_PROPERTIES_FILE: ${{ secrets.LOCAL_PROPERTIES_FILE }} - name: Build run: chmod +x ./gradlew && ./gradlew :app::assembleAlpha --quiet env: diff --git a/.gitignore b/.gitignore index 1a1d9d92..8198de2c 100644 --- a/.gitignore +++ b/.gitignore @@ -52,8 +52,8 @@ captures/ # Keystore files # Uncomment the following lines if you do not want to check your keystore files in. -#*.jks -#*.keystore +*.jks +*.keystore # External native build folder generated in Android Studio 2.2 and later .externalNativeBuild diff --git a/app/build.gradle b/app/build.gradle index 4ae5d8dd..2afdc567 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,6 +10,9 @@ plugins { id 'com.google.firebase.crashlytics' } +Properties properties = new Properties() +properties.load(project.rootProject.file('local.properties').newDataInputStream()) + android { compileSdk 33 @@ -24,6 +27,7 @@ android { } signingConfigs { + // GitHub Action 으로 빌드, 배포할 때 사용함 ciDebug { storeFile file("$project.rootDir/debug.keystore") storePassword System.getenv('DEBUG_STORE_PASSWORD') @@ -36,6 +40,19 @@ android { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + manifestPlaceholders = [ + KAKAO_APP_KEY: properties.getProperty('release.kakao.app-key') + ] + buildConfigField 'String', 'BASE_URL', "\"${properties.getProperty('release.three-days.base-url')}\"" + buildConfigField 'String', 'KAKAO_APP_KEY', "\"${properties.getProperty('release.kakao.app-key')}\"" + } + debug { + applicationIdSuffix '.debug' + manifestPlaceholders = [ + KAKAO_APP_KEY: properties.getProperty('debug.kakao.app-key') + ] + buildConfigField 'String', 'BASE_URL', "\"${properties.getProperty('debug.three-days.base-url')}\"" + buildConfigField 'String', 'KAKAO_APP_KEY', "\"${properties.getProperty('debug.kakao.app-key')}\"" } alpha { initWith debug @@ -57,6 +74,7 @@ android { dependencies { implementation(project(":domain")) implementation(project(":data")) + implementation(project(":build-property")) implementation(project(":core")) implementation(project(":navigator")) implementation(project(":presentation:home")) diff --git a/app/src/alpha/google-services.json b/app/src/alpha/google-services.json new file mode 100644 index 00000000..5de693f1 --- /dev/null +++ b/app/src/alpha/google-services.json @@ -0,0 +1,39 @@ +{ + "project_info": { + "project_number": "1091459842156", + "project_id": "three-days-develop", + "storage_bucket": "three-days-develop.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:1091459842156:android:8f8abdfe99c268fe485849", + "android_client_info": { + "package_name": "com.depromeet.threedays.debug" + } + }, + "oauth_client": [ + { + "client_id": "1091459842156-pjhtgjo35d6ro1ra922ngi7aabealvsc.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyB0EiDGdeC8Oxa9HvFshjg0YCZrfhzY4cs" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "1091459842156-pjhtgjo35d6ro1ra922ngi7aabealvsc.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/app/src/debug/google-services.json b/app/src/debug/google-services.json new file mode 100644 index 00000000..5de693f1 --- /dev/null +++ b/app/src/debug/google-services.json @@ -0,0 +1,39 @@ +{ + "project_info": { + "project_number": "1091459842156", + "project_id": "three-days-develop", + "storage_bucket": "three-days-develop.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:1091459842156:android:8f8abdfe99c268fe485849", + "android_client_info": { + "package_name": "com.depromeet.threedays.debug" + } + }, + "oauth_client": [ + { + "client_id": "1091459842156-pjhtgjo35d6ro1ra922ngi7aabealvsc.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyB0EiDGdeC8Oxa9HvFshjg0YCZrfhzY4cs" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "1091459842156-pjhtgjo35d6ro1ra922ngi7aabealvsc.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/app/google-services.json b/app/src/google-services.json similarity index 100% rename from app/google-services.json rename to app/src/google-services.json diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c4833b99..1ff31d66 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -95,8 +95,9 @@ - + diff --git a/app/src/main/java/com/depromeet/threedays/ThreeDaysApplication.kt b/app/src/main/java/com/depromeet/threedays/ThreeDaysApplication.kt index f360f1c5..9063b6f3 100644 --- a/app/src/main/java/com/depromeet/threedays/ThreeDaysApplication.kt +++ b/app/src/main/java/com/depromeet/threedays/ThreeDaysApplication.kt @@ -1,13 +1,19 @@ package com.depromeet.threedays import android.app.Application +import com.depromeet.threedays.buildproperty.BuildProperty +import com.depromeet.threedays.buildproperty.BuildPropertyRepository import com.depromeet.threedays.core.analytics.AnalyticsUtil import com.kakao.sdk.common.KakaoSdk import dagger.hilt.android.HiltAndroidApp import timber.log.Timber +import javax.inject.Inject @HiltAndroidApp class ThreeDaysApplication : Application() { + @Inject + lateinit var buildPropertyRepository: BuildPropertyRepository + override fun onCreate() { super.onCreate() @@ -21,7 +27,10 @@ class ThreeDaysApplication : Application() { } private fun initKakaoSdk() { - KakaoSdk.init(this, "f0c0458b5837b0f245c73b5a22908319") + KakaoSdk.init( + context = this, + appKey = buildPropertyRepository.get(BuildProperty.KAKAO_APP_KEY), + ) } private fun initAnalytics() { diff --git a/app/src/main/java/com/depromeet/threedays/firebase/FCMService.kt b/app/src/main/java/com/depromeet/threedays/firebase/FCMService.kt index 2c2b5f69..63790eda 100644 --- a/app/src/main/java/com/depromeet/threedays/firebase/FCMService.kt +++ b/app/src/main/java/com/depromeet/threedays/firebase/FCMService.kt @@ -13,8 +13,6 @@ import com.depromeet.threedays.domain.usecase.notification.token.UpdateNotificat import com.depromeet.threedays.home.MainActivity import com.google.firebase.messaging.FirebaseMessagingService import com.google.firebase.messaging.RemoteMessage -import kotlinx.coroutines.runBlocking -import timber.log.Timber import javax.inject.Inject import com.depromeet.threedays.core_design_system.R as CoreDesignSystemResources @@ -25,18 +23,6 @@ class FCMService @Inject constructor() : FirebaseMessagingService() { override fun onNewToken(token: String) { super.onNewToken(token) Log.i(TAG, "onNewToken: $token") - try { - // FIXME: - // - useCase initialize 안되었다는 에러 발생함. - // - 앱 처음 설치하면 스플래시 화면에서 호출되는데 이 때 memberId 없음 - runBlocking { - updateNotificationTokenUseCase(token) - } - } catch (e: Exception) { - Timber.w( - e, "Failed to update fcm registration token" - ) - } } override fun onMessageReceived(message: RemoteMessage) { diff --git a/app/src/main/java/com/depromeet/threedays/property/BuildPropertyModule.kt b/app/src/main/java/com/depromeet/threedays/property/BuildPropertyModule.kt new file mode 100644 index 00000000..008c8480 --- /dev/null +++ b/app/src/main/java/com/depromeet/threedays/property/BuildPropertyModule.kt @@ -0,0 +1,19 @@ +package com.depromeet.threedays.property + +import com.depromeet.threedays.buildproperty.BuildPropertyRepository +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +class BuildPropertyModule { + + @Singleton + @Provides + fun bindsBuildPropertyRepositoryProvide( + buildPropertyRepositoryImpl: BuildPropertyRepositoryImpl, + ): BuildPropertyRepository = buildPropertyRepositoryImpl +} diff --git a/app/src/main/java/com/depromeet/threedays/property/BuildPropertyRepositoryImpl.kt b/app/src/main/java/com/depromeet/threedays/property/BuildPropertyRepositoryImpl.kt new file mode 100644 index 00000000..677a8e6b --- /dev/null +++ b/app/src/main/java/com/depromeet/threedays/property/BuildPropertyRepositoryImpl.kt @@ -0,0 +1,33 @@ +package com.depromeet.threedays.property + +import com.depromeet.threedays.BuildConfig +import com.depromeet.threedays.buildproperty.BuildProperty +import com.depromeet.threedays.buildproperty.BuildPropertyRepository +import timber.log.Timber +import javax.inject.Inject + +class BuildPropertyRepositoryImpl @Inject constructor() : BuildPropertyRepository { + override fun get(buildProperty: BuildProperty): String { + try { + return readProperties(buildProperty = buildProperty) + } catch (e: Exception) { + throw IllegalStateException( + "Failed to read property from local.properties. key: $buildProperty", + e + ) + } + } + + override fun getOrNull(buildProperty: BuildProperty): String? { + return try { + readProperties(buildProperty = buildProperty) + } catch (e: Exception) { + Timber.e(e, "Failed to read property from local.properties. key: $buildProperty") + null + } + } + + private fun readProperties(buildProperty: BuildProperty): String { + return BuildConfig::class.java.getDeclaredField(buildProperty.key).get(null) as String + } +} diff --git a/build-property/.gitignore b/build-property/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/build-property/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/build-property/build.gradle b/build-property/build.gradle new file mode 100644 index 00000000..184c74a5 --- /dev/null +++ b/build-property/build.gradle @@ -0,0 +1,54 @@ +plugins { + id 'com.android.library' + id 'org.jetbrains.kotlin.android' + id 'kotlin-android' + id 'kotlin-kapt' + id 'kotlin-parcelize' + id 'dagger.hilt.android.plugin' +} + +android { + namespace 'com.depromeet.threedays.buildproperty' + compileSdk 33 + + defaultConfig { + minSdk 26 + targetSdk 33 + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles "consumer-rules.pro" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + alpha { + initWith debug + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = '1.8' + } + buildFeatures { + dataBinding true + } +} + +dependencies { + implementation(jetpackDeps) + implementation(coroutines) + + implementation deps.hilt.core + kapt deps.hilt.compiler + + implementation deps.timber + + testImplementation(testDeps) + androidTestImplementation(androidTestDeps) +} diff --git a/build-property/consumer-rules.pro b/build-property/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/build-property/proguard-rules.pro b/build-property/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/build-property/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# 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 diff --git a/build-property/src/androidTest/java/com/depromeet/threedays/buildproperty/ExampleInstrumentedTest.kt b/build-property/src/androidTest/java/com/depromeet/threedays/buildproperty/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..10fa7acc --- /dev/null +++ b/build-property/src/androidTest/java/com/depromeet/threedays/buildproperty/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.depromeet.threedays.buildproperty + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.depromeet.threedays.buildproperty.test", appContext.packageName) + } +} diff --git a/build-property/src/main/AndroidManifest.xml b/build-property/src/main/AndroidManifest.xml new file mode 100644 index 00000000..1a801d81 --- /dev/null +++ b/build-property/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + diff --git a/build-property/src/main/java/com/depromeet/threedays/buildproperty/BuildProperty.kt b/build-property/src/main/java/com/depromeet/threedays/buildproperty/BuildProperty.kt new file mode 100644 index 00000000..de9accea --- /dev/null +++ b/build-property/src/main/java/com/depromeet/threedays/buildproperty/BuildProperty.kt @@ -0,0 +1,21 @@ +package com.depromeet.threedays.buildproperty + +/** + * LocalProperties 에 저장된 값을 buildConfigField 를 통해 접근할 때 사용하는 key 모음. + */ +enum class BuildProperty( + private val description: String, +) { + BASE_URL("api 서버 주소"), + KAKAO_APP_KEY("Kakao native app key"), + ; + + /** + * build.gradle 의 buildConfigField 에 정의한 변수 이름 + */ + val key: String = this.name + + override fun toString(): String { + return "BuildProperty(description='$description', key='$key')" + } +} diff --git a/build-property/src/main/java/com/depromeet/threedays/buildproperty/BuildPropertyRepository.kt b/build-property/src/main/java/com/depromeet/threedays/buildproperty/BuildPropertyRepository.kt new file mode 100644 index 00000000..62f6eca6 --- /dev/null +++ b/build-property/src/main/java/com/depromeet/threedays/buildproperty/BuildPropertyRepository.kt @@ -0,0 +1,6 @@ +package com.depromeet.threedays.buildproperty + +interface BuildPropertyRepository { + fun get(buildProperty: BuildProperty): String + fun getOrNull(buildProperty: BuildProperty): String? +} diff --git a/build-property/src/test/java/com/depromeet/threedays/buildproperty/ExampleUnitTest.kt b/build-property/src/test/java/com/depromeet/threedays/buildproperty/ExampleUnitTest.kt new file mode 100644 index 00000000..92efdbd9 --- /dev/null +++ b/build-property/src/test/java/com/depromeet/threedays/buildproperty/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.depromeet.threedays.buildproperty + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} diff --git a/core/build.gradle b/core/build.gradle index 412c00d5..7c0decd6 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -42,6 +42,7 @@ android { dependencies { implementation(project(":core-design-system")) + implementation(project(":build-property")) implementation(jetpackDeps) implementation(coroutines) diff --git a/data/build.gradle b/data/build.gradle index 093a7793..8c532383 100644 --- a/data/build.gradle +++ b/data/build.gradle @@ -37,6 +37,7 @@ android { dependencies { implementation(project(":domain")) implementation(project(":navigator")) + implementation(project(":build-property")) implementation(jetpackDeps) implementation(coroutines) diff --git a/data/src/main/java/com/depromeet/threedays/data/api/interceptor/AuthInterceptor.kt b/data/src/main/java/com/depromeet/threedays/data/api/interceptor/AuthInterceptor.kt index 26d60f84..b1cd83e3 100644 --- a/data/src/main/java/com/depromeet/threedays/data/api/interceptor/AuthInterceptor.kt +++ b/data/src/main/java/com/depromeet/threedays/data/api/interceptor/AuthInterceptor.kt @@ -3,7 +3,8 @@ package com.depromeet.threedays.data.api.interceptor import android.content.Context import android.content.Intent import androidx.core.content.edit -import com.depromeet.threedays.data.di.BASE_URL +import com.depromeet.threedays.buildproperty.BuildProperty +import com.depromeet.threedays.buildproperty.BuildPropertyRepository import com.depromeet.threedays.data.entity.auth.RenewalTokenResponse import com.depromeet.threedays.domain.key.ACCESS_TOKEN_KEY import com.depromeet.threedays.domain.key.REFRESH_TOKEN_KEY @@ -18,7 +19,8 @@ class AuthInterceptor @Inject constructor( @ApplicationContext private val context: Context, private val client: OkHttpClient, private val gson: Gson, - private val signupNavigator: SignupNavigator + private val signupNavigator: SignupNavigator, + private val buildPropertyRepository: BuildPropertyRepository, ) : Interceptor { private val preference by lazy { context.getSharedPreferences(SHARED_PREFERENCE_KEY, Context.MODE_PRIVATE) @@ -57,7 +59,7 @@ class AuthInterceptor @Inject constructor( val requestBody: RequestBody = FormBody.Builder().build() val renewalTokensRequest = chain.request().newBuilder() .post(requestBody) - .url("${BASE_URL}/api/v1/members/tokens") + .url("${buildPropertyRepository.get(BuildProperty.BASE_URL)}/api/v1/members/tokens") .addHeader(X_THREE_DAYS_REFRESH_TOKEN, refreshToken) .addHeader("Content-Type", "application/json; charset=UTF-8") .build() diff --git a/data/src/main/java/com/depromeet/threedays/data/di/ServiceModule.kt b/data/src/main/java/com/depromeet/threedays/data/di/ServiceModule.kt index 7875a151..a71df8f8 100644 --- a/data/src/main/java/com/depromeet/threedays/data/di/ServiceModule.kt +++ b/data/src/main/java/com/depromeet/threedays/data/di/ServiceModule.kt @@ -1,6 +1,8 @@ package com.depromeet.threedays.data.di import android.content.Context +import com.depromeet.threedays.buildproperty.BuildProperty +import com.depromeet.threedays.buildproperty.BuildPropertyRepository import com.depromeet.threedays.data.api.* import com.depromeet.threedays.data.api.deserializer.LocalDateDeserializer import com.depromeet.threedays.data.api.deserializer.LocalDateTimeDeserializer @@ -86,14 +88,23 @@ class NetworkModule { fun providesHttpClient( @ApplicationContext context: Context, gson: Gson, - signupNavigator: SignupNavigator + signupNavigator: SignupNavigator, + buildPropertyRepository: BuildPropertyRepository, ): OkHttpClient { val client = OkHttpClient.Builder() .readTimeout(10, TimeUnit.SECONDS) .connectTimeout(10, TimeUnit.SECONDS) .writeTimeout(10, TimeUnit.SECONDS) val clientWithAuthInterceptor = client - .addInterceptor(AuthInterceptor(context, client.build(), gson, signupNavigator)) + .addInterceptor( + interceptor = AuthInterceptor( + context = context, + client = client.build(), + gson = gson, + signupNavigator = signupNavigator, + buildPropertyRepository = buildPropertyRepository, + ), + ) .addInterceptor(getLoggingInterceptor()) return clientWithAuthInterceptor.build() } @@ -107,7 +118,8 @@ class NetworkModule { @Singleton @Provides fun providesRetrofit( - okHttpClient: OkHttpClient + okHttpClient: OkHttpClient, + buildPropertyRepository: BuildPropertyRepository, ): Retrofit { val gsonWithAdapter: Gson = GsonBuilder() .registerTypeAdapter(LocalDateTime::class.java, LocalDateTimeDeserializer()) @@ -120,7 +132,7 @@ class NetworkModule { .create() return Retrofit.Builder() - .baseUrl(BASE_URL) + .baseUrl(buildPropertyRepository.get(BuildProperty.BASE_URL)) .client(okHttpClient) .addConverterFactory(GsonConverterFactory.create(gsonWithAdapter)) .build() @@ -131,5 +143,3 @@ class NetworkModule { level = HttpLoggingInterceptor.Level.BODY } } - -const val BASE_URL = "https://api.jjaksim.com" diff --git a/presentation/mypage/src/main/java/com/depromeet/threedays/mypage/MyPageFragment.kt b/presentation/mypage/src/main/java/com/depromeet/threedays/mypage/MyPageFragment.kt index ad3fa6c7..ac838bfa 100644 --- a/presentation/mypage/src/main/java/com/depromeet/threedays/mypage/MyPageFragment.kt +++ b/presentation/mypage/src/main/java/com/depromeet/threedays/mypage/MyPageFragment.kt @@ -44,7 +44,12 @@ class MyPageFragment : */ private fun initView(view: View) { // FIXME: getPackageInfo(String, PackageInfoFlags) 써보려했으나 에러나서 일단 동작하는 코드 사용함 - val versionName = view.context.packageManager.getPackageInfo("com.depromeet.threedays", 0,).versionName + val packageName = if (BuildConfig.DEBUG) { + "com.depromeet.threedays.debug" + } else { + "com.depromeet.threedays" + } + val versionName = view.context.packageManager.getPackageInfo(packageName, 0,).versionName binding.tvAppVersionName.text = versionName } diff --git a/settings.gradle b/settings.gradle index c5992cd9..d1a35ad4 100644 --- a/settings.gradle +++ b/settings.gradle @@ -31,3 +31,4 @@ include ':presentation:onboarding' include ':presentation:splash' include ':presentation:policy' include ':presentation:signup' +include ':build-property'