Created
February 1, 2024 09:27
-
-
Save sunmeat/1b1cde13ab4b9fb0fba753072d7a734b to your computer and use it in GitHub Desktop.
android + facebook integration example (login + post text + post photo)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"><Не выбраны друзья></string> | |
<string name="no_place_selected"><Не выбрано место></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