Firebase 추가
Crashlytics 추가 RemoteConfig 이용한 강제 업데이트 로직 추가
This commit is contained in:
		
							
								
								
									
										7
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -20,7 +20,8 @@ bin/ | |||||||
| gen/ | gen/ | ||||||
| out/ | out/ | ||||||
| #  Uncomment the following line in case you need and you don't have the release build type files in your app | #  Uncomment the following line in case you need and you don't have the release build type files in your app | ||||||
| release/ | /release/ | ||||||
|  | /debug/ | ||||||
|  |  | ||||||
| # Gradle files | # Gradle files | ||||||
| .gradle/ | .gradle/ | ||||||
| @@ -302,7 +303,7 @@ fabric.properties | |||||||
| ### AndroidStudio Patch ### | ### AndroidStudio Patch ### | ||||||
|  |  | ||||||
| !/gradle/wrapper/gradle-wrapper.jar | !/gradle/wrapper/gradle-wrapper.jar | ||||||
| app/debug/* | app/debug/ | ||||||
| app/release/* | app/release/ | ||||||
|  |  | ||||||
| # End of https://www.toptal.com/developers/gitignore/api/macos,android,androidstudio,visualstudiocode,git,kotlin,java | # End of https://www.toptal.com/developers/gitignore/api/macos,android,androidstudio,visualstudiocode,git,kotlin,java | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| plugins { | plugins { | ||||||
|     id 'com.android.application' |     id 'com.android.application' | ||||||
|     id 'org.jetbrains.kotlin.android' |     id 'org.jetbrains.kotlin.android' | ||||||
|  |     id 'com.google.gms.google-services' | ||||||
|     id 'com.google.android.gms.oss-licenses-plugin' |     id 'com.google.android.gms.oss-licenses-plugin' | ||||||
|  |  | ||||||
|     id 'kotlin-kapt' |     id 'kotlin-kapt' | ||||||
| @@ -8,6 +9,7 @@ plugins { | |||||||
|     id 'org.jlleitschuh.gradle.ktlint' |     id 'org.jlleitschuh.gradle.ktlint' | ||||||
|  |  | ||||||
|     id 'io.objectbox' |     id 'io.objectbox' | ||||||
|  |     id("com.google.firebase.crashlytics") | ||||||
| } | } | ||||||
|  |  | ||||||
| android { | android { | ||||||
| @@ -115,4 +117,12 @@ dependencies { | |||||||
|     implementation 'com.github.dhaval2404:imagepicker:2.1' |     implementation 'com.github.dhaval2404:imagepicker:2.1' | ||||||
|  |  | ||||||
|     implementation 'com.google.android.gms:play-services-oss-licenses:17.0.1' |     implementation 'com.google.android.gms:play-services-oss-licenses:17.0.1' | ||||||
|  |  | ||||||
|  |     // Firebase | ||||||
|  |     implementation platform('com.google.firebase:firebase-bom:32.2.0') | ||||||
|  |     implementation 'com.google.firebase:firebase-dynamic-links-ktx' | ||||||
|  |     implementation 'com.google.firebase:firebase-crashlytics-ktx' | ||||||
|  |     implementation 'com.google.firebase:firebase-analytics-ktx' | ||||||
|  |     implementation 'com.google.firebase:firebase-messaging-ktx' | ||||||
|  |     implementation 'com.google.firebase:firebase-config-ktx' | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										39
									
								
								app/src/debug/google-services.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								app/src/debug/google-services.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | |||||||
|  | { | ||||||
|  |   "project_info": { | ||||||
|  |     "project_number": "758414412471", | ||||||
|  |     "project_id": "sodalive-test", | ||||||
|  |     "storage_bucket": "sodalive-test.appspot.com" | ||||||
|  |   }, | ||||||
|  |   "client": [ | ||||||
|  |     { | ||||||
|  |       "client_info": { | ||||||
|  |         "mobilesdk_app_id": "1:758414412471:android:dcea9dff87fa125c7a5b32", | ||||||
|  |         "android_client_info": { | ||||||
|  |           "package_name": "kr.co.vividnext.sodalive.debug" | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|  |       "oauth_client": [ | ||||||
|  |         { | ||||||
|  |           "client_id": "758414412471-g35socquiplhaamhfl4e6bsta5blabi7.apps.googleusercontent.com", | ||||||
|  |           "client_type": 3 | ||||||
|  |         } | ||||||
|  |       ], | ||||||
|  |       "api_key": [ | ||||||
|  |         { | ||||||
|  |           "current_key": "AIzaSyAeNDVDY_r5afz97L1NPvQC6oFy5lPXHNI" | ||||||
|  |         } | ||||||
|  |       ], | ||||||
|  |       "services": { | ||||||
|  |         "appinvite_service": { | ||||||
|  |           "other_platform_oauth_client": [ | ||||||
|  |             { | ||||||
|  |               "client_id": "758414412471-g35socquiplhaamhfl4e6bsta5blabi7.apps.googleusercontent.com", | ||||||
|  |               "client_type": 3 | ||||||
|  |             } | ||||||
|  |           ] | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   ], | ||||||
|  |   "configuration_version": "1" | ||||||
|  | } | ||||||
| @@ -0,0 +1,74 @@ | |||||||
|  | package kr.co.vividnext.sodalive.base | ||||||
|  |  | ||||||
|  | import android.app.Activity | ||||||
|  | import android.graphics.Color | ||||||
|  | import android.graphics.drawable.ColorDrawable | ||||||
|  | import android.view.LayoutInflater | ||||||
|  | import android.view.View | ||||||
|  | import android.view.WindowManager | ||||||
|  | import androidx.appcompat.app.AlertDialog | ||||||
|  | import kr.co.vividnext.sodalive.databinding.DialogSodaBinding | ||||||
|  | import kr.co.vividnext.sodalive.extensions.dpToPx | ||||||
|  |  | ||||||
|  | open class SodaDialog( | ||||||
|  |     activity: Activity, | ||||||
|  |     layoutInflater: LayoutInflater, | ||||||
|  |     title: String, | ||||||
|  |     desc: String, | ||||||
|  |     confirmButtonTitle: String, | ||||||
|  |     confirmButtonClick: () -> Unit, | ||||||
|  |     cancelButtonTitle: String = "", | ||||||
|  |     cancelButtonClick: (() -> Unit)? = null, | ||||||
|  | ) { | ||||||
|  |  | ||||||
|  |     private val alertDialog: AlertDialog | ||||||
|  |  | ||||||
|  |     val dialogView = DialogSodaBinding.inflate(layoutInflater) | ||||||
|  |  | ||||||
|  |     init { | ||||||
|  |         val dialogBuilder = AlertDialog.Builder(activity) | ||||||
|  |         dialogBuilder.setView(dialogView.root) | ||||||
|  |  | ||||||
|  |         alertDialog = dialogBuilder.create() | ||||||
|  |         alertDialog.setCancelable(false) | ||||||
|  |         alertDialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) | ||||||
|  |  | ||||||
|  |         dialogView.tvTitle.text = title | ||||||
|  |         dialogView.tvDesc.text = desc | ||||||
|  |  | ||||||
|  |         dialogView.tvCancel.text = cancelButtonTitle | ||||||
|  |         dialogView.tvCancel.setOnClickListener { | ||||||
|  |             alertDialog.dismiss() | ||||||
|  |             cancelButtonClick?.let { it() } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         dialogView.tvConfirm.text = confirmButtonTitle | ||||||
|  |         dialogView.tvConfirm.setOnClickListener { | ||||||
|  |             alertDialog.dismiss() | ||||||
|  |             confirmButtonClick() | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         dialogView.tvCancel.visibility = if (cancelButtonTitle.isNotBlank()) { | ||||||
|  |             View.VISIBLE | ||||||
|  |         } else { | ||||||
|  |             View.GONE | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         dialogView.tvConfirm.visibility = if (confirmButtonTitle.isNotBlank()) { | ||||||
|  |             View.VISIBLE | ||||||
|  |         } else { | ||||||
|  |             View.GONE | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fun show(width: Int) { | ||||||
|  |         alertDialog.show() | ||||||
|  |  | ||||||
|  |         val lp = WindowManager.LayoutParams() | ||||||
|  |         lp.copyFrom(alertDialog.window?.attributes) | ||||||
|  |         lp.width = width - (26.7f.dpToPx()).toInt() | ||||||
|  |         lp.height = WindowManager.LayoutParams.WRAP_CONTENT | ||||||
|  |  | ||||||
|  |         alertDialog.window?.attributes = lp | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -12,4 +12,8 @@ object Constants { | |||||||
|  |  | ||||||
|     const val EXTRA_DATA = "extra_data" |     const val EXTRA_DATA = "extra_data" | ||||||
|     const val EXTRA_TERMS = "extra_terms" |     const val EXTRA_TERMS = "extra_terms" | ||||||
|  |     const val EXTRA_USER_ID = "extra_user_id" | ||||||
|  |     const val EXTRA_ROOM_ID = "extra_room_id" | ||||||
|  |  | ||||||
|  |     const val EXTRA_AUDIO_CONTENT_ID = "extra_audio_content_id" | ||||||
| } | } | ||||||
|   | |||||||
| @@ -2,10 +2,21 @@ package kr.co.vividnext.sodalive.splash | |||||||
|  |  | ||||||
| import android.annotation.SuppressLint | import android.annotation.SuppressLint | ||||||
| import android.content.Intent | import android.content.Intent | ||||||
|  | import android.net.Uri | ||||||
| import android.os.Bundle | import android.os.Bundle | ||||||
| import android.os.Handler | import android.os.Handler | ||||||
| import android.os.Looper | import android.os.Looper | ||||||
|  | import androidx.core.os.bundleOf | ||||||
|  | import com.google.firebase.dynamiclinks.PendingDynamicLinkData | ||||||
|  | import com.google.firebase.dynamiclinks.ktx.dynamicLinks | ||||||
|  | import com.google.firebase.ktx.Firebase | ||||||
|  | import com.google.firebase.remoteconfig.ktx.get | ||||||
|  | import com.google.firebase.remoteconfig.ktx.remoteConfig | ||||||
|  | import com.google.firebase.remoteconfig.ktx.remoteConfigSettings | ||||||
|  | import kr.co.vividnext.sodalive.BuildConfig | ||||||
| import kr.co.vividnext.sodalive.base.BaseActivity | import kr.co.vividnext.sodalive.base.BaseActivity | ||||||
|  | import kr.co.vividnext.sodalive.base.SodaDialog | ||||||
|  | import kr.co.vividnext.sodalive.common.Constants | ||||||
| import kr.co.vividnext.sodalive.common.SharedPreferenceManager | import kr.co.vividnext.sodalive.common.SharedPreferenceManager | ||||||
| import kr.co.vividnext.sodalive.databinding.ActivitySplashBinding | import kr.co.vividnext.sodalive.databinding.ActivitySplashBinding | ||||||
| import kr.co.vividnext.sodalive.main.MainActivity | import kr.co.vividnext.sodalive.main.MainActivity | ||||||
| @@ -15,21 +26,163 @@ import kr.co.vividnext.sodalive.user.login.LoginActivity | |||||||
| class SplashActivity : BaseActivity<ActivitySplashBinding>(ActivitySplashBinding::inflate) { | class SplashActivity : BaseActivity<ActivitySplashBinding>(ActivitySplashBinding::inflate) { | ||||||
|  |  | ||||||
|     private val handler = Handler(Looper.getMainLooper()) |     private val handler = Handler(Looper.getMainLooper()) | ||||||
|  |     private val remoteConfig = Firebase.remoteConfig | ||||||
|  |  | ||||||
|     override fun onCreate(savedInstanceState: Bundle?) { |     override fun onCreate(savedInstanceState: Bundle?) { | ||||||
|         super.onCreate(savedInstanceState) |         super.onCreate(savedInstanceState) | ||||||
|  |  | ||||||
|         if (SharedPreferenceManager.token.isBlank()) { |         setupRemoteConfig() | ||||||
|             showLoginActivity() |         fetchAndroidLatestVersion() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private fun setupRemoteConfig() { | ||||||
|  |         val configSettings = remoteConfigSettings { | ||||||
|  |             minimumFetchIntervalInSeconds = 300 | ||||||
|  |         } | ||||||
|  |         remoteConfig.setConfigSettingsAsync(configSettings) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private fun fetchAndroidLatestVersion() { | ||||||
|  |         remoteConfig.fetchAndActivate() | ||||||
|  |             .addOnCompleteListener(this) { task -> | ||||||
|  |                 if (task.isSuccessful) { | ||||||
|  |                     handler.post { | ||||||
|  |                         checkAppVersion(remoteConfig["android_latest_version"].asString()) | ||||||
|  |                     } | ||||||
|  |                 } else { | ||||||
|  |                     checkFirebaseDynamicLink() | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private fun checkAppVersion(latestVersion: String) { | ||||||
|  |         val versions = BuildConfig.VERSION_NAME.split(".") | ||||||
|  |         val latestVersions = latestVersion.split(".") | ||||||
|  |  | ||||||
|  |         if (latestVersions.isNotEmpty() && latestVersions.size == versions.size) { | ||||||
|  |             val latestMajor = latestVersions[0].toInt() | ||||||
|  |             val latestMinor = latestVersions[1].toInt() | ||||||
|  |             val latestPatch = latestVersions[2].toInt() | ||||||
|  |  | ||||||
|  |             val major = versions[0].toInt() | ||||||
|  |             val minor = versions[1].toInt() | ||||||
|  |             val patch = versions[2].toInt() | ||||||
|  |  | ||||||
|  |             if (latestMajor > major || (latestMajor == major && latestMinor > minor)) { | ||||||
|  |                 showUpdateDialog(isEssential = true) | ||||||
|  |             } else if (latestMajor == major && latestMinor == minor && latestPatch > patch) { | ||||||
|  |                 showUpdateDialog(isEssential = false) | ||||||
|  |             } else { | ||||||
|  |                 checkFirebaseDynamicLink() | ||||||
|  |             } | ||||||
|         } else { |         } else { | ||||||
|             showMainActivity() |             checkFirebaseDynamicLink() | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private fun showMainActivity() { |     private fun showUpdateDialog(isEssential: Boolean = false) { | ||||||
|  |         val desc = if (isEssential) { | ||||||
|  |             "필수 업데이트가 있습니다.\n업데이트 후 사용가능합니다." | ||||||
|  |         } else { | ||||||
|  |             "최신 업데이트가 있습니다.\n업데이트 하시겠습니까?" | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         val cancelButtonClick = if (!isEssential) { | ||||||
|  |             { checkFirebaseDynamicLink() } | ||||||
|  |         } else { | ||||||
|  |             null | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         SodaDialog( | ||||||
|  |             activity = this, | ||||||
|  |             layoutInflater = layoutInflater, | ||||||
|  |             title = "업데이트", | ||||||
|  |             desc = desc, | ||||||
|  |             confirmButtonTitle = "업데이트", | ||||||
|  |             confirmButtonClick = { | ||||||
|  |                 startActivity( | ||||||
|  |                     Intent( | ||||||
|  |                         Intent.ACTION_VIEW, | ||||||
|  |                         Uri.parse("market://details?id=${BuildConfig.APPLICATION_ID}") | ||||||
|  |                     ) | ||||||
|  |                 ) | ||||||
|  |                 finish() | ||||||
|  |             }, | ||||||
|  |             cancelButtonTitle = if (isEssential) { | ||||||
|  |                 "" | ||||||
|  |             } else { | ||||||
|  |                 "다음에" | ||||||
|  |             }, | ||||||
|  |             cancelButtonClick = cancelButtonClick | ||||||
|  |         ).show(screenWidth) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private fun checkFirebaseDynamicLink() { | ||||||
|  |         Firebase.dynamicLinks | ||||||
|  |             .getDynamicLink(intent) | ||||||
|  |             .addOnSuccessListener(this) { getDynamicLinkSuccess(it) } | ||||||
|  |             .addOnFailureListener(this) { getDynamicLinkFailure() } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private fun getDynamicLinkSuccess(pendingDynamicLinkData: PendingDynamicLinkData?) { | ||||||
|  |         var deepLink: Uri? = null | ||||||
|  |         if (pendingDynamicLinkData != null) { | ||||||
|  |             deepLink = pendingDynamicLinkData.link | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         val extras = if (deepLink != null) { | ||||||
|  |             val roomIdString = deepLink.getQueryParameter("room_id") | ||||||
|  |             val channelIdString = deepLink.getQueryParameter("channel_id") | ||||||
|  |             val audioContentIdString = deepLink.getQueryParameter("audio_content_id") | ||||||
|  |  | ||||||
|  |             if (roomIdString != null) { | ||||||
|  |                 bundleOf( | ||||||
|  |                     Constants.EXTRA_ROOM_ID to roomIdString.toLong() | ||||||
|  |                 ) | ||||||
|  |             } else if (channelIdString != null) { | ||||||
|  |                 bundleOf( | ||||||
|  |                     Constants.EXTRA_USER_ID to channelIdString.toLong() | ||||||
|  |                 ) | ||||||
|  |             } else if (audioContentIdString != null) { | ||||||
|  |                 bundleOf( | ||||||
|  |                     Constants.EXTRA_AUDIO_CONTENT_ID to audioContentIdString.toLong() | ||||||
|  |                 ) | ||||||
|  |             } else { | ||||||
|  |                 null | ||||||
|  |             } | ||||||
|  |         } else if (intent.extras != null) { | ||||||
|  |             intent.extras | ||||||
|  |         } else { | ||||||
|  |             null | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         startNextActivity(extras = extras) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private fun getDynamicLinkFailure() { | ||||||
|  |         val extras = intent.getBundleExtra(Constants.EXTRA_DATA) | ||||||
|  |             ?: if (intent.extras != null) { | ||||||
|  |                 intent.extras | ||||||
|  |             } else { | ||||||
|  |                 null | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |         startNextActivity(extras = extras) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private fun startNextActivity(extras: Bundle? = null) { | ||||||
|  |         if (SharedPreferenceManager.token.isBlank()) { | ||||||
|  |             showLoginActivity(extras) | ||||||
|  |         } else { | ||||||
|  |             showMainActivity(extras) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private fun showMainActivity(extras: Bundle?) { | ||||||
|         handler.postDelayed({ |         handler.postDelayed({ | ||||||
|             startActivity( |             startActivity( | ||||||
|                 Intent(applicationContext, MainActivity::class.java).apply { |                 Intent(applicationContext, MainActivity::class.java).apply { | ||||||
|  |                     putExtra(Constants.EXTRA_DATA, extras) | ||||||
|                     intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) |                     intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) | ||||||
|                     intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) |                     intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) | ||||||
|                 } |                 } | ||||||
| @@ -38,10 +191,11 @@ class SplashActivity : BaseActivity<ActivitySplashBinding>(ActivitySplashBinding | |||||||
|         }, 500) |         }, 500) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private fun showLoginActivity() { |     private fun showLoginActivity(extras: Bundle?) { | ||||||
|         handler.postDelayed({ |         handler.postDelayed({ | ||||||
|             startActivity( |             startActivity( | ||||||
|                 Intent(applicationContext, LoginActivity::class.java).apply { |                 Intent(applicationContext, LoginActivity::class.java).apply { | ||||||
|  |                     putExtra(Constants.EXTRA_DATA, extras) | ||||||
|                     intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) |                     intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) | ||||||
|                     intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) |                     intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) | ||||||
|                 } |                 } | ||||||
|   | |||||||
| @@ -0,0 +1,8 @@ | |||||||
|  | <?xml version="1.0" encoding="utf-8"?> | ||||||
|  | <shape xmlns:android="http://schemas.android.com/apk/res/android"> | ||||||
|  |     <solid android:color="@color/color_339970ff" /> | ||||||
|  |     <corners android:radius="10dp" /> | ||||||
|  |     <stroke | ||||||
|  |         android:width="1dp" | ||||||
|  |         android:color="@color/color_9970ff" /> | ||||||
|  | </shape> | ||||||
							
								
								
									
										79
									
								
								app/src/main/res/layout/dialog_soda.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								app/src/main/res/layout/dialog_soda.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,79 @@ | |||||||
|  | <?xml version="1.0" encoding="utf-8"?> | ||||||
|  | <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||||
|  |     xmlns:app="http://schemas.android.com/apk/res-auto" | ||||||
|  |     xmlns:tools="http://schemas.android.com/tools" | ||||||
|  |     android:layout_width="match_parent" | ||||||
|  |     android:layout_height="wrap_content" | ||||||
|  |     android:layout_gravity="center" | ||||||
|  |     android:background="@drawable/bg_round_corner_10_222222"> | ||||||
|  |  | ||||||
|  |     <TextView | ||||||
|  |         android:id="@+id/tv_title" | ||||||
|  |         android:layout_width="wrap_content" | ||||||
|  |         android:layout_height="wrap_content" | ||||||
|  |         android:layout_marginTop="40dp" | ||||||
|  |         android:fontFamily="@font/gmarket_sans_bold" | ||||||
|  |         android:gravity="center" | ||||||
|  |         android:textColor="@color/color_bbbbbb" | ||||||
|  |         android:textSize="18.3sp" | ||||||
|  |         app:layout_constraintEnd_toEndOf="parent" | ||||||
|  |         app:layout_constraintStart_toStartOf="parent" | ||||||
|  |         app:layout_constraintTop_toTopOf="parent" | ||||||
|  |         tools:text="작성글 등록" /> | ||||||
|  |  | ||||||
|  |     <TextView | ||||||
|  |         android:id="@+id/tv_desc" | ||||||
|  |         android:layout_width="wrap_content" | ||||||
|  |         android:layout_height="wrap_content" | ||||||
|  |         android:layout_marginTop="6dp" | ||||||
|  |         android:fontFamily="@font/gmarket_sans_medium" | ||||||
|  |         android:gravity="center" | ||||||
|  |         android:textColor="@color/color_bbbbbb" | ||||||
|  |         android:textSize="15sp" | ||||||
|  |         app:layout_constraintEnd_toEndOf="parent" | ||||||
|  |         app:layout_constraintStart_toStartOf="parent" | ||||||
|  |         app:layout_constraintTop_toBottomOf="@+id/tv_title" | ||||||
|  |         tools:text="작성한 글을 등록하시겠습니까?" /> | ||||||
|  |  | ||||||
|  |     <LinearLayout | ||||||
|  |         android:layout_width="0dp" | ||||||
|  |         android:layout_height="wrap_content" | ||||||
|  |         android:layout_marginHorizontal="16.7dp" | ||||||
|  |         android:layout_marginTop="45dp" | ||||||
|  |         android:layout_marginBottom="16.7dp" | ||||||
|  |         android:gravity="center" | ||||||
|  |         app:layout_constraintBottom_toBottomOf="parent" | ||||||
|  |         app:layout_constraintEnd_toEndOf="parent" | ||||||
|  |         app:layout_constraintStart_toStartOf="parent" | ||||||
|  |         app:layout_constraintTop_toBottomOf="@+id/tv_desc"> | ||||||
|  |  | ||||||
|  |         <TextView | ||||||
|  |             android:id="@+id/tv_cancel" | ||||||
|  |             android:layout_width="0dp" | ||||||
|  |             android:layout_height="wrap_content" | ||||||
|  |             android:layout_marginEnd="13.3dp" | ||||||
|  |             android:layout_weight="1" | ||||||
|  |             android:background="@drawable/bg_round_corner_10_339970ff_9970ff" | ||||||
|  |             android:fontFamily="@font/gmarket_sans_bold" | ||||||
|  |             android:gravity="center" | ||||||
|  |             android:paddingVertical="16dp" | ||||||
|  |             android:textColor="@color/color_9970ff" | ||||||
|  |             android:textSize="18.3sp" | ||||||
|  |             tools:text="취소" /> | ||||||
|  |  | ||||||
|  |         <TextView | ||||||
|  |             android:id="@+id/tv_confirm" | ||||||
|  |             android:layout_width="0dp" | ||||||
|  |             android:layout_height="wrap_content" | ||||||
|  |             android:layout_weight="1" | ||||||
|  |             android:background="@drawable/bg_round_corner_10_9970ff" | ||||||
|  |             android:fontFamily="@font/gmarket_sans_bold" | ||||||
|  |             android:gravity="center" | ||||||
|  |             android:paddingVertical="16dp" | ||||||
|  |             android:textColor="@color/white" | ||||||
|  |             android:textSize="18.3sp" | ||||||
|  |             tools:text="확인" /> | ||||||
|  |  | ||||||
|  |     </LinearLayout> | ||||||
|  |  | ||||||
|  | </androidx.constraintlayout.widget.ConstraintLayout> | ||||||
| @@ -16,4 +16,5 @@ | |||||||
|  |  | ||||||
|     <color name="color_b3909090">#B3909090</color> |     <color name="color_b3909090">#B3909090</color> | ||||||
|     <color name="color_88909090">#88909090</color> |     <color name="color_88909090">#88909090</color> | ||||||
|  |     <color name="color_339970ff">#339970FF</color> | ||||||
| </resources> | </resources> | ||||||
|   | |||||||
| @@ -22,6 +22,8 @@ plugins { | |||||||
|     id 'org.jetbrains.kotlin.android' version '1.8.20' apply false |     id 'org.jetbrains.kotlin.android' version '1.8.20' apply false | ||||||
|  |  | ||||||
|     id "org.jlleitschuh.gradle.ktlint" version "11.2.0" |     id "org.jlleitschuh.gradle.ktlint" version "11.2.0" | ||||||
|  |     id 'com.google.gms.google-services' version '4.3.15' apply false | ||||||
|  |     id("com.google.firebase.crashlytics") version "2.9.7" apply false | ||||||
| } | } | ||||||
|  |  | ||||||
| task clean(type: Delete) { | task clean(type: Delete) { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user