or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

app-events.mdapp-links.mdbolts-tasks.mdcore-authentication.mdgaming.mdgraph-api.mdindex.mdlogin.mdmessenger.mdsharing.md

messenger.mddocs/

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

```