0
# Messenger Integration
1
2
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.
3
4
## Messenger Utils
5
6
Core utilities for Messenger integration:
7
8
```kotlin { .api }
9
object MessengerUtils {
10
// Messenger availability
11
fun hasMessengerInstalled(context: Context): Boolean
12
fun openMessengerInPlayStore(context: Context)
13
14
// Content sharing
15
fun shareToMessenger(
16
activity: Activity,
17
requestCode: Int,
18
shareToMessengerParams: ShareToMessengerParams
19
)
20
21
// Reply handling
22
fun handleMessengerThreadReply(
23
intent: Intent,
24
requestCode: Int
25
): ShareToMessengerParams?
26
27
// Intent creation
28
fun createShareToMessengerIntent(
29
shareToMessengerParams: ShareToMessengerParams
30
): Intent?
31
}
32
```
33
34
### Basic Messenger Sharing
35
36
```kotlin
37
class MessengerSharingActivity : AppCompatActivity() {
38
companion object {
39
private const val SHARE_TO_MESSENGER_REQUEST_CODE = 1000
40
}
41
42
override fun onCreate(savedInstanceState: Bundle?) {
43
super.onCreate(savedInstanceState)
44
45
// Check if Messenger is installed
46
if (MessengerUtils.hasMessengerInstalled(this)) {
47
shareToMessenger()
48
} else {
49
// Prompt user to install Messenger
50
showInstallMessengerDialog()
51
}
52
}
53
54
private fun shareToMessenger() {
55
// Create content to share
56
val bitmap = createSharableBitmap()
57
58
val shareParams = ShareToMessengerParams.newBuilder(bitmap, "image/png")
59
.setMetaData(Bundle().apply {
60
putString("content_type", "photo")
61
putString("source", "my_app")
62
putLong("timestamp", System.currentTimeMillis())
63
})
64
.build()
65
66
MessengerUtils.shareToMessenger(this, SHARE_TO_MESSENGER_REQUEST_CODE, shareParams)
67
}
68
69
private fun showInstallMessengerDialog() {
70
AlertDialog.Builder(this)
71
.setTitle("Install Messenger")
72
.setMessage("This feature requires Facebook Messenger. Would you like to install it?")
73
.setPositiveButton("Install") { _, _ ->
74
MessengerUtils.openMessengerInPlayStore(this)
75
}
76
.setNegativeButton("Cancel", null)
77
.show()
78
}
79
80
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
81
super.onActivityResult(requestCode, resultCode, data)
82
83
if (requestCode == SHARE_TO_MESSENGER_REQUEST_CODE) {
84
if (resultCode == RESULT_OK) {
85
Log.d("Messenger", "Content shared successfully")
86
87
// Handle reply if user responds in Messenger
88
val replyParams = data?.let {
89
MessengerUtils.handleMessengerThreadReply(it, requestCode)
90
}
91
92
if (replyParams != null) {
93
handleMessengerReply(replyParams)
94
}
95
} else {
96
Log.d("Messenger", "Share cancelled or failed")
97
}
98
}
99
}
100
101
private fun handleMessengerReply(replyParams: ShareToMessengerParams) {
102
val metaData = replyParams.metaData
103
val isReply = metaData?.getBoolean(ShareToMessengerParams.RECIPIENT_DID_REPLY, false) ?: false
104
105
if (isReply) {
106
val threadToken = metaData?.getString(ShareToMessengerParams.THREAD_TOKEN)
107
Log.d("Messenger", "Received reply from thread: $threadToken")
108
109
// Handle the reply content
110
val uri = replyParams.uri
111
val mimeType = replyParams.mimeType
112
113
// Process the reply based on content type
114
when {
115
mimeType?.startsWith("image/") == true -> handleImageReply(uri)
116
mimeType?.startsWith("video/") == true -> handleVideoReply(uri)
117
else -> handleGenericReply(uri, mimeType)
118
}
119
}
120
}
121
}
122
```
123
124
## Share to Messenger Params
125
126
Configuration for content sharing to Messenger:
127
128
```kotlin { .api }
129
class ShareToMessengerParams private constructor() {
130
val uri: Uri?
131
val mimeType: String?
132
val metaData: Bundle?
133
134
companion object {
135
// Builder methods
136
fun newBuilder(uri: Uri, mimeType: String): Builder
137
fun newBuilder(bitmap: Bitmap, mimeType: String): Builder
138
139
// Metadata keys
140
const val THREAD_TOKEN = "thread_token"
141
const val RECIPIENT_DID_REPLY = "recipient_did_reply"
142
const val REPLY_TO_MESSAGE_ID = "reply_to_message_id"
143
const val SOURCE_APPLICATION = "source_application"
144
145
// MIME types
146
const val MIME_TYPE_PNG = "image/png"
147
const val MIME_TYPE_JPEG = "image/jpeg"
148
const val MIME_TYPE_GIF = "image/gif"
149
const val MIME_TYPE_MP4 = "video/mp4"
150
const val MIME_TYPE_WEBP = "image/webp"
151
}
152
153
class Builder internal constructor() {
154
fun setUri(uri: Uri): Builder
155
fun setBitmap(bitmap: Bitmap): Builder
156
fun setMimeType(mimeType: String): Builder
157
fun setMetaData(metaData: Bundle?): Builder
158
fun build(): ShareToMessengerParams
159
}
160
}
161
```
162
163
### Advanced Messenger Sharing
164
165
```kotlin
166
class AdvancedMessengerSharing {
167
168
fun sharePhoto(activity: Activity, bitmap: Bitmap, caption: String) {
169
val metaData = Bundle().apply {
170
putString("content_type", "photo")
171
putString("caption", caption)
172
putString("app_name", activity.getString(R.string.app_name))
173
putLong("created_time", System.currentTimeMillis())
174
}
175
176
val params = ShareToMessengerParams.newBuilder(bitmap, ShareToMessengerParams.MIME_TYPE_PNG)
177
.setMetaData(metaData)
178
.build()
179
180
MessengerUtils.shareToMessenger(activity, SHARE_REQUEST_CODE, params)
181
}
182
183
fun shareVideo(activity: Activity, videoUri: Uri) {
184
val metaData = Bundle().apply {
185
putString("content_type", "video")
186
putString("video_source", "user_generated")
187
putLong("duration_ms", getVideoDuration(videoUri))
188
}
189
190
val params = ShareToMessengerParams.newBuilder(videoUri, ShareToMessengerParams.MIME_TYPE_MP4)
191
.setMetaData(metaData)
192
.build()
193
194
MessengerUtils.shareToMessenger(activity, SHARE_REQUEST_CODE, params)
195
}
196
197
fun shareGif(activity: Activity, gifUri: Uri) {
198
val params = ShareToMessengerParams.newBuilder(gifUri, ShareToMessengerParams.MIME_TYPE_GIF)
199
.setMetaData(Bundle().apply {
200
putString("content_type", "gif")
201
putBoolean("animated", true)
202
})
203
.build()
204
205
MessengerUtils.shareToMessenger(activity, SHARE_REQUEST_CODE, params)
206
}
207
208
fun shareWebpSticker(activity: Activity, stickerUri: Uri) {
209
val params = ShareToMessengerParams.newBuilder(stickerUri, ShareToMessengerParams.MIME_TYPE_WEBP)
210
.setMetaData(Bundle().apply {
211
putString("content_type", "sticker")
212
putBoolean("is_sticker", true)
213
})
214
.build()
215
216
MessengerUtils.shareToMessenger(activity, SHARE_REQUEST_CODE, params)
217
}
218
}
219
```
220
221
## Messenger Thread Params
222
223
Handle thread-specific messaging parameters:
224
225
```kotlin { .api }
226
class MessengerThreadParams private constructor() {
227
val threadToken: String?
228
val metaData: Bundle?
229
val participants: List<String>?
230
val isComposer: Boolean
231
232
companion object {
233
const val THREAD_PARTICIPANTS = "thread_participants"
234
const val IS_REPLY = "is_reply"
235
const val COMPOSER_CONTEXT = "composer_context"
236
}
237
238
class Builder {
239
fun setThreadToken(threadToken: String): Builder
240
fun setMetaData(metaData: Bundle): Builder
241
fun setParticipants(participants: List<String>): Builder
242
fun setIsComposer(isComposer: Boolean): Builder
243
fun build(): MessengerThreadParams
244
}
245
}
246
```
247
248
## Messenger Extensions Support
249
250
Support for Messenger Platform Extensions:
251
252
```kotlin
253
class MessengerExtensions {
254
255
fun createQuickReply(text: String, payload: String): JSONObject {
256
return JSONObject().apply {
257
put("content_type", "text")
258
put("title", text)
259
put("payload", payload)
260
}
261
}
262
263
fun createButtonTemplate(
264
text: String,
265
buttons: List<JSONObject>
266
): JSONObject {
267
return JSONObject().apply {
268
put("template_type", "button")
269
put("text", text)
270
put("buttons", JSONArray(buttons))
271
}
272
}
273
274
fun createGenericTemplate(elements: List<JSONObject>): JSONObject {
275
return JSONObject().apply {
276
put("template_type", "generic")
277
put("elements", JSONArray(elements))
278
}
279
}
280
281
fun createWebUrlButton(title: String, url: String): JSONObject {
282
return JSONObject().apply {
283
put("type", "web_url")
284
put("title", title)
285
put("url", url)
286
}
287
}
288
289
fun createPostbackButton(title: String, payload: String): JSONObject {
290
return JSONObject().apply {
291
put("type", "postback")
292
put("title", title)
293
put("payload", payload)
294
}
295
}
296
}
297
```
298
299
## Gaming-Specific Messenger Integration
300
301
Messenger features specifically for gaming apps:
302
303
```kotlin
304
class MessengerGaming {
305
306
fun shareGameScore(
307
activity: Activity,
308
score: Int,
309
level: Int,
310
screenshot: Bitmap?
311
) {
312
val metaData = Bundle().apply {
313
putString("content_type", "game_score")
314
putInt("score", score)
315
putInt("level", level)
316
putString("game_name", activity.getString(R.string.app_name))
317
putLong("achieved_at", System.currentTimeMillis())
318
}
319
320
val bitmap = screenshot ?: createScoreShareBitmap(score, level)
321
322
val params = ShareToMessengerParams.newBuilder(bitmap, ShareToMessengerParams.MIME_TYPE_PNG)
323
.setMetaData(metaData)
324
.build()
325
326
MessengerUtils.shareToMessenger(activity, SHARE_REQUEST_CODE, params)
327
}
328
329
fun shareGameInvite(
330
activity: Activity,
331
inviteMessage: String,
332
gameData: String
333
) {
334
val metaData = Bundle().apply {
335
putString("content_type", "game_invite")
336
putString("invite_message", inviteMessage)
337
putString("game_data", gameData)
338
putString("invite_type", "challenge")
339
}
340
341
// Create invite image
342
val inviteBitmap = createGameInviteBitmap(inviteMessage)
343
344
val params = ShareToMessengerParams.newBuilder(inviteBitmap, ShareToMessengerParams.MIME_TYPE_PNG)
345
.setMetaData(metaData)
346
.build()
347
348
MessengerUtils.shareToMessenger(activity, SHARE_REQUEST_CODE, params)
349
}
350
351
fun shareGameResult(
352
activity: Activity,
353
gameResult: GameResult,
354
competitorName: String?
355
) {
356
val metaData = Bundle().apply {
357
putString("content_type", "game_result")
358
putString("result_type", gameResult.type)
359
putInt("player_score", gameResult.playerScore)
360
putInt("opponent_score", gameResult.opponentScore)
361
putString("winner", gameResult.winner)
362
competitorName?.let { putString("competitor_name", it) }
363
putBoolean("is_multiplayer", competitorName != null)
364
}
365
366
val resultBitmap = createGameResultBitmap(gameResult, competitorName)
367
368
val params = ShareToMessengerParams.newBuilder(resultBitmap, ShareToMessengerParams.MIME_TYPE_PNG)
369
.setMetaData(metaData)
370
.build()
371
372
MessengerUtils.shareToMessenger(activity, SHARE_REQUEST_CODE, params)
373
}
374
375
data class GameResult(
376
val type: String, // "win", "lose", "draw"
377
val playerScore: Int,
378
val opponentScore: Int,
379
val winner: String
380
)
381
}
382
```
383
384
## Reply Handling and Thread Management
385
386
Handle replies and manage conversation threads:
387
388
```kotlin
389
class MessengerReplyHandler {
390
391
fun processReply(
392
context: Context,
393
replyParams: ShareToMessengerParams
394
) {
395
val metaData = replyParams.metaData ?: return
396
val threadToken = metaData.getString(ShareToMessengerParams.THREAD_TOKEN)
397
val isReply = metaData.getBoolean(ShareToMessengerParams.RECIPIENT_DID_REPLY, false)
398
399
if (isReply && threadToken != null) {
400
Log.d("Messenger", "Processing reply from thread: $threadToken")
401
402
// Store thread information for future reference
403
storeThreadInfo(context, threadToken, metaData)
404
405
// Process the reply content
406
val contentType = metaData.getString("content_type")
407
when (contentType) {
408
"photo" -> handlePhotoReply(replyParams)
409
"video" -> handleVideoReply(replyParams)
410
"sticker" -> handleStickerReply(replyParams)
411
"game_move" -> handleGameMoveReply(replyParams)
412
else -> handleGenericReply(replyParams)
413
}
414
415
// Track reply analytics
416
trackReplyReceived(contentType, threadToken)
417
}
418
}
419
420
private fun storeThreadInfo(context: Context, threadToken: String, metaData: Bundle) {
421
val prefs = context.getSharedPreferences("messenger_threads", Context.MODE_PRIVATE)
422
prefs.edit().apply {
423
putString("thread_$threadToken", metaData.toString())
424
putLong("thread_${threadToken}_last_activity", System.currentTimeMillis())
425
apply()
426
}
427
}
428
429
private fun handlePhotoReply(params: ShareToMessengerParams) {
430
val uri = params.uri
431
val metaData = params.metaData
432
433
// Process photo reply
434
Log.d("Messenger", "Received photo reply: $uri")
435
436
// Extract any game-specific data
437
val score = metaData?.getInt("score", -1)
438
val level = metaData?.getInt("level", -1)
439
440
if (score != -1 && level != -1) {
441
// This is a game score reply
442
handleGameScoreReply(uri, score, level)
443
} else {
444
// Regular photo reply
445
handleRegularPhotoReply(uri)
446
}
447
}
448
449
private fun handleGameMoveReply(params: ShareToMessengerParams) {
450
val metaData = params.metaData ?: return
451
val gameData = metaData.getString("game_data")
452
val moveData = metaData.getString("move_data")
453
val threadToken = metaData.getString(ShareToMessengerParams.THREAD_TOKEN)
454
455
if (gameData != null && moveData != null && threadToken != null) {
456
// Process the game move
457
processGameMove(threadToken, gameData, moveData)
458
459
// Generate response move if needed
460
generateResponseMove(threadToken, gameData, moveData)
461
}
462
}
463
464
private fun trackReplyReceived(contentType: String?, threadToken: String) {
465
val logger = AppEventsLogger.newLogger(context)
466
val parameters = Bundle().apply {
467
putString("reply_content_type", contentType)
468
putString("thread_token_hash", threadToken.hashCode().toString())
469
putLong("reply_timestamp", System.currentTimeMillis())
470
}
471
logger.logEvent("messenger_reply_received", parameters)
472
}
473
}
474
```
475
476
## Error Handling and Fallbacks
477
478
Handle common Messenger integration errors:
479
480
```kotlin
481
class MessengerErrorHandler {
482
483
fun handleMessengerError(
484
activity: Activity,
485
error: Exception,
486
fallbackAction: () -> Unit
487
) {
488
when (error) {
489
is SecurityException -> {
490
// Permission issue
491
Log.e("Messenger", "Permission denied: ${error.message}")
492
showPermissionError(activity)
493
}
494
is ActivityNotFoundException -> {
495
// Messenger not installed or can't handle intent
496
Log.e("Messenger", "Messenger not available: ${error.message}")
497
offerMessengerInstallation(activity)
498
}
499
else -> {
500
// Other errors
501
Log.e("Messenger", "Messenger error: ${error.message}")
502
showGenericError(activity, fallbackAction)
503
}
504
}
505
}
506
507
private fun offerMessengerInstallation(activity: Activity) {
508
AlertDialog.Builder(activity)
509
.setTitle("Messenger Required")
510
.setMessage("This feature requires Facebook Messenger. Install it now?")
511
.setPositiveButton("Install") { _, _ ->
512
MessengerUtils.openMessengerInPlayStore(activity)
513
}
514
.setNegativeButton("Cancel", null)
515
.show()
516
}
517
518
private fun showGenericError(activity: Activity, fallbackAction: () -> Unit) {
519
AlertDialog.Builder(activity)
520
.setTitle("Sharing Failed")
521
.setMessage("Unable to share to Messenger. Try another sharing method?")
522
.setPositiveButton("Try Alternative") { _, _ ->
523
fallbackAction()
524
}
525
.setNegativeButton("Cancel", null)
526
.show()
527
}
528
529
fun validateShareParams(params: ShareToMessengerParams): Boolean {
530
return when {
531
params.uri == null -> {
532
Log.e("Messenger", "Share params missing URI")
533
false
534
}
535
params.mimeType.isNullOrEmpty() -> {
536
Log.e("Messenger", "Share params missing MIME type")
537
false
538
}
539
!isSupportedMimeType(params.mimeType) -> {
540
Log.e("Messenger", "Unsupported MIME type: ${params.mimeType}")
541
false
542
}
543
else -> true
544
}
545
}
546
547
private fun isSupportedMimeType(mimeType: String?): Boolean {
548
return mimeType in listOf(
549
ShareToMessengerParams.MIME_TYPE_PNG,
550
ShareToMessengerParams.MIME_TYPE_JPEG,
551
ShareToMessengerParams.MIME_TYPE_GIF,
552
ShareToMessengerParams.MIME_TYPE_MP4,
553
ShareToMessengerParams.MIME_TYPE_WEBP
554
)
555
}
556
}
557
```