Search code examples
androidfacebookdeep-linkingfacebook-sharedeeplink

Post video to Facebook from Android with deep link back to app


Problem: I'm trying to share content to Facebook from my Android app that is hosted somewhere else and have it deeplink back to my app.

Details I'm developing and Android app and the problem I'm facing is I have a video that I want to share to the user's newsfeed with a deeplink back in to the app. The video is hosted on Cloudinary, but that is irrelevant, trying to post a Youtube video to a users newsfeed but have it deeplink back to the app when they click on the description below would be the same problem.

I've come really close using Facebook's ShareLinkContent and a ShareDialog. The code looks like this:

ShareLinkContent content = new ShareLinkContent.Builder()
                        .setContentUrl(Uri.parse("https://kt4rh.app.goo.gl/naxy")) /* Deeplink back into my app created using Firebase Dynamic Link */
                        .setImageUrl(Uri.parse({path to an image stored on clodinary}))
                        .build();
                ShareDialog shareDialog = new ShareDialog(getActivity());
                shareDialog.show(content);

This shows an image, with a deeplink back to my app when the user taps on it. However, what I want to do is show a video (or a gif, either would be fine) instead of the image.

I've tried downloading the video to the device and using a ShareVideoContent, hoping that I could set the localUrl and the contentUrl (similar to how I set the thumbnail and contentUrl in the ShareLinkContent above) like this:

Uri videoUri = Uri.fromFile(new File(localPathToVideo));
                ShareVideo shareVideo = new ShareVideo.Builder()
                        .setLocalUrl(videoUri)
                        .build();
                ShareVideoContent videoContent  = new ShareVideoContent.Builder()
                        .setVideo(shareVideo)
                        .setContentUrl(Uri.parse("https://kt4rh.app.goo.gl/naxy")) /* Deeplink back to app */
                        .build();
                ShareDialog shareDialog = new ShareDialog(getActivity());
                callbackManager = CallbackManager.Factory.create();
                shareDialog.registerCallback(callbackManager, new FacebookCallback<Sharer.Result>() {
                    @Override
                    public void onSuccess(Sharer.Result result) {}

                    @Override
                    public void onCancel() {}

                    @Override
                    public void onError(FacebookException error) {
                        Log.e(LOG_TAG, error.getMessage());
                    }
                });
                if (shareDialog.canShow(videoContent)) {
                    shareDialog.show(videoContent);
                }

But I just get an error back saying "Only one of link, photos, and video should be specified.", meaning I can't set the local url and content url.

So now I'm stuck. Seems like this should be a relatively common issue (to share content hosted somewhere else and have it deeplink back to your app), but I can't figure out how to do it.


Solution

  • Ended up solving this by creating an html page for every item to be shared (in this case trip itineraries) and hosting them on Firebase's Cloud Storage. Here's what the code looks like

    /**
     * In order to share gifs to facebook and have a animating gif show up, we need to construct an arbitrary HTML page that wraps the
     * gif image that we want to share. When the user clicks on the wrapped html page, it will forward to the real page we want it to show.
     *
     * Facebook uses custom meta properties to determine what image to show in the preview, which is why we need to wrap the actual gif with another image this way
     */
    private static final String GIF_IMAGE = "<gif_image_here>";
    private static final String REDIRECT_LINK = "<redirect_link_here>";
    
    private static final String SHARE_HTML_PAGE = "<!DOCTYPE html><html>" +
            "<head>" +
            "<meta property=\"og:type\" content=\"video.other\" />" +
            "<meta property=\"og:url\" content=\"" + GIF_IMAGE + "\" />" +
            "<meta property=\"og:image\" content=\"" + GIF_IMAGE + "\" />" +
            "<meta http-equiv=\"refresh\" content=\"0; url=" + REDIRECT_LINK + "\" />" +
            "</head></html>";
    
    public static void shareGifToFacebook(final Activity activity, final String gifUrl, final Trip trip, final ShareStatusListener shareStatusListener) {
        String htmlPageToShare = SHARE_HTML_PAGE.replaceAll(GIF_IMAGE, gifUrl).replaceAll(REDIRECT_LINK, DeeplinkUtils.getTripDeeplink(trip.getUid()));
    
        // Upload the html page somewhere accessible
        FirebaseStorageUploader.uploadShareHtml(TravellerManager.getInstance().getCurrentlyLoggedInUserUid(), htmlPageToShare, new FirebaseStorageUploader.UploadShareHtmlListener() {
            @Override
            public void onSuccess(String publicUrl) {
                Log.d("ShareUtils", "shareGifToFacebook() publicUrl to be shortened:  " + publicUrl);
    
                URLShortener.shortUrl(publicUrl, new URLShortener.LoadingCallback() {
                    @Override
                    public void startedLoading() {
    
                    }
    
                    @Override
                    public void finishedLoading(@Nullable String shortUrl) {
                        Log.d("ShareUtils", "shareGifToFacebook() finishedLoading() shortenedUrl " + shortUrl);
    
                        ShareLinkContent content = new ShareLinkContent.Builder()
                                .setContentUrl(Uri.parse(shortUrl))
                                .setContentDescription(activity.getString(R.string.view_trip_on_wd))
                                .setContentTitle(trip.getName())
                                .build();
                        ShareDialog shareDialog = new ShareDialog(activity);
                        shareDialog.show(content);
    
                        if (shareStatusListener != null) {
                            shareStatusListener.onShared();
                        }
                    }
                });
            }
    
            @Override
            public void onFailed() {
                Toaster.toastLong(activity.getString(R.string.general_error));
            }
        });
    }