으으음.. 딥링크도 처음 접해보고.. 카톡과 sdk 연결도 처음해보는 나는 이 기능 구현이 좀 힘들었다😭
저녁에 시작해서 이틀동안 잡고 있었다..!
심지어 내가 처음에 구현했던 건 pm님과의 소통 오류로 기각되어서 다시 구현했다..
나는 명함 이미지를 카톡으로 공유하는건지 알았는데 알고보니 이미지를 공유하는 것이 아닌 일련번호를 공유하는거!!
근데 일련번호만 텍스트로 띡 보내기엔 성의없어서?? 카톡 링크 공유에서 피드 템플릿을 이용하는게 좋다고 생각해 이를 이용하기로 했다!!
~~새로운 기능을 구현해볼 수 있는 좋은 기회라고 생각하기로ㅎㅎㅎㅎ
근데 우리 앱은 url이 없어서.. 어떻게 링크를 공유할지 고민하다가 url이 없어도 앱을 열 수 있는!! 딥링크라는게 있다고해서 이를 이용하기로함!
딥링크에서는 링크를 눌렀을때 가장 먼저 나오게 하고싶은 화면을 설정해주면 그 화면으로 바로 이동도 가능하다!!->나는 검색창이 바로 뜨도록 만들고자함!!
![]() | ![]() |
---|
1 startActivity()를 이용해 카카오톡 앱을 실행시키고 링크를 전달하기 때문에 프래그먼트랑 액티비티가 연결이 되어있어야하므로 리스너 인터페이스를 사용해 BottomSheet2Fragment에서 발생한 이벤트를 MainActivity2 클래스에서 처리하도록 했음!
아무래도 다이얼로그에서 버튼을 클릭하니깐,, 프래그먼트 간에 데이터 넘겨주는게 많다..!
2.카톡 링크 공유를 할때 피드 템플릿을 사용해 앱의 로고 이미지랑 제목, 설명을 함께 보내도록 함 + 일련번호는 아이템으로 설정
++버튼을 두개만들어 '앱으로 이동' 버튼에는 딥링크를 넣어줌
(URI 스킴 방식 이용 : 앱에 URI 스킴(scheme) 값을 등록하여 딥링크 사용)
3.카톡이 깔려있는 경우와 깔려있지 않는 경우를 나눠
깔려있는 경우에는 인텐트를 사용해 해당 링크를 실행
깔려있지 않는 경우에는 카톡 다운로드 웹사이트로 이동하게함
초기설정해줄게 매우매우 많다!!
오류가 나지 않도록 하나도 빠짐없이 해줘야함
우리는 카카오 소셜로그인을 사용했기 때문에 카카오에 앱등록은 되어있던 상태!!
여기서 네이티브 앱 키가 중요함! 복붙해서 쓸때 오타내지 않도록 주의 또 주의
settings.gradle에서 Android SDK 레파지토리를 설정
이것도 소셜로그인때 설정해줌!
repositories {
google()
mavenCentral()
//maven에 카카오
maven(url = "https://devrepo.kakao.com/nexus/content/groups/public/")
}
kakaolink를 이용한 카카오톡 공유하기 기능을 사용할 것이기 때문에 하나의 모듈만 추가해주면 된다.
implementation ("com.kakao.sdk:v2-share:2.19.0") // 메시지(카카오톡 공유)
<uses-permission android:name="android.permission.INTERNET" />
<queries>
<package android:name="com.kakao.talk" />
</queries>
//스킴 딥링크 이용
<activity
android:name=".Myprofile.MainActivity2"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
//data 태그에 URL Scheme을 정의할 수 있음
//여기서는 kakao네이티브앱키://kakaolink 가 딥링크로 설정됨
<data android:host="kakaolink"
android:scheme="@string/kakao_scheme" />
</intent-filter>
</activity>
여기서,,, string은 네이티브 앱 키를 저장해둔거!!
이중에서 kakao+네이티브 앱 키 가져와 사용해줌
<string name="kakao_scheme" translatable="false">kakao네이티브앱키</string>
<string name="kakao_share_app_key" translatable="false">네이티브앱키</string>
Application을 상속한 클래스를 사용해서 초기화해줌
카카오 init 에 아까 string 으로 만들어 두었던 네이티브 앱 키 를 가져와서 사용
public class KakaoApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
//카카오 SDK 초기화
KakaoSdk.init(this, "14bbf6997b475309590efc7c9a7d9b11");
}
}
<application
//application과 동일한 클래스 이름 쓰기!!
android:name=".KakaoApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/icon_aboutme"
android:label="@string/app_name"
android:roundIcon="@mipmap/icon_aboutme_round"
android:supportsRtl="true"
android:theme="@style/Theme.AboutMe"
tools:targetApi="33">
이제 본격적으로 코드를 짜는데 우선 액티비티랑 다이얼로그를 연결하는 흐름을 설명하자면..
mainactivity2랑 mainprofilefragment를 연결하고 그 다음 bottomsheet2 리스너를 만들어서 mainactivity2랑 bottomsheet2fragment랑도 연결함!!
=>setActivity(activity: MainActivity2) 함수를 사용하여 mActivity 변수가 MainActivity2의 인스턴스와 연결 = MainActivity2와 MainProfileFragment 사이의 연결을 나타낸다.
=>BottomSheet2.OnBottomSheetListener 인터페이스를 구현한 객체를 setOnBottomSheetListener(listener: BottomSheet2.OnBottomSheetListener) 함수를 통해 mListener 변수에 연결 = MainActivity2와 BottomSheet2Fragment 사이의 연결을 나타낸다.
따라서, MainActivity2와 MainProfileFragment 간의 연결을 위해 setActivity(activity: MainActivity2) 함수를 호출하고, MainActivity2와 BottomSheet2Fragment 간의 연결을 위해 setOnBottomSheetListener(listener: BottomSheet2.OnBottomSheetListener) 함수를 호출
mActivity 변수가 MainActivity2의 인스턴스와 연결하는 메서드 setActivity()
=> 이는 MainActivity2와 MainProfileFragment 사이의 연결을 나타낸다
//MyprofileFragment
fun setActivity(activity: MainActivity2) {
mActivity = activity
}
class MainActivity2 : AppCompatActivity(){
//onCreate() 메서드에서 MyProfileFragment를 생성하고,
//supportFragmentManager를 사용하여 화면에 추가
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
if (savedInstanceState == null) {
val myProfileFragment = MyProfileFragment()
supportFragmentManager.beginTransaction().apply {
setReorderingAllowed(true)
add(R.id.frame_container, myProfileFragment)
commit()
}
}
}
// 액티비티가 활성화되어 사용자와 상호작용이 가능한 상태가 될 때 호출되는 메서드
//MyProfileFragment의 인스턴스를 찾아 초기화 코드를 실행
override fun onResume() {
super.onResume()
// 프래그먼트가 완전히 활성화된 후에 초기화 코드 실행
val myProfileFragment =
supportFragmentManager.findFragmentById(R.id.frame_container) as? MyProfileFragment
//setActivity(this)를 호출하여 MyProfileFragment에 현재 액티비티의 참조 전달
//MyProfileFragment에 현재 액티비티 설정
// BottomSheet2 리스너 설정
myProfileFragment?.setActivity(this)
}
}
//bottomsheet2에서 발생한 액션을 처리하기 위한 리스너 인터페이스
interface OnBottomSheetListener {
// 선언된 인터페이스 메서드 onBottomSheetAction
fun onBottomSheetAction()
}
// BottomSheet 이벤트를 처리하는 리스너를 담을 변수. 초기값은 null.
private var listener: OnBottomSheetListener? = null
// 외부에서 이 메서드를 호출하여 listener 변수에 새로운 리스너를 설정할 수 있도록 하는 함수
fun setOnBottomSheetListener(listener: OnBottomSheetListener) {
// 전달된 리스너를 클래스 내부의 리스너 변수에 할당
this.listener = listener
}
}
bottomsheet2
//MyprofileFragment가 BottomSheet2.OnBottomSheetListener인터페이스 구현
class MyProfileFragment : Fragment(), BottomSheet2.OnBottomSheetListener {
//mListener는 BottomSheet2.OnBottomSheetListener 인터페이스를 구현한 객체를 나타내는 변수
private var mListener: BottomSheet2.OnBottomSheetListener? = null
//외부에서 호출 가능한 함수
//BottomSheet2.OnBottomSheetListener 인터페이스를 구현한 객체를 인자로 받아 mListener 변수에 새로운 리스너를 설정할 수 있음
fun setOnBottomSheetListener(listener: BottomSheet2.OnBottomSheetListener) {
mListener = listener
}
BottomSheet2.OnBottomSheetListener 인터페이스를 구현한 객체를 setOnBottomSheetListener(listener: BottomSheet2.OnBottomSheetListener) 함수를 통해 mListener 변수에 연결 = MainActivity2와 BottomSheet2Fragment가 연결됨!
//MainActivity2가 BottomSheet2.OnBottomSheetListener 인터페이스를 구현
class MainActivity2 : AppCompatActivity(), BottomSheet2.OnBottomSheetListener{
//MyProfileFragment의 인스턴스를 찾아 초기화 코드를 실행
override fun onResume() {
super.onResume()
// 프래그먼트가 완전히 활성화된 후에 초기화 코드 실행
val myProfileFragment =
supportFragmentManager.findFragmentById(R.id.frame_container) as? MyProfileFragment
//setActivity(this)를 호출하여 MyProfileFragment에 현재 액티비티(MainActivity2)의 참조를 전달
myProfileFragment?.setActivity(this)
//setOnBottomSheetListener(this)를 호출하여 MainActivity2를 BottomSheet2.OnBottomSheetListener로 설정
myProfileFragment?.setOnBottomSheetListener(this)
}
//onBottomSheetAction() 메서드를 오버라이드하여 BottomSheet2에서 발생한 액션에 대한 동작을 정의
//BottomSheet2에서 어떤 액션이 발생했을 때 MainActivity2에서 해당 액션에 대한 처리를 수행할 수 있음
override fun onBottomSheetAction() {
// BottomSheet2에서 발생한 액션에 대한 동작
}
}
카톡 링크 공유를 위한 메세지 템플릿 만들기!!(피드형)
메세지 형식 만드는 함수 -> 카카오 개발 웹사이트에서 형식 참고
//defaultFeed는 FeedTemplate 객체를 나타내며, 지연 초기화를 통해 사용될 때까지 초기화를 지연
private val defaultFeed: FeedTemplate by lazy {
// 메시지 템플릿 만들기 (피드형)
FeedTemplate(
content = Content(
title = "테디님의 AboutMe 프로필을 확인해보세요",
description = "앱의 홈 화면에 있는 검색창에 일련번호를 입력하면 프로필을 찾을 수 있어요.",
//로고 이미지 : url로 넣어줘야하기 떄문에 imgur이라는 사이트 이용
imageUrl = "https://i.imgur.com/8LO8kWd.png",
link = Link(
webUrl = "https://developers.kakao.com",
mobileWebUrl = "https://developers.kakao.com"
)
),
itemContent = ItemContent(
profileText = "일련번호 : 123456"
),
buttons = listOf(
Button(
"앱 다운로드",
Link(
webUrl = "https://developers.kakao.com",
mobileWebUrl = "https://developers.kakao.com"
)
),
Button(
"앱으로 이동",
Link(
//바로 앱으로 이동하게 해주는 딥링크
//key랑 value 부분을 사용해서 앱에서 어떤 상세페이지를 띄울지 결정
androidExecutionParams = mapOf("key1" to "value1")
)
)
)
)
}
BottomSheet2프래그먼트에서 카톡 공유 버튼이 눌렸을때 설정
// 'shareBottomSheet2KakaoBtn' 버튼에 대한 클릭 리스너 설정
binding.shareBottomSheet2KakaoBtn.setOnClickListener {
// 버튼이 클릭되었을 때, 'listener' 객체가 null이 아니라면 'onBottomSheetAction()' 메서드 호출
listener?.onBottomSheetAction()
// 'context'가 null이 아니라면 실행
context?.let { nonNullContext ->
// 카카오톡 링크를 공유하는 작업 수행
// 현재 프래그먼트가 액티비티에 추가되었다면 실행
if (isAdded) {
ShareClient.instance.shareDefault(
nonNullContext, // 카카오톡 링크를 공유할 때 필요한 컨텍스트
defaultFeed // 카카오톡 링크에 사용될 기본 피드 정보
// 카카오톡 링크 공유 결과 및 오류 처리
) { sharingResult, error ->
// 카카오톡 링크 공유 실패 시 처리
if (error != null) {
// 카카오톡이 설치되어 있지 않은 경우 메시지 표시
Toast.makeText(
nonNullContext,
"카카오톡이 설치되어있지 않습니다",
Toast.LENGTH_SHORT
).show()
// 카카오톡 링크 공유 실패 시 웹으로 공유 시도
val sharerUrl = WebSharerClient.instance.makeDefaultUrl(defaultFeed)
// 웹으로 공유 시도 중 에러 발생 시 예외 처리
try {
KakaoCustomTabsClient.openWithDefault(nonNullContext, sharerUrl)
} catch (e: UnsupportedOperationException) {
// CustomTabsServiceConnection 지원 브라우저가 없을 때 예외처리
}
// 디바이스에 설치된 인터넷 브라우저가 없을 때 예외 처리
try {
KakaoCustomTabsClient.open(nonNullContext, sharerUrl)
} catch (e: ActivityNotFoundException) {
// 디바이스에 설치된 인터넷 브라우저가 없을 때 예외처리
}
// 카카오톡 링크 공유 실패 시 다이얼로그 닫기
dismiss()
} else if (sharingResult != null) { // 카카오톡 링크 공유 성공 시 처리
// 카카오톡 링크 공유 성공 시 해당 링크 실행
//암시적 인텐트 사용: 앱 외부의 액티비티나 서비스를 호출
startActivity(sharingResult.intent)
// 카카오톡 링크 공유 성공 시 경고 및 인자 메시지 표시
Log.d(TAG, "카카오톡 공유 성공 ${sharingResult.intent}")
Log.w(TAG, "Warning Msg: ${sharingResult.warningMsg}")
Log.w(TAG, "Argument Msg: ${sharingResult.argumentMsg}")
// 카카오톡 링크 공유 성공 시 다이얼로그 닫기
dismiss()
}
}
}
}
}
인텐트
여기서는 암시적 인텐트 사용!! 클래스명이 아닌 Manifest의 Intent Filter 정보를 활용합니다. 주로 클래스명을 알 수 없는 외부 앱의 컴포넌트를 실행할 때 이용
1.화면간 이동할때
단순히 액티비티를 띄워주는 것이 아니라 어떤 액티비티를 띄운 것인지 그리고 띄웠던 액티비티로부터의 응답을 받아 처리하는 코드가 필요함!
액티비티를 띄우기 위해 사용되는 메서드는 startActivity()와 startActivityForResult()이 있음override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(binding.root) val intent = Intent(this, SubActivity::class.java) . binding.button.setOnClickListener{startActivity(intent)} }
2. 화면 간 데이터 전달할때
액티비티 간 정보 주고 받을때
-메인액티비티
Key, Value로 값을 담아서 보냄
putExtra() 함수 이용해 인텐트에 엑스트라 데이터를 추가
Data2의 value는 Int형 데이터 이기 때문에 문자열 템플릿 (${}) 을 사용해서 문자열로 변환해주기val intent = Intent(this, DetailActivity::class.java) intent.putExtra("data1", "hello") intent.putExtra("data2",10) startActivity(intent)
-서브액티비티
인텐트 객체만들고 get IntExtra() 형태의 함수로 데이터를 가져옴val intent = Intent val data1 = intent.getStringExtra("data1") val data2 = intent.getIntExtra("data2", 0)