使用 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 从设备获取图像的代码。