使用 ACTION_SEND 在 Android 中发布到 Facebook
Posting to Facebook in Android with ACTION_SEND
据官方统计,Facebook 不完全支持 ACTION_SEND 并且不使用所提供的数据。
他们的政策是程序员应该使用他们的 SDK 在 Facebook 上post。
发布意图时,生成的列表包括 FB。这是一个问题,因为它需要不同格式的参数。
此外,他们似乎无法从弹出的列表中删除 FB,即使他们实际上不支持 ACTION_SEND 正确。
可以使用 ActionChooser 删除它,但这意味着我们必须有一个单独的按钮才能通过 FB 共享。
最好的解决方案是什么?
一个可能的解决方案:
基本思路是 "hijack" Facebook 意图并将其用于我自己的目的。
首先,我定义一个activity。 activity 包含通过 FB SDK post 消息的代码。
package il.co.anykey.apps.yavnehVeChachmaya.activities;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import com.facebook.CallbackManager;
import com.facebook.FacebookCallback;
import com.facebook.FacebookException;
import com.facebook.FacebookSdk;
import com.facebook.share.Sharer;
import com.facebook.share.model.ShareLinkContent;
import com.facebook.share.widget.ShareDialog;
/**
* Created by jonathan.b on 22/07/2015.
*/
public class PostToFBActivity extends Activity {
public static final String EXTRA_CONTENT_URL = "content_url";
public static final String EXTRA_IMAGE_URL = "image_url";
private CallbackManager _fbCallbackManager;
private ShareDialog _fbShareDialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String contentTitle = getIntent().getStringExtra(android.content.Intent.EXTRA_SUBJECT);
String contentDescription = getIntent().getStringExtra(Intent.EXTRA_TEXT);
String contentURL = getIntent().getStringExtra(EXTRA_CONTENT_URL);
String imageURL = getIntent().getStringExtra(EXTRA_IMAGE_URL);
try {
FacebookSdk.sdkInitialize(getApplicationContext());
_fbCallbackManager = CallbackManager.Factory.create();
_fbShareDialog = new ShareDialog(this);
// this part is optional
_fbShareDialog.registerCallback(_fbCallbackManager, new FacebookCallback<Sharer.Result>() {
/**
* Called when the dialog completes without error.
* @param result Result from the dialog
*/
@Override
public void onSuccess(Sharer.Result result) {
finish();
}
/**
* Called when the dialog is canceled.
*/
@Override
public void onCancel() {
finish();
}
/**
* Called when the dialog finishes with an error.
*
* @param error The error that occurred
*/
@Override
public void onError(FacebookException error) {
finish();
}
});
if (ShareDialog.canShow(ShareLinkContent.class)) {
ShareLinkContent.Builder builder = new ShareLinkContent.Builder();
if (contentTitle != null)
builder.setContentTitle(contentTitle);
if (contentDescription != null)
builder.setContentDescription(contentDescription);
if (contentURL != null)
builder.setContentUrl(Uri.parse(contentURL));
if (imageURL != null)
builder.setImageUrl(Uri.parse(imageURL));
_fbShareDialog.show(builder.build());
}
}
catch(Exception ex){
ex.printStackTrace();
}
}
@Override
protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
super.onActivityResult(requestCode, resultCode, data);
_fbCallbackManager.onActivityResult(requestCode, resultCode, data);
}
}
可以看出,activity 从标准 ACTION_SEND extras 和几个 "extra extras" 接收数据。
然后它调用 FB SDK 来 post 它。
activity 没有布局并在 post 完成后退出。
下一步是将 activity 添加到清单中:
<activity android:name=".activities.PostToFBActivity" android:noHistory="true" android:screenOrientation="portrait"
android:icon="@drawable/facebook_icon" android:label="Facebook" android:exported="false">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND_MULTIPLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>
</activity>
现在 activity 将能够响应 ACTION_SEND 意图。为了让这个意图只能从这个应用程序中访问,我们添加了 attribute android:exported="false"
。
此外,我们再添加两个属性:
android:icon="@drawable/facebook_icon" android:label="Facebook"
这些定义了应用程序选择器弹出时将显示的图标和文本。
(@drawable/facebook_icon是复制FB图标)。
完整的意义将在下面解释。
下一阶段是创建一个排除 Facebook 包(如果已安装)的 IntentChooser。
private Intent generateCustomChooserIntent(Intent prototype, String[] forbiddenChoices) {
List<Intent> targetedShareIntents = new ArrayList<Intent>();
List<HashMap<String, String>> intentMetaInfo = new ArrayList<HashMap<String, String>>();
Intent chooserIntent;
Intent dummy = new Intent(prototype.getAction());
dummy.setType(prototype.getType());
List<ResolveInfo> resInfo = getPackageManager().queryIntentActivities(dummy, 0);
if (!resInfo.isEmpty()) {
for (ResolveInfo resolveInfo : resInfo) {
if (resolveInfo.activityInfo == null || Arrays.asList(forbiddenChoices).contains(resolveInfo.activityInfo.packageName))
continue;
HashMap<String, String> info = new HashMap<String, String>();
info.put("packageName", resolveInfo.activityInfo.packageName);
info.put("className", resolveInfo.activityInfo.name);
info.put("simpleName", String.valueOf(resolveInfo.activityInfo.loadLabel(getPackageManager())));
intentMetaInfo.add(info);
}
if (!intentMetaInfo.isEmpty()) {
// sorting for nice readability
Collections.sort(intentMetaInfo, new Comparator<HashMap<String, String>>() {
@Override
public int compare(HashMap<String, String> map, HashMap<String, String> map2) {
return map.get("simpleName").compareTo(map2.get("simpleName"));
}
});
// create the custom intent list
for (HashMap<String, String> metaInfo : intentMetaInfo) {
Intent targetedShareIntent = (Intent) prototype.clone();
targetedShareIntent.setPackage(metaInfo.get("packageName"));
targetedShareIntent.setClassName(metaInfo.get("packageName"), metaInfo.get("className"));
targetedShareIntents.add(targetedShareIntent);
}
chooserIntent = Intent.createChooser(targetedShareIntents.remove(targetedShareIntents.size() - 1), getString(R.string.share));
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetedShareIntents.toArray(new Parcelable[]{}));
return chooserIntent;
}
}
return Intent.createChooser(prototype, getString(R.string.share));
}
这段代码不是我的而是来自这里:https://gist.github.com/mediavrog/5625602
最后,我使用我添加的额外内容以正常方式调用 ACTION_SEND 意图:
intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(android.content.Intent.EXTRA_SUBJECT, "You Subject");
intent.putExtra(Intent.EXTRA_TEXT, "You Text.");
intent.putExtra(PostToFBActivity.EXTRA_CONTENT_URL,"http://your.site.url");
intent.putExtra(PostToFBActivity.EXTRA_IMAGE_URL,"you.image.url");
String[] blacklist = new String[]{"com.facebook.orca", "com.facebook.katana"};
startActivity(generateCustomChooserIntent(intent, blacklist));
这将弹出一个意图,选择 Facebook 条目的显示位置。
然而,这并不是真正的FB意图。相反,它是我们清单中添加的那个。
由于我们已经设置了FB的图标和标签,所以用户看起来好像是在调用FB,而实际上他们是在使用PostToFBActivity。
我已经测试过了,确实有效。
有趣的是,它甚至可以在没有安装 FB 的设备上运行。
我只用它来 post 带有 link 图片的消息。
应该可以添加允许 post 从设备获取图像的代码。
据官方统计,Facebook 不完全支持 ACTION_SEND 并且不使用所提供的数据。 他们的政策是程序员应该使用他们的 SDK 在 Facebook 上post。
发布意图时,生成的列表包括 FB。这是一个问题,因为它需要不同格式的参数。 此外,他们似乎无法从弹出的列表中删除 FB,即使他们实际上不支持 ACTION_SEND 正确。
可以使用 ActionChooser 删除它,但这意味着我们必须有一个单独的按钮才能通过 FB 共享。
最好的解决方案是什么?
一个可能的解决方案:
基本思路是 "hijack" Facebook 意图并将其用于我自己的目的。
首先,我定义一个activity。 activity 包含通过 FB SDK post 消息的代码。
package il.co.anykey.apps.yavnehVeChachmaya.activities;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import com.facebook.CallbackManager;
import com.facebook.FacebookCallback;
import com.facebook.FacebookException;
import com.facebook.FacebookSdk;
import com.facebook.share.Sharer;
import com.facebook.share.model.ShareLinkContent;
import com.facebook.share.widget.ShareDialog;
/**
* Created by jonathan.b on 22/07/2015.
*/
public class PostToFBActivity extends Activity {
public static final String EXTRA_CONTENT_URL = "content_url";
public static final String EXTRA_IMAGE_URL = "image_url";
private CallbackManager _fbCallbackManager;
private ShareDialog _fbShareDialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String contentTitle = getIntent().getStringExtra(android.content.Intent.EXTRA_SUBJECT);
String contentDescription = getIntent().getStringExtra(Intent.EXTRA_TEXT);
String contentURL = getIntent().getStringExtra(EXTRA_CONTENT_URL);
String imageURL = getIntent().getStringExtra(EXTRA_IMAGE_URL);
try {
FacebookSdk.sdkInitialize(getApplicationContext());
_fbCallbackManager = CallbackManager.Factory.create();
_fbShareDialog = new ShareDialog(this);
// this part is optional
_fbShareDialog.registerCallback(_fbCallbackManager, new FacebookCallback<Sharer.Result>() {
/**
* Called when the dialog completes without error.
* @param result Result from the dialog
*/
@Override
public void onSuccess(Sharer.Result result) {
finish();
}
/**
* Called when the dialog is canceled.
*/
@Override
public void onCancel() {
finish();
}
/**
* Called when the dialog finishes with an error.
*
* @param error The error that occurred
*/
@Override
public void onError(FacebookException error) {
finish();
}
});
if (ShareDialog.canShow(ShareLinkContent.class)) {
ShareLinkContent.Builder builder = new ShareLinkContent.Builder();
if (contentTitle != null)
builder.setContentTitle(contentTitle);
if (contentDescription != null)
builder.setContentDescription(contentDescription);
if (contentURL != null)
builder.setContentUrl(Uri.parse(contentURL));
if (imageURL != null)
builder.setImageUrl(Uri.parse(imageURL));
_fbShareDialog.show(builder.build());
}
}
catch(Exception ex){
ex.printStackTrace();
}
}
@Override
protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
super.onActivityResult(requestCode, resultCode, data);
_fbCallbackManager.onActivityResult(requestCode, resultCode, data);
}
}
可以看出,activity 从标准 ACTION_SEND extras 和几个 "extra extras" 接收数据。 然后它调用 FB SDK 来 post 它。 activity 没有布局并在 post 完成后退出。
下一步是将 activity 添加到清单中:
<activity android:name=".activities.PostToFBActivity" android:noHistory="true" android:screenOrientation="portrait"
android:icon="@drawable/facebook_icon" android:label="Facebook" android:exported="false">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND_MULTIPLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>
</activity>
现在 activity 将能够响应 ACTION_SEND 意图。为了让这个意图只能从这个应用程序中访问,我们添加了 attribute android:exported="false"
。
此外,我们再添加两个属性:
android:icon="@drawable/facebook_icon" android:label="Facebook"
这些定义了应用程序选择器弹出时将显示的图标和文本。 (@drawable/facebook_icon是复制FB图标)。 完整的意义将在下面解释。
下一阶段是创建一个排除 Facebook 包(如果已安装)的 IntentChooser。
private Intent generateCustomChooserIntent(Intent prototype, String[] forbiddenChoices) {
List<Intent> targetedShareIntents = new ArrayList<Intent>();
List<HashMap<String, String>> intentMetaInfo = new ArrayList<HashMap<String, String>>();
Intent chooserIntent;
Intent dummy = new Intent(prototype.getAction());
dummy.setType(prototype.getType());
List<ResolveInfo> resInfo = getPackageManager().queryIntentActivities(dummy, 0);
if (!resInfo.isEmpty()) {
for (ResolveInfo resolveInfo : resInfo) {
if (resolveInfo.activityInfo == null || Arrays.asList(forbiddenChoices).contains(resolveInfo.activityInfo.packageName))
continue;
HashMap<String, String> info = new HashMap<String, String>();
info.put("packageName", resolveInfo.activityInfo.packageName);
info.put("className", resolveInfo.activityInfo.name);
info.put("simpleName", String.valueOf(resolveInfo.activityInfo.loadLabel(getPackageManager())));
intentMetaInfo.add(info);
}
if (!intentMetaInfo.isEmpty()) {
// sorting for nice readability
Collections.sort(intentMetaInfo, new Comparator<HashMap<String, String>>() {
@Override
public int compare(HashMap<String, String> map, HashMap<String, String> map2) {
return map.get("simpleName").compareTo(map2.get("simpleName"));
}
});
// create the custom intent list
for (HashMap<String, String> metaInfo : intentMetaInfo) {
Intent targetedShareIntent = (Intent) prototype.clone();
targetedShareIntent.setPackage(metaInfo.get("packageName"));
targetedShareIntent.setClassName(metaInfo.get("packageName"), metaInfo.get("className"));
targetedShareIntents.add(targetedShareIntent);
}
chooserIntent = Intent.createChooser(targetedShareIntents.remove(targetedShareIntents.size() - 1), getString(R.string.share));
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetedShareIntents.toArray(new Parcelable[]{}));
return chooserIntent;
}
}
return Intent.createChooser(prototype, getString(R.string.share));
}
这段代码不是我的而是来自这里:https://gist.github.com/mediavrog/5625602
最后,我使用我添加的额外内容以正常方式调用 ACTION_SEND 意图:
intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(android.content.Intent.EXTRA_SUBJECT, "You Subject");
intent.putExtra(Intent.EXTRA_TEXT, "You Text.");
intent.putExtra(PostToFBActivity.EXTRA_CONTENT_URL,"http://your.site.url");
intent.putExtra(PostToFBActivity.EXTRA_IMAGE_URL,"you.image.url");
String[] blacklist = new String[]{"com.facebook.orca", "com.facebook.katana"};
startActivity(generateCustomChooserIntent(intent, blacklist));
这将弹出一个意图,选择 Facebook 条目的显示位置。
然而,这并不是真正的FB意图。相反,它是我们清单中添加的那个。 由于我们已经设置了FB的图标和标签,所以用户看起来好像是在调用FB,而实际上他们是在使用PostToFBActivity。
我已经测试过了,确实有效。 有趣的是,它甚至可以在没有安装 FB 的设备上运行。
我只用它来 post 带有 link 图片的消息。 应该可以添加允许 post 从设备获取图像的代码。