Skip to content

Commit

Permalink
[FEAT] #228 파이어베이스 동적 링크로 코스 공유 구현 완료
Browse files Browse the repository at this point in the history
- iOS 호환 테스트 완료
  • Loading branch information
unam98 committed Feb 15, 2024
1 parent b2a7cd2 commit d1f3853
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 90 deletions.
1 change: 1 addition & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ dependencies {
implementation 'androidx.core:core:1.9.0'
implementation 'com.google.firebase:firebase-common-ktx:20.1.0'
implementation 'androidx.preference:preference-ktx:1.2.1'
implementation 'com.google.firebase:firebase-dynamic-links-ktx:21.2.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
Expand Down
5 changes: 2 additions & 3 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,9 @@
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<!-- "kakao${YOUR_NATIVE_APP_KEY}://kakaolink" 형식의 앱 실행 스킴을 설정하는데 사용 -->
<data
android:host="kakaolink"
android:scheme="@string/kakao_redirection_scheme" />
android:host="rnnt.page.link"
android:scheme="https" />
</intent-filter>
</activity>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.Intent
import android.graphics.Rect
import android.net.Uri
import android.os.Bundle
import android.view.Gravity
import android.view.MotionEvent
Expand All @@ -14,6 +15,8 @@ import androidx.activity.OnBackPressedCallback
import androidx.activity.viewModels
import androidx.core.view.isVisible
import coil.load
import com.google.firebase.dynamiclinks.DynamicLink
import com.google.firebase.dynamiclinks.FirebaseDynamicLinks
import com.gun0912.tedpermission.provider.TedPermissionProvider.context
import com.kakao.sdk.common.util.KakaoCustomTabsClient
import com.kakao.sdk.link.LinkClient
Expand Down Expand Up @@ -90,33 +93,44 @@ class CourseDetailActivity :

Analytics.logClickedItemEvent(VIEW_COURSE_DETAIL)

initIntentExtraData()
updatePublicCourseIdFromDeepLink()
getCourseDetail()

addListener()
addObserver()
registerBackPressedCallback()
updatePublicCourseIdFromDynamicLink { dynamicLinkHandled ->
if (!dynamicLinkHandled) {
initIntentExtraData()
}
addListener()
addObserver()
registerBackPressedCallback()
getCourseDetail()
}
}

private fun initIntentExtraData() {
intent.getCompatibleSerializableExtra<CourseDetailRootScreen>(EXTRA_ROOT_SCREEN)?.let {
rootScreen = it
}
publicCourseId = intent.getIntExtra(EXTRA_PUBLIC_COURSE_ID, 0)
}

private fun updatePublicCourseIdFromDeepLink() {
// 딥링크를 통해 열린 경우
if (Intent.ACTION_VIEW == intent.action) {
isFromDeepLink = true
val uri = intent.data
if (uri != null) {
// 여기서 androidExecutionParams 값들을 받아와 어떠한 상세 페이지를 띄울지 결정할 수 있음.
publicCourseId = uri.getQueryParameter("publicCourseId")!!.toInt()
Timber.tag("deeplink-publicCourseId").d("$publicCourseId")
publicCourseId = intent.getIntExtra(EXTRA_PUBLIC_COURSE_ID, -1)
Timber.tag("intent-publicCourseId").d("$publicCourseId")
}

private fun updatePublicCourseIdFromDynamicLink(completion: (Boolean) -> Unit) {
FirebaseDynamicLinks.getInstance().getDynamicLink(intent)
.addOnSuccessListener(this) { pendingDynamicLinkData ->
val deepLink: Uri? = pendingDynamicLinkData?.link
if (deepLink != null) {
isFromDeepLink = true
publicCourseId = deepLink.getQueryParameter("courseId")?.toInt() ?: -1
if (publicCourseId != -1) {
Timber.tag("deeplink-publicCourseId").d("$publicCourseId")
completion(true)
return@addOnSuccessListener
}
}
completion(false)
}
.addOnFailureListener(this) { e ->
Timber.e("getDynamicLink:onFailure", e)
completion(false)
}
}
}

private fun getCourseDetail() {
Expand Down Expand Up @@ -163,7 +177,7 @@ class CourseDetailActivity :
intent?.let { newIntent ->
newIntent.getCompatibleSerializableExtra<CourseDetailRootScreen>(EXTRA_ROOT_SCREEN)
?.let { rootScreen = it }
publicCourseId = newIntent.getIntExtra(EXTRA_PUBLIC_COURSE_ID, 0)
publicCourseId = newIntent.getIntExtra(EXTRA_PUBLIC_COURSE_ID, -1)
getCourseDetail()
}
}
Expand Down Expand Up @@ -238,9 +252,42 @@ class CourseDetailActivity :
}
}

private fun sendFirebaseDynamicLink(title: String, desc: String, image: String) {
val link = "https://rnnt.page.link/?courseId=$publicCourseId"

FirebaseDynamicLinks.getInstance().createDynamicLink()
.setLink(Uri.parse(link))
.setDomainUriPrefix("https://rnnt.page.link")
// Set parameters
.setAndroidParameters(DynamicLink.AndroidParameters.Builder().build())
.setIosParameters(DynamicLink.IosParameters.Builder("com.runnect.Runnect-iOS").build())
.setSocialMetaTagParameters(
DynamicLink.SocialMetaTagParameters.Builder()
.setTitle(title)
.setDescription(desc)
.setImageUrl(Uri.parse(image))
.build())
.buildShortDynamicLink()
.addOnSuccessListener { result ->
val shortLink = result.shortLink
shareLink(shortLink.toString())
}
.addOnFailureListener {
it.printStackTrace()
}
}

private fun shareLink(url: String) {
val intent = Intent(Intent.ACTION_SEND).apply {
type = "text/plain"
putExtra(Intent.EXTRA_TEXT, url)
}
startActivity(Intent.createChooser(intent, "Share Link"))
}

private fun initShareButtonClickListener() {
binding.btnShare.setOnClickListener {
sendKakaoLink(
sendFirebaseDynamicLink(
title = courseDetail.title,
desc = courseDetail.description,
image = courseDetail.image
Expand All @@ -255,71 +302,6 @@ class CourseDetailActivity :
}
}

// todo: 함수를 더 작게 분리하는 게 좋을 거 같아요! @우남
private fun sendKakaoLink(title: String, desc: String, image: String) {
// 메시지 템플릿 만들기 (피드형)
val defaultFeed = FeedTemplate(
content = Content(
title = title,
description = desc,
imageUrl = image,
link = Link(
mobileWebUrl = "https://play.google.com/store/apps/details?id=com.runnect.runnect"
)
),
buttons = listOf(
Button(
"자세히 보기",
Link(
//이 부분을 사용해서 어떤 상세페이지를 띄울지 결정할수 있다
androidExecutionParams = mapOf(
"publicCourseId" to publicCourseId.toString(),
)
),
)
)
)

// 피드 메시지 보내기
if (context?.let { LinkClient.instance.isKakaoLinkAvailable(it) } == true) {
// 카카오톡으로 카카오링크 공유 가능
context?.let {
LinkClient.instance.defaultTemplate(it, defaultFeed) { linkResult, error ->
if (error != null) {
Timber.tag("kakao_link").d("카카오링크 보내기 실패: $error")
} else if (linkResult != null) {
Timber.tag("kakao_link").d("카카오링크 보내기 성공: ${linkResult.intent}")

startActivity(linkResult.intent) //카카오톡이 깔려있을 경우 카카오톡으로 넘기기

// 카카오링크 보내기에 성공했지만 아래 경고 메시지가 존재할 경우 일부 컨텐츠가 정상 동작하지 않음
Timber.tag("kakao_link").d("Warning Msg: ${linkResult.warningMsg}")
Timber.tag("kakao_link").d("Argument Msg: ${linkResult.argumentMsg}")
}
}
}
} else { // 카카오톡 미설치: 웹 공유 사용 권장
// 웹 공유 예시 코드
val sharerUrl = WebSharerClient.instance.defaultTemplateUri(defaultFeed)

// 1. CustomTabs으로 Chrome 브라우저 열기
try {
context?.let { KakaoCustomTabsClient.openWithDefault(it, sharerUrl) }
} catch (e: UnsupportedOperationException) {
// Chrome 브라우저가 없을 때
Toast.makeText(context, "chrome 또는 인터넷 브라우저를 설치해주세요", Toast.LENGTH_SHORT).show()
}

// 2. CustomTabs으로 디바이스 기본 브라우저 열기
try {
context?.let { KakaoCustomTabsClient.open(it, sharerUrl) }
} catch (e: ActivityNotFoundException) {
// 인터넷 브라우저가 없을 때
Toast.makeText(context, "chrome 또는 인터넷 브라우저를 설치해주세요", Toast.LENGTH_SHORT).show()
}
}
}

private fun initStartRunButtonClickListener() {
binding.btnCourseDetailStartRun.setOnClickListener {
if (isVisitorMode) {
Expand Down

0 comments on commit d1f3853

Please sign in to comment.