This recipe contains a big snippet that helps sharing media to various social platforms, and the instructions to set it up. Includes working with the Twitter and Facebook SDKs.
Set-up Facebook
After registering a Facebook app, enter the details in your module’s gradle file:
...
defaultConfig {
...
def facebookId = "12345"; // TODO Change this
resValue "string", "facebook_app_id", "$facebookId"
resValue "string", "fb_login_protocol_scheme", "fb$facebookId"
resValue "string", "fb_provider", "com.facebook.app.FacebookContentProvider$facebookId"
...
}
}
dependencies {
...
// Social
implementation "com.facebook.android:facebook-android-sdk:4.30.0"
implementation "com.twitter.sdk.android:twitter-core:3.1.1"
...
}
Also add the necessary details to the AndroidManifest.xml
:
...
<application>
<meta-data
android:name="com.facebook.sdk.ApplicationId"
android:value="@string/facebook_app_id" />
<activity
android:name="com.facebook.FacebookActivity"
android:configChanges="keyboard|keyboardHidden|screenLayout|screenSize|orientation"
android:label="@string/app_name" />
<activity
android:name="com.facebook.CustomTabActivity"
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 android:scheme="@string/fb_login_protocol_scheme" />
</intent-filter>
</activity>
<provider
android:name="com.facebook.FacebookContentProvider"
android:authorities="@string/fb_provider"
android:exported="true" />
</activity>
Include the ShareUtil.kt
snippet
This snippet contains various methods for sharing that are also explained in another snippet. The methods are pretty self-explanatory. Here’s a list of what it can do:
- ‘Save a GIF to the gallery’ - since GIFs can’t be stored in the media store, this saves it to the Downloads folder instead.
- Share to SMS, WhatsApp, email, etc.
- Upload pictures and videos to Facebook and Twitter.
- Share links to online pictures and videos to Facebook and Twitter.
- Open the Play Store for apps that are not installed.
package com.pixplicity.shareutil
import android.app.DownloadManager
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Context.DOWNLOAD_SERVICE
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.MediaStore
import android.provider.Telephony
import android.support.v4.app.ShareCompat
import android.support.v4.content.FileProvider
import android.support.v7.app.AppCompatActivity
import com.facebook.FacebookSdk
import com.facebook.share.model.*
import com.facebook.share.widget.ShareDialog
import java.io.File
import java.io.FileNotFoundException
import java.io.FileOutputStream
import java.io.IOException
class ShareUtil {
companion object {
const val INSTAGRAM = "com.instagram.android"
@Deprecated(message = "Don't use for uploading files.", replaceWith = ReplaceWith("shareVideoToFacebook, shareBitmapToFacebook"))
const val FACEBOOK = "com.facebook.katana"
@Deprecated(message = "Don't use for uploading files.", replaceWith = ReplaceWith("shareVideoToFacebook, shareBitmapToFacebook"))
const val FACEBOOK_LITE = "com.facebook.lite"
const val FACEBOOK_MESSENGER = "com.facebook.orca"
const val TWITTER = "com.twitter.android"
const val WHATSAPP = "com.whatsapp"
fun getSharedFilePath(context: Context, uri: Uri): File = File(File(context.filesDir, SHARED_FOLDER), uri.lastPathSegment)
fun getUriForFile(context: Context, file: File): Uri = FileProvider.getUriForFile(context, "${context.packageName}.sharefileprovider", file)
}
/**
* GIFs can't be inserted into the media store (just appears as a static jpeg) so we save it into the Downloads folder instead.
*/
fun saveGifToDownloadsFolder(context: Context, gifFile: File) {
val dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
val app = context.getString(R.string.app_name)
val now = System.currentTimeMillis()
val file = File(dir, "${app}-${now}.gif")
gifFile.copyTo(file)
val downloadManager = context.getSystemService(DOWNLOAD_SERVICE) as DownloadManager
downloadManager.addCompletedDownload(file.name, file.name, true, MIME_TYPE_IMAGE_GIF, file.absolutePath, file.length(), true)
}
private fun createIntentBuilder(activity: AppCompatActivity, content: String?): ShareCompat.IntentBuilder =
ShareCompat.IntentBuilder.from(activity).apply {
if (content != null) {
setText(content)
setType(MIME_TYPE_PLAIN)
}
}
private fun createIntentBuilder(activity: AppCompatActivity, fileUri: Uri?): ShareCompat.IntentBuilder {
val intent = ShareCompat.IntentBuilder.from(activity)
.setType(MIME_TYPE_IMAGE)
if (fileUri != null) {
intent.addStream(fileUri)
}
return intent
}
fun createChooserIntent(activity: AppCompatActivity, fileUri: Uri): Intent =
createIntentBuilder(activity, fileUri)
.createChooserIntent()
fun createChooserIntent(activity: AppCompatActivity, content: String): Intent =
createIntentBuilder(activity, content)
.createChooserIntent()
fun createShareIntent(activity: AppCompatActivity, fileUri: Uri): Intent =
createIntentBuilder(activity, fileUri)
.intent
fun createShareIntent(activity: AppCompatActivity, content: String): Intent =
createIntentBuilder(activity, content)
.intent
fun createShareIntentForPackage(activity: AppCompatActivity, content: String?, packageName: String): Intent =
createIntentBuilder(activity, content)
.intent
.setPackage(packageName)
fun createShareIntentForPackage(activity: AppCompatActivity, fileUri: Uri?, packageName: String): Intent =
createIntentBuilder(activity, fileUri)
.intent
.setPackage(packageName)
/**
* Checks if the user has a certain app installed
*
* @return `true` if the user has the app, `false` otherwise.
*/
fun hasApp(packageManager: PackageManager, packageName: String): Boolean = try {
val info = packageManager.getApplicationInfo(packageName, 0)
info != null
} catch (e: PackageManager.NameNotFoundException) {
false
}
/**
* Launches the share intent for the specified application, if it is installed.
*/
fun launchIf(activity: AppCompatActivity, fileUri: Uri?, packageName: String): Boolean {
if (hasApp(activity.packageManager, packageName)) {
activity.startActivity(createShareIntentForPackage(activity, fileUri, packageName))
return true
}
return false
}
/**
* Launches the Play store to download the specified application.
* If this is a device without Google Play, falls back to opening Google Play on the web.
*/
fun launchGooglePlay(activity: AppCompatActivity, packageName: String) {
try {
activity.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + packageName)))
} catch (anfe: android.content.ActivityNotFoundException) {
activity.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=" + packageName)))
}
}
/**
* Shares to the default SMS message app, via MMS if a file is specified
*/
fun createShareSms(activity: AppCompatActivity, fileUri: Uri?, mimeType: String? = null): Intent {
val intent: Intent
if (Build.VERSION.SDK_INT >= 19) {
val p = Telephony.Sms.getDefaultSmsPackage(activity)
intent = createShareIntentForPackage(activity, fileUri, p)
} else {
intent = Intent(Intent.ACTION_VIEW, Uri.parse("smsto:"))
}
intent.apply {
// putExtra("sms_body", "")
// putExtra("address", "")
if (fileUri != null) {
putExtra(Intent.EXTRA_STREAM, fileUri)
type = mimeType ?: MIME_TYPE_IMAGE
}
}
return intent
}
fun createShareEmail(content: String?, fileUri: Uri?): Intent {
val intent = Intent(Intent.ACTION_SEND)
intent.apply {
type = "message/rfc822"
if (content != null) {
putExtra(Intent.EXTRA_TEXT, content)
}
if (fileUri != null) {
putExtra(Intent.EXTRA_STREAM, fileUri)
}
}
return intent
}
/**
* Uses the Facebook SDK. Can be used for LOCAL uris, for GIFs and mp4
*/
fun shareVideoToFacebook(activity: AppCompatActivity, localUri: Uri) {
FacebookSdk.sdkInitialize(activity.applicationContext)
val video = ShareVideo.Builder()
.setLocalUrl(localUri)
.build()
val content = ShareVideoContent.Builder()
.setVideo(video)
.build()
showFacebookShareDialog(activity, content)
}
fun shareBitmapToFacebook(activity: AppCompatActivity, bitmap: Bitmap) {
FacebookSdk.sdkInitialize(activity.applicationContext)
val photo = SharePhoto.Builder()
.setBitmap(bitmap)
.build()
val content = SharePhotoContent.Builder()
.addPhoto(photo)
.build()
showFacebookShareDialog(activity, content)
}
private fun showFacebookShareDialog(activity: AppCompatActivity, content: ShareContent<*, *>) {
val dialog = ShareDialog(activity)
if (!dialog.canShow(content)) {
if (hasApp(activity.packageManager, FACEBOOK)) {
// User has to log in, launch the app
val intent = activity.packageManager.getLaunchIntentForPackage(ShareUtil.FACEBOOK)
if (intent != null) {
activity.startActivity(intent)
}
} else if (hasApp(activity.packageManager, FACEBOOK_LITE)) {
// User has to log in, launch the app
val intent = activity.packageManager.getLaunchIntentForPackage(ShareUtil.FACEBOOK_LITE)
if (intent != null) {
activity.startActivity(intent)
}
} else {
// User has to download app
launchGooglePlay(activity, FACEBOOK)
}
} else {
dialog.show(content)
}
}
/**
* Combines `launchIf` and `launchGooglePlay` to either launch the app or to launch the Play store.
*
* @param activity The parent activity
* @param packageName The app to share to. E.g. ShareUtil.TWITTER. Use `shareMore` for sharing to any app.
* @param fileUri Optional uri for a <strong>local</strong> file to attach/upload.
*/
fun share(activity: AppCompatActivity, packageName: String, fileUri: Uri?) {
if (!shareUtil.launchIf(this, fileUri, packageName)) {
shareUtil.launchGooglePlay(this, packageName)
}
}
/**
* Shares to any app
*/
fun shareMore(activity: AppCompatActivity, fileUri: Uri?) {
val chooserIntent = createChooserIntent(activity, fileUri)
if (fileUri != null) {
activity.packageManager.queryIntentActivities(chooserIntent, PackageManager.MATCH_DEFAULT_ONLY)
.map { it.activityInfo.packageName }
.forEach { grantUriPermission(it, fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION) }
}
startActivity(chooserIntent)
}
}