Facebook SDK for Android providing comprehensive integration with Facebook platform features including Login, Sharing, Messenger, App Links, Analytics, and Graph API
—
The Facebook Messenger module provides direct integration with Facebook Messenger for sharing content and creating rich messaging experiences. This enables users to share content from your app directly to Messenger conversations.
Core utilities for Messenger integration:
object MessengerUtils {
// Messenger availability
fun hasMessengerInstalled(context: Context): Boolean
fun openMessengerInPlayStore(context: Context)
// Content sharing
fun shareToMessenger(
activity: Activity,
requestCode: Int,
shareToMessengerParams: ShareToMessengerParams
)
// Reply handling
fun handleMessengerThreadReply(
intent: Intent,
requestCode: Int
): ShareToMessengerParams?
// Intent creation
fun createShareToMessengerIntent(
shareToMessengerParams: ShareToMessengerParams
): Intent?
}class MessengerSharingActivity : AppCompatActivity() {
companion object {
private const val SHARE_TO_MESSENGER_REQUEST_CODE = 1000
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Check if Messenger is installed
if (MessengerUtils.hasMessengerInstalled(this)) {
shareToMessenger()
} else {
// Prompt user to install Messenger
showInstallMessengerDialog()
}
}
private fun shareToMessenger() {
// Create content to share
val bitmap = createSharableBitmap()
val shareParams = ShareToMessengerParams.newBuilder(bitmap, "image/png")
.setMetaData(Bundle().apply {
putString("content_type", "photo")
putString("source", "my_app")
putLong("timestamp", System.currentTimeMillis())
})
.build()
MessengerUtils.shareToMessenger(this, SHARE_TO_MESSENGER_REQUEST_CODE, shareParams)
}
private fun showInstallMessengerDialog() {
AlertDialog.Builder(this)
.setTitle("Install Messenger")
.setMessage("This feature requires Facebook Messenger. Would you like to install it?")
.setPositiveButton("Install") { _, _ ->
MessengerUtils.openMessengerInPlayStore(this)
}
.setNegativeButton("Cancel", null)
.show()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == SHARE_TO_MESSENGER_REQUEST_CODE) {
if (resultCode == RESULT_OK) {
Log.d("Messenger", "Content shared successfully")
// Handle reply if user responds in Messenger
val replyParams = data?.let {
MessengerUtils.handleMessengerThreadReply(it, requestCode)
}
if (replyParams != null) {
handleMessengerReply(replyParams)
}
} else {
Log.d("Messenger", "Share cancelled or failed")
}
}
}
private fun handleMessengerReply(replyParams: ShareToMessengerParams) {
val metaData = replyParams.metaData
val isReply = metaData?.getBoolean(ShareToMessengerParams.RECIPIENT_DID_REPLY, false) ?: false
if (isReply) {
val threadToken = metaData?.getString(ShareToMessengerParams.THREAD_TOKEN)
Log.d("Messenger", "Received reply from thread: $threadToken")
// Handle the reply content
val uri = replyParams.uri
val mimeType = replyParams.mimeType
// Process the reply based on content type
when {
mimeType?.startsWith("image/") == true -> handleImageReply(uri)
mimeType?.startsWith("video/") == true -> handleVideoReply(uri)
else -> handleGenericReply(uri, mimeType)
}
}
}
}Configuration for content sharing to Messenger:
class ShareToMessengerParams private constructor() {
val uri: Uri?
val mimeType: String?
val metaData: Bundle?
companion object {
// Builder methods
fun newBuilder(uri: Uri, mimeType: String): Builder
fun newBuilder(bitmap: Bitmap, mimeType: String): Builder
// Metadata keys
const val THREAD_TOKEN = "thread_token"
const val RECIPIENT_DID_REPLY = "recipient_did_reply"
const val REPLY_TO_MESSAGE_ID = "reply_to_message_id"
const val SOURCE_APPLICATION = "source_application"
// MIME types
const val MIME_TYPE_PNG = "image/png"
const val MIME_TYPE_JPEG = "image/jpeg"
const val MIME_TYPE_GIF = "image/gif"
const val MIME_TYPE_MP4 = "video/mp4"
const val MIME_TYPE_WEBP = "image/webp"
}
class Builder internal constructor() {
fun setUri(uri: Uri): Builder
fun setBitmap(bitmap: Bitmap): Builder
fun setMimeType(mimeType: String): Builder
fun setMetaData(metaData: Bundle?): Builder
fun build(): ShareToMessengerParams
}
}class AdvancedMessengerSharing {
fun sharePhoto(activity: Activity, bitmap: Bitmap, caption: String) {
val metaData = Bundle().apply {
putString("content_type", "photo")
putString("caption", caption)
putString("app_name", activity.getString(R.string.app_name))
putLong("created_time", System.currentTimeMillis())
}
val params = ShareToMessengerParams.newBuilder(bitmap, ShareToMessengerParams.MIME_TYPE_PNG)
.setMetaData(metaData)
.build()
MessengerUtils.shareToMessenger(activity, SHARE_REQUEST_CODE, params)
}
fun shareVideo(activity: Activity, videoUri: Uri) {
val metaData = Bundle().apply {
putString("content_type", "video")
putString("video_source", "user_generated")
putLong("duration_ms", getVideoDuration(videoUri))
}
val params = ShareToMessengerParams.newBuilder(videoUri, ShareToMessengerParams.MIME_TYPE_MP4)
.setMetaData(metaData)
.build()
MessengerUtils.shareToMessenger(activity, SHARE_REQUEST_CODE, params)
}
fun shareGif(activity: Activity, gifUri: Uri) {
val params = ShareToMessengerParams.newBuilder(gifUri, ShareToMessengerParams.MIME_TYPE_GIF)
.setMetaData(Bundle().apply {
putString("content_type", "gif")
putBoolean("animated", true)
})
.build()
MessengerUtils.shareToMessenger(activity, SHARE_REQUEST_CODE, params)
}
fun shareWebpSticker(activity: Activity, stickerUri: Uri) {
val params = ShareToMessengerParams.newBuilder(stickerUri, ShareToMessengerParams.MIME_TYPE_WEBP)
.setMetaData(Bundle().apply {
putString("content_type", "sticker")
putBoolean("is_sticker", true)
})
.build()
MessengerUtils.shareToMessenger(activity, SHARE_REQUEST_CODE, params)
}
}Handle thread-specific messaging parameters:
class MessengerThreadParams private constructor() {
val threadToken: String?
val metaData: Bundle?
val participants: List<String>?
val isComposer: Boolean
companion object {
const val THREAD_PARTICIPANTS = "thread_participants"
const val IS_REPLY = "is_reply"
const val COMPOSER_CONTEXT = "composer_context"
}
class Builder {
fun setThreadToken(threadToken: String): Builder
fun setMetaData(metaData: Bundle): Builder
fun setParticipants(participants: List<String>): Builder
fun setIsComposer(isComposer: Boolean): Builder
fun build(): MessengerThreadParams
}
}Support for Messenger Platform Extensions:
class MessengerExtensions {
fun createQuickReply(text: String, payload: String): JSONObject {
return JSONObject().apply {
put("content_type", "text")
put("title", text)
put("payload", payload)
}
}
fun createButtonTemplate(
text: String,
buttons: List<JSONObject>
): JSONObject {
return JSONObject().apply {
put("template_type", "button")
put("text", text)
put("buttons", JSONArray(buttons))
}
}
fun createGenericTemplate(elements: List<JSONObject>): JSONObject {
return JSONObject().apply {
put("template_type", "generic")
put("elements", JSONArray(elements))
}
}
fun createWebUrlButton(title: String, url: String): JSONObject {
return JSONObject().apply {
put("type", "web_url")
put("title", title)
put("url", url)
}
}
fun createPostbackButton(title: String, payload: String): JSONObject {
return JSONObject().apply {
put("type", "postback")
put("title", title)
put("payload", payload)
}
}
}Messenger features specifically for gaming apps:
class MessengerGaming {
fun shareGameScore(
activity: Activity,
score: Int,
level: Int,
screenshot: Bitmap?
) {
val metaData = Bundle().apply {
putString("content_type", "game_score")
putInt("score", score)
putInt("level", level)
putString("game_name", activity.getString(R.string.app_name))
putLong("achieved_at", System.currentTimeMillis())
}
val bitmap = screenshot ?: createScoreShareBitmap(score, level)
val params = ShareToMessengerParams.newBuilder(bitmap, ShareToMessengerParams.MIME_TYPE_PNG)
.setMetaData(metaData)
.build()
MessengerUtils.shareToMessenger(activity, SHARE_REQUEST_CODE, params)
}
fun shareGameInvite(
activity: Activity,
inviteMessage: String,
gameData: String
) {
val metaData = Bundle().apply {
putString("content_type", "game_invite")
putString("invite_message", inviteMessage)
putString("game_data", gameData)
putString("invite_type", "challenge")
}
// Create invite image
val inviteBitmap = createGameInviteBitmap(inviteMessage)
val params = ShareToMessengerParams.newBuilder(inviteBitmap, ShareToMessengerParams.MIME_TYPE_PNG)
.setMetaData(metaData)
.build()
MessengerUtils.shareToMessenger(activity, SHARE_REQUEST_CODE, params)
}
fun shareGameResult(
activity: Activity,
gameResult: GameResult,
competitorName: String?
) {
val metaData = Bundle().apply {
putString("content_type", "game_result")
putString("result_type", gameResult.type)
putInt("player_score", gameResult.playerScore)
putInt("opponent_score", gameResult.opponentScore)
putString("winner", gameResult.winner)
competitorName?.let { putString("competitor_name", it) }
putBoolean("is_multiplayer", competitorName != null)
}
val resultBitmap = createGameResultBitmap(gameResult, competitorName)
val params = ShareToMessengerParams.newBuilder(resultBitmap, ShareToMessengerParams.MIME_TYPE_PNG)
.setMetaData(metaData)
.build()
MessengerUtils.shareToMessenger(activity, SHARE_REQUEST_CODE, params)
}
data class GameResult(
val type: String, // "win", "lose", "draw"
val playerScore: Int,
val opponentScore: Int,
val winner: String
)
}Handle replies and manage conversation threads:
class MessengerReplyHandler {
fun processReply(
context: Context,
replyParams: ShareToMessengerParams
) {
val metaData = replyParams.metaData ?: return
val threadToken = metaData.getString(ShareToMessengerParams.THREAD_TOKEN)
val isReply = metaData.getBoolean(ShareToMessengerParams.RECIPIENT_DID_REPLY, false)
if (isReply && threadToken != null) {
Log.d("Messenger", "Processing reply from thread: $threadToken")
// Store thread information for future reference
storeThreadInfo(context, threadToken, metaData)
// Process the reply content
val contentType = metaData.getString("content_type")
when (contentType) {
"photo" -> handlePhotoReply(replyParams)
"video" -> handleVideoReply(replyParams)
"sticker" -> handleStickerReply(replyParams)
"game_move" -> handleGameMoveReply(replyParams)
else -> handleGenericReply(replyParams)
}
// Track reply analytics
trackReplyReceived(contentType, threadToken)
}
}
private fun storeThreadInfo(context: Context, threadToken: String, metaData: Bundle) {
val prefs = context.getSharedPreferences("messenger_threads", Context.MODE_PRIVATE)
prefs.edit().apply {
putString("thread_$threadToken", metaData.toString())
putLong("thread_${threadToken}_last_activity", System.currentTimeMillis())
apply()
}
}
private fun handlePhotoReply(params: ShareToMessengerParams) {
val uri = params.uri
val metaData = params.metaData
// Process photo reply
Log.d("Messenger", "Received photo reply: $uri")
// Extract any game-specific data
val score = metaData?.getInt("score", -1)
val level = metaData?.getInt("level", -1)
if (score != -1 && level != -1) {
// This is a game score reply
handleGameScoreReply(uri, score, level)
} else {
// Regular photo reply
handleRegularPhotoReply(uri)
}
}
private fun handleGameMoveReply(params: ShareToMessengerParams) {
val metaData = params.metaData ?: return
val gameData = metaData.getString("game_data")
val moveData = metaData.getString("move_data")
val threadToken = metaData.getString(ShareToMessengerParams.THREAD_TOKEN)
if (gameData != null && moveData != null && threadToken != null) {
// Process the game move
processGameMove(threadToken, gameData, moveData)
// Generate response move if needed
generateResponseMove(threadToken, gameData, moveData)
}
}
private fun trackReplyReceived(contentType: String?, threadToken: String) {
val logger = AppEventsLogger.newLogger(context)
val parameters = Bundle().apply {
putString("reply_content_type", contentType)
putString("thread_token_hash", threadToken.hashCode().toString())
putLong("reply_timestamp", System.currentTimeMillis())
}
logger.logEvent("messenger_reply_received", parameters)
}
}Handle common Messenger integration errors:
class MessengerErrorHandler {
fun handleMessengerError(
activity: Activity,
error: Exception,
fallbackAction: () -> Unit
) {
when (error) {
is SecurityException -> {
// Permission issue
Log.e("Messenger", "Permission denied: ${error.message}")
showPermissionError(activity)
}
is ActivityNotFoundException -> {
// Messenger not installed or can't handle intent
Log.e("Messenger", "Messenger not available: ${error.message}")
offerMessengerInstallation(activity)
}
else -> {
// Other errors
Log.e("Messenger", "Messenger error: ${error.message}")
showGenericError(activity, fallbackAction)
}
}
}
private fun offerMessengerInstallation(activity: Activity) {
AlertDialog.Builder(activity)
.setTitle("Messenger Required")
.setMessage("This feature requires Facebook Messenger. Install it now?")
.setPositiveButton("Install") { _, _ ->
MessengerUtils.openMessengerInPlayStore(activity)
}
.setNegativeButton("Cancel", null)
.show()
}
private fun showGenericError(activity: Activity, fallbackAction: () -> Unit) {
AlertDialog.Builder(activity)
.setTitle("Sharing Failed")
.setMessage("Unable to share to Messenger. Try another sharing method?")
.setPositiveButton("Try Alternative") { _, _ ->
fallbackAction()
}
.setNegativeButton("Cancel", null)
.show()
}
fun validateShareParams(params: ShareToMessengerParams): Boolean {
return when {
params.uri == null -> {
Log.e("Messenger", "Share params missing URI")
false
}
params.mimeType.isNullOrEmpty() -> {
Log.e("Messenger", "Share params missing MIME type")
false
}
!isSupportedMimeType(params.mimeType) -> {
Log.e("Messenger", "Unsupported MIME type: ${params.mimeType}")
false
}
else -> true
}
}
private fun isSupportedMimeType(mimeType: String?): Boolean {
return mimeType in listOf(
ShareToMessengerParams.MIME_TYPE_PNG,
ShareToMessengerParams.MIME_TYPE_JPEG,
ShareToMessengerParams.MIME_TYPE_GIF,
ShareToMessengerParams.MIME_TYPE_MP4,
ShareToMessengerParams.MIME_TYPE_WEBP
)
}
}Install with Tessl CLI
npx tessl i tessl/maven-com-facebook-android--facebook-android-sdk