Skip to content

Instantly share code, notes, and snippets.

@sunmeat
Created February 1, 2024 09:27
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sunmeat/1b1cde13ab4b9fb0fba753072d7a734b to your computer and use it in GitHub Desktop.
Save sunmeat/1b1cde13ab4b9fb0fba753072d7a734b to your computer and use it in GitHub Desktop.
android + facebook integration example (login + post text + post photo)
AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
...
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
...
<application
...
android:theme="@android:style/Theme.NoTitleBar"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<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>
<meta-data
android:name="com.facebook.sdk.ApplicationId"
android:value="@string/app_id" />
<meta-data
android:name="com.facebook.sdk.ApplicationName"
android:value="@string/facebook_app_name" />
<provider
android:name="com.facebook.FacebookContentProvider"
android:authorities="com.facebook.app.FacebookContentProvider355198514515820"
android:exported="false" />
<receiver
android:name="com.sunmeat.facebook.HelloFacebookBroadcastReceiver"
android:exported="false" />
</application>
</manifest>
================================================================================================================
MainActivity.java:
package com.sunmeat.facebook;
import android.app.AlertDialog;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentActivity;
import com.facebook.AccessToken;
import com.facebook.CallbackManager;
import com.facebook.FacebookAuthorizationException;
import com.facebook.FacebookCallback;
import com.facebook.FacebookException;
import com.facebook.Profile;
import com.facebook.ProfileTracker;
import com.facebook.login.LoginManager;
import com.facebook.login.LoginResult;
import com.facebook.login.widget.ProfilePictureView;
import com.facebook.share.ShareApi;
import com.facebook.share.Sharer;
import com.facebook.share.model.ShareLinkContent;
import com.facebook.share.model.SharePhoto;
import com.facebook.share.model.SharePhotoContent;
import com.facebook.share.widget.ShareDialog;
public class MainActivity extends FragmentActivity {
private final String PENDING_ACTION_BUNDLE_KEY = "com.sunmeat.facebook:PendingAction";
private Button postStatusUpdateButton;
private Button postPhotoButton;
private ProfilePictureView profilePictureView;
private TextView greeting;
private PendingAction pendingAction = PendingAction.NONE;
private boolean canPresentShareDialog;
private boolean canPresentShareDialogWithPhotos;
private CallbackManager callbackManager;
private ProfileTracker profileTracker;
private ShareDialog shareDialog;
private final FacebookCallback<Sharer.Result> shareCallback =
new FacebookCallback<Sharer.Result>() {
@Override
public void onCancel() {
Log.d("HelloFacebook", "Отменено");
}
@Override
public void onError(@NonNull FacebookException error) {
Log.d("HelloFacebook", String.format("Ошибка: %s", error.toString()));
String title = getString(R.string.error);
String alertMessage = error.getMessage();
showResult(title, alertMessage);
}
@Override
public void onSuccess(@NonNull Sharer.Result result) {
Log.d("HelloFacebook", "Всё ок!");
if (result.getPostId() != null) {
String title = getString(R.string.success);
String id = result.getPostId();
String alertMessage = getString(R.string.successfully_posted_post, id);
showResult(title, alertMessage);
}
}
private void showResult(String title, String alertMessage) {
new AlertDialog.Builder(MainActivity.this)
.setTitle(title)
.setMessage(alertMessage)
.setPositiveButton(R.string.ok, null)
.show();
}
};
private enum PendingAction {
NONE,
POST_PHOTO,
POST_STATUS_UPDATE
}
private static final int PICK_IMAGE_REQUEST = 1;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
callbackManager = CallbackManager.Factory.create();
LoginManager.getInstance()
.registerCallback(
callbackManager,
new FacebookCallback<LoginResult>() {
@Override
public void onSuccess(@NonNull LoginResult loginResult) {
handlePendingAction();
updateUI();
}
@Override
public void onCancel() {
if (pendingAction != PendingAction.NONE) {
showAlert();
pendingAction = PendingAction.NONE;
}
updateUI();
}
@Override
public void onError(@NonNull FacebookException exception) {
if (pendingAction != PendingAction.NONE
&& exception instanceof FacebookAuthorizationException) {
showAlert();
pendingAction = PendingAction.NONE;
}
updateUI();
}
private void showAlert() {
new AlertDialog.Builder(MainActivity.this)
.setTitle(R.string.cancelled)
.setMessage(R.string.permission_not_granted)
.setPositiveButton(R.string.ok, null)
.show();
}
});
shareDialog = new ShareDialog(this);
shareDialog.registerCallback(callbackManager, shareCallback);
if (savedInstanceState != null) {
String name = savedInstanceState.getString(PENDING_ACTION_BUNDLE_KEY);
pendingAction = PendingAction.valueOf(name);
}
setContentView(R.layout.activity_main);
profileTracker = new ProfileTracker() {
@Override
protected void onCurrentProfileChanged(Profile oldProfile, Profile currentProfile) {
updateUI();
handlePendingAction();
}
};
profilePictureView = findViewById(R.id.profilePicture);
greeting = findViewById(R.id.greeting);
postStatusUpdateButton = findViewById(R.id.postStatusUpdateButton);
postStatusUpdateButton.setOnClickListener(
view -> onClickPostStatusUpdate());
postPhotoButton = findViewById(R.id.postPhotoButton);
postPhotoButton.setOnClickListener(
view -> onClickPostPhoto());
canPresentShareDialog = ShareDialog.canShow(ShareLinkContent.class);
canPresentShareDialogWithPhotos = ShareDialog.canShow(SharePhotoContent.class);
}
@Override
protected void onResume() {
super.onResume();
updateUI();
}
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(PENDING_ACTION_BUNDLE_KEY, pendingAction.name());
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
callbackManager.onActivityResult(requestCode, resultCode, data);
// обработка результатов выбора изображения
if (requestCode == PICK_IMAGE_REQUEST && resultCode == RESULT_OK && data != null && data.getData() != null) {
Uri selectedImageUri = data.getData();
postSelectedPhoto(selectedImageUri);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
profileTracker.stopTracking();
LoginManager.getInstance().unregisterCallback(callbackManager);
}
private void updateUI() {
boolean enableButtons = AccessToken.isCurrentAccessTokenActive();
postStatusUpdateButton.setEnabled(enableButtons || canPresentShareDialog);
postPhotoButton.setEnabled(enableButtons || canPresentShareDialogWithPhotos);
Profile profile = Profile.getCurrentProfile();
if (enableButtons && profile != null) {
profilePictureView.setProfileId(profile.getId());
greeting.setText(getString(R.string.hello_user, profile.getFirstName()));
} else {
profilePictureView.setProfileId(null);
greeting.setText(null);
}
}
private void handlePendingAction() {
PendingAction previouslyPendingAction = pendingAction;
pendingAction = PendingAction.NONE;
switch (previouslyPendingAction) {
case NONE:
break;
case POST_PHOTO:
postPhoto();
break;
case POST_STATUS_UPDATE:
postStatusUpdate();
break;
}
}
private void onClickPostStatusUpdate() {
performPublish(canPresentShareDialog);
}
private void postStatusUpdate() {
Profile profile = Profile.getCurrentProfile();
ShareLinkContent linkContent =
new ShareLinkContent.Builder()
.setContentUrl(Uri.parse("https://www.google.com/"))
.build();
if (canPresentShareDialog) {
shareDialog.show(linkContent);
} else if (profile != null && hasPublishPermission()) {
ShareApi.share(linkContent, shareCallback);
} else {
pendingAction = PendingAction.POST_STATUS_UPDATE;
}
}
private void onClickPostPhoto() {
// интент для выбора изображения из галереи
Intent intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(intent, PICK_IMAGE_REQUEST);
}
private void postPhoto() {
Intent photoPickerIntent = new Intent(Intent.ACTION_PICK);
photoPickerIntent.setType("image/*");
startActivityForResult(photoPickerIntent, PICK_IMAGE_REQUEST);
}
private void postSelectedPhoto(Uri selectedImageUri) {
SharePhoto sharePhoto = new SharePhoto.Builder()
.setImageUrl(selectedImageUri)
.setCaption("Your photo caption")
.build();
SharePhotoContent sharePhotoContent = new SharePhotoContent.Builder()
.addPhoto(sharePhoto)
.build();
ShareDialog.show(this, sharePhotoContent);
}
private boolean hasPublishPermission() {
return AccessToken.isCurrentAccessTokenActive()
&& AccessToken.getCurrentAccessToken().getPermissions().contains("publish_actions");
}
private void performPublish(boolean allowNoToken) {
if (AccessToken.isCurrentAccessTokenActive() || allowNoToken) {
pendingAction = PendingAction.POST_STATUS_UPDATE;
handlePendingAction();
}
}
private void showResult(String title, String alertMessage) {
new AlertDialog.Builder(MainActivity.this)
.setTitle(title)
.setMessage(alertMessage)
.setPositiveButton(R.string.ok, null)
.show();
}
}
================================================================================================================
HelloFacebookBroadcastReceiver.kt:
package com.sunmeat.facebook
import android.os.Bundle
import android.util.Log
import com.facebook.FacebookBroadcastReceiver
// пример показывает, как можно поставить обработчик длительных операций вроде загрузки фото
class HelloFacebookBroadcastReceiver: FacebookBroadcastReceiver() {
override fun onSuccessfulAppCall(appCallId: String, action: String, extras: Bundle) {
Log.d("HelloFacebook", "Фото по запросу # $appCallId загружено.")
}
override fun onFailedAppCall(appCallId: String, action: String, extras: Bundle) {
Log.d("HelloFacebook", "Фото по запросу # $appCallId НЕ загружено.")
}
}
================================================================================================================
res \ drawable: нужна картинка icon.png
================================================================================================================
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:facebook="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFF"
android:orientation="vertical"
android:paddingLeft="16dp"
android:paddingTop="16dp"
android:paddingRight="16dp"
android:paddingBottom="16dp">
<LinearLayout
android:id="@+id/main_ui_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFF"
android:orientation="vertical">
<com.facebook.login.widget.LoginButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="5dp"
facebook:com_facebook_confirm_logout="false"
facebook:com_facebook_tooltip_mode="never_display" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:id="@+id/greeting"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="10dp"
android:textColor="#333"
android:textSize="18sp" />
<com.facebook.login.widget.ProfilePictureView
android:id="@+id/profilePicture"
android:layout_width="match_parent"
android:layout_height="300dp"
android:layout_marginBottom="10dp"
android:gravity="center_horizontal"
facebook:com_facebook_preset_size="normal" />
<Button
android:id="@+id/postStatusUpdateButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/button1_text" />
<Button
android:id="@+id/postPhotoButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/button2_text" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
================================================================================================================
res \ values \ strings.xml:
<resources>
<string name="app_name">Интеграция с Facebook</string>
<string name="app_id">249524008095035</string>
<string name="facebook_app_id">249524008095035</string>
<string name="fb_login_protocol_scheme">fb249524008095035</string>
<string name="facebook_client_token">eaa3b9554598c8ee8969821c52769535</string>
<string name="facebook_app_name">WallPosting</string>
<string name="cancelled">Отменено</string>
<string name="permission_not_granted">Невозможно выполнить выбранное действие, поскольку разрешения не были предоставлены.</string>
<string name="ok">OK</string>
<string name="hello_user">Привет, %1$s!</string>
<string name="success">Всё ОК</string>
<string name="successfully_posted_post">Айди поста: %1$s</string>
<string name="error">Упс...</string>
<string name="status_update">Обновление статуса для %1$s в %2$s</string>
<string name="photo_post">Фото пост</string>
<string name="you_picked">Вы выбрали:</string>
<string name="no_friends_selected">&lt;Не выбраны друзья&gt;</string>
<string name="no_place_selected">&lt;Не выбрано место&gt;</string>
<string name="pick_seattle_place">Выберите место</string>
<string name="exception">Исключение: %1$s</string>
<string name="successfully_posted_photo">Ок</string>
<string name="button2_text">Выложить фото на стену</string>
<string name="button1_text">Выложить текст на стену</string>
</resources>
================================================================================================================
res \ values \ themes.xml:
<resources>
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
</resources>
================================================================================================================
build.gradle.kts (Module:app):
plugins {
id("com.android.application")
}
android {
namespace = "com.sunmeat.facebook"
compileSdk = 34
defaultConfig {
applicationId = "com.sunmeat.facebook"
minSdk = 26
targetSdk = 34
versionCode = 1
versionName = "1.0"
}
...
}
repositories {
maven { url = uri("https://jitpack.io") }
mavenCentral()
}
dependencies {
...
implementation ("com.facebook.android:facebook-android-sdk:12.3.0") // на момент написания примера, свежайшая версия 16.0.0
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment