Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
14792 manas 1
/**
2
 * Copyright 2010-present Facebook.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *    http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
 
17
package com.facebook.widget;
18
 
19
import android.app.Activity;
20
import android.content.Context;
21
import android.content.Intent;
22
import android.graphics.Bitmap;
23
import android.net.Uri;
24
import android.os.Bundle;
25
import android.os.Parcel;
26
import android.os.Parcelable;
27
import android.support.v4.app.Fragment;
28
import com.facebook.*;
29
import com.facebook.internal.*;
30
import com.facebook.model.GraphObject;
31
import com.facebook.model.GraphObjectList;
32
import com.facebook.model.OpenGraphAction;
33
import com.facebook.model.OpenGraphObject;
34
import org.json.JSONArray;
35
import org.json.JSONException;
36
import org.json.JSONObject;
37
 
38
import java.io.File;
39
import java.util.*;
40
 
41
/*
42
 * Provides an interface for presenting dialogs provided by the Facebook application for Android. This class
43
 * provides builders that present a strongly-typed interface to generate properly-formed Intents for launching
44
 * the appropriate Activities provided by the Facebook application.
45
 */
46
public class FacebookDialog {
47
 
48
    public static final String COMPLETION_GESTURE_CANCEL = "cancel";
49
 
50
    private static final String EXTRA_DIALOG_COMPLETE_KEY = "com.facebook.platform.extra.DID_COMPLETE";
51
    private static final String EXTRA_DIALOG_COMPLETION_GESTURE_KEY =
52
            "com.facebook.platform.extra.COMPLETION_GESTURE";
53
    private static final String EXTRA_DIALOG_COMPLETION_ID_KEY = "com.facebook.platform.extra.POST_ID";
54
 
55
    public static final String RESULT_ARGS_DIALOG_COMPLETE_KEY = "didComplete";
56
    public static final String RESULT_ARGS_DIALOG_COMPLETION_GESTURE_KEY = "completionGesture";
57
    public static final String RESULT_ARGS_DIALOG_COMPLETION_ID_KEY = "postId";
58
 
59
    private static NativeAppCallAttachmentStore attachmentStore;
60
 
61
    /**
62
     * Defines a callback interface that will be called when the user completes interacting with a Facebook
63
     * dialog, or if an error occurs.
64
     */
65
    public interface Callback {
66
        /**
67
         * Called when the user completes interacting with a Facebook dialog.
68
         *
69
         * @param pendingCall a PendingCall containing the call ID and original Intent used to launch the dialog
70
         * @param data        a Bundle containing the results of the dialog, whose contents will vary depending on the
71
         *                    type of dialog being displayed.
72
         */
73
        void onComplete(PendingCall pendingCall, Bundle data);
74
 
75
        /**
76
         * Called if an error occurred while presenting a Facebook dialog.
77
         *
78
         * @param pendingCall a PendingCall containing the call ID and original Intent used to launch the dialog
79
         * @param error       the error that occurred
80
         * @param data        the full set of extras associated with the activity result
81
         */
82
        void onError(PendingCall pendingCall, Exception error, Bundle data);
83
    }
84
 
85
    /**
86
     * Provides an interface for describing a specific feature provided by a FacebookDialog.
87
     * This is public primarily to allow its use elsewhere in the Android SDK; developers are discouraged from
88
     * constructing their own DialogFeature implementations internal API may change.
89
     */
90
    public interface DialogFeature {
91
        /**
92
         * This method is for internal use only.
93
         */
94
        String getAction();
95
 
96
        /**
97
         * This method is for internal use only.
98
         */
99
        int getMinVersion();
100
 
101
        /**
102
         * This method is for internal use only.
103
         *
104
         * For all Enums that implement this interface, the name() method is already present. It returns the String
105
         * representation of the Enum value, verbatim.
106
         *
107
         */
108
        String name();
109
    }
110
 
111
    /**
112
     * Defines a set of features that may be supported by the native Share dialog exposed by the Facebook application.
113
     * As additional features are added, these flags may be passed to
114
     * {@link FacebookDialog#canPresentShareDialog(android.content.Context,
115
     * com.facebook.widget.FacebookDialog.ShareDialogFeature...)}
116
     * to determine whether the version of the Facebook application installed on the user's device is recent
117
     * enough to support specific features, which in turn may be used to determine which UI, etc., to present to the
118
     * user.
119
     */
120
    public enum ShareDialogFeature implements DialogFeature {
121
        /**
122
         * Indicates whether the native Share dialog itself is supported by the installed version of the
123
         * Facebook application.
124
         */
125
        SHARE_DIALOG(NativeProtocol.PROTOCOL_VERSION_20130618),
126
        /**
127
         * Indicates whether the native Share dialog supports sharing of photo images.
128
         */
129
        PHOTOS(NativeProtocol.PROTOCOL_VERSION_20140204),
130
        /**
131
         * Indicates whether the native Share dialog supports sharing of videos.
132
         */
133
        VIDEO(NativeProtocol.PROTOCOL_VERSION_20141028),
134
        ;
135
 
136
        private int minVersion;
137
 
138
        private ShareDialogFeature(int minVersion) {
139
            this.minVersion = minVersion;
140
        }
141
 
142
        /**
143
         * This method is for internal use only.
144
         */
145
        public String getAction() {
146
            return NativeProtocol.ACTION_FEED_DIALOG;
147
        }
148
 
149
        /**
150
         * This method is for internal use only.
151
         */
152
        public int getMinVersion() {
153
            return minVersion;
154
        }
155
    }
156
 
157
    /**
158
     * Defines a set of features that may be supported by the native Message dialog exposed by the Facebook Messenger application.
159
     * As additional features are added, these flags may be passed to
160
     * {@link FacebookDialog#canPresentMessageDialog(android.content.Context,
161
     * com.facebook.widget.FacebookDialog.MessageDialogFeature...)}
162
     * to determine whether the version of the Facebook application installed on the user's device is recent
163
     * enough to support specific features, which in turn may be used to determine which UI, etc., to present to the
164
     * user.
165
     */
166
    public enum MessageDialogFeature implements DialogFeature {
167
        /**
168
         * Indicates whether the native Message dialog itself is supported by the installed version of the
169
         * Facebook application.
170
         */
171
        MESSAGE_DIALOG(NativeProtocol.PROTOCOL_VERSION_20140204),
172
        /**
173
         * Indicates whether the native Message dialog supports sharing of photo images.
174
         */
175
        PHOTOS(NativeProtocol.PROTOCOL_VERSION_20140324),
176
        ;
177
 
178
        private int minVersion;
179
 
180
        private MessageDialogFeature(int minVersion) {
181
            this.minVersion = minVersion;
182
        }
183
 
184
        /**
185
         * This method is for internal use only.
186
         */
187
        public String getAction() {
188
            return NativeProtocol.ACTION_MESSAGE_DIALOG;
189
        }
190
 
191
        /**
192
         * This method is for internal use only.
193
         */
194
        public int getMinVersion() {
195
            return minVersion;
196
        }
197
    }
198
 
199
 
200
    /**
201
     * Defines a set of features that may be supported by the native Open Graph dialogs exposed by the Facebook
202
     * application. As additional features are added, these flags may be passed to
203
     * {@link FacebookDialog#canPresentOpenGraphActionDialog(android.content.Context,
204
     * com.facebook.widget.FacebookDialog.OpenGraphActionDialogFeature...)}
205
     * to determine whether the version of the Facebook application installed on the user's device is recent
206
     * enough to support specific features, which in turn may be used to determine which UI, etc., to present to the
207
     * user.
208
     */
209
    public enum OpenGraphActionDialogFeature implements DialogFeature {
210
        /**
211
         * Indicates whether the native Open Graph action dialog itself is supported by the installed version of the
212
         * Facebook application.
213
         */
214
        OG_ACTION_DIALOG(NativeProtocol.PROTOCOL_VERSION_20130618);
215
 
216
        private int minVersion;
217
 
218
        private OpenGraphActionDialogFeature(int minVersion) {
219
            this.minVersion = minVersion;
220
        }
221
 
222
        /**
223
         * This method is for internal use only.
224
         */
225
        public String getAction() {
226
            return NativeProtocol.ACTION_OGACTIONPUBLISH_DIALOG;
227
        }
228
 
229
        /**
230
         * This method is for internal use only.
231
         */
232
        public int getMinVersion() {
233
            return minVersion;
234
        }
235
    }
236
 
237
    /**
238
     * Defines a set of features that may be supported by the native Open Graph Message dialogs exposed by the Facebook
239
     * application. As additional features are added, these flags may be passed to
240
     * {@link FacebookDialog#canPresentOpenGraphMessageDialog(android.content.Context,
241
     * com.facebook.widget.FacebookDialog.OpenGraphMessageDialogFeature...)}
242
     * to determine whether the version of the Facebook application installed on the user's device is recent
243
     * enough to support specific features, which in turn may be used to determine which UI, etc., to present to the
244
     * user.
245
     */
246
    public enum OpenGraphMessageDialogFeature implements DialogFeature {
247
        /**
248
         * Indicates whether the native Open Graph Message dialog itself is supported by the installed version of the
249
         * Messenger application.
250
         */
251
        OG_MESSAGE_DIALOG(NativeProtocol.PROTOCOL_VERSION_20140204);
252
 
253
        private int minVersion;
254
 
255
        private OpenGraphMessageDialogFeature(int minVersion) {
256
            this.minVersion = minVersion;
257
        }
258
 
259
        /**
260
         * This method is for internal use only.
261
         */
262
        public String getAction() {
263
            return NativeProtocol.ACTION_OGMESSAGEPUBLISH_DIALOG;
264
        }
265
 
266
        /**
267
         * This method is for internal use only.
268
         */
269
        public int getMinVersion() {
270
            return minVersion;
271
        }
272
    }
273
 
274
    interface OnPresentCallback {
275
        void onPresent(Context context) throws Exception;
276
    }
277
 
278
    /**
279
     * Determines whether the native dialog completed normally (without error or exception).
280
     *
281
     * @param result the bundle passed back to onActivityResult
282
     * @return true if the native dialog completed normally
283
     */
284
    public static boolean getNativeDialogDidComplete(Bundle result) {
285
        if (result.containsKey(RESULT_ARGS_DIALOG_COMPLETE_KEY)) {
286
            return result.getBoolean(RESULT_ARGS_DIALOG_COMPLETE_KEY);
287
        }
288
        return result.getBoolean(EXTRA_DIALOG_COMPLETE_KEY, false);
289
    }
290
 
291
    /**
292
     * Returns the gesture with which the user completed the native dialog. This is only returned if the
293
     * user has previously authorized the calling app with basic permissions.
294
     *
295
     * @param result the bundle passed back to onActivityResult
296
     * @return "post" or "cancel" as the completion gesture
297
     */
298
    public static String getNativeDialogCompletionGesture(Bundle result) {
299
        if (result.containsKey(RESULT_ARGS_DIALOG_COMPLETION_GESTURE_KEY)) {
300
            return result.getString(RESULT_ARGS_DIALOG_COMPLETION_GESTURE_KEY);
301
        }
302
        return result.getString(EXTRA_DIALOG_COMPLETION_GESTURE_KEY);
303
    }
304
 
305
    /**
306
     * Returns the id of the published post. This is only returned if the user has previously given the
307
     * app publish permissions.
308
     *
309
     * @param result the bundle passed back to onActivityResult
310
     * @return the id of the published post
311
     */
312
    public static String getNativeDialogPostId(Bundle result) {
313
        if (result.containsKey(RESULT_ARGS_DIALOG_COMPLETION_ID_KEY)) {
314
            return result.getString(RESULT_ARGS_DIALOG_COMPLETION_ID_KEY);
315
        }
316
        return result.getString(EXTRA_DIALOG_COMPLETION_ID_KEY);
317
    }
318
 
319
    private Activity activity;
320
    private Fragment fragment;
321
    private PendingCall appCall;
322
    private OnPresentCallback onPresentCallback;
323
 
324
    private FacebookDialog(Activity activity, Fragment fragment, PendingCall appCall,
325
            OnPresentCallback onPresentCallback) {
326
        this.activity = activity;
327
        this.fragment = fragment;
328
        this.appCall = appCall;
329
        this.onPresentCallback = onPresentCallback;
330
    }
331
 
332
    /**
333
     * Launches an activity in the Facebook application to present the desired dialog. This method returns a
334
     * PendingCall that contains a unique ID associated with this call to the Facebook application. In general,
335
     * a calling Activity should use UiLifecycleHelper to handle incoming activity results, in order to ensure
336
     * proper processing of the results from this dialog.
337
     *
338
     * @return a PendingCall containing the unique call ID corresponding to this call to the Facebook application
339
     */
340
    public PendingCall present() {
341
        logDialogActivity(activity, fragment, getEventName(appCall.getRequestIntent()),
342
                AnalyticsEvents.PARAMETER_DIALOG_OUTCOME_VALUE_COMPLETED);
343
 
344
        if (onPresentCallback != null) {
345
            try {
346
                onPresentCallback.onPresent(activity);
347
            } catch (Exception e) {
348
                throw new FacebookException(e);
349
            }
350
        }
351
 
352
        if (fragment != null) {
353
            fragment.startActivityForResult(appCall.getRequestIntent(), appCall.getRequestCode());
354
        } else {
355
            activity.startActivityForResult(appCall.getRequestIntent(), appCall.getRequestCode());
356
        }
357
        return appCall;
358
    }
359
 
360
    /**
361
     * Parses the results of a dialog activity and calls the appropriate method on the provided Callback.
362
     *
363
     * @param context     the Context that is handling the activity result
364
     * @param appCall     an PendingCall containing the call ID and original Intent used to launch the dialog
365
     * @param requestCode the request code for the activity result
366
     * @param data        the result Intent
367
     * @param callback    a callback to call after parsing the results
368
     * @return true if the activity result was handled, false if not
369
     */
370
    public static boolean handleActivityResult(Context context, PendingCall appCall, int requestCode, Intent data,
371
            Callback callback) {
372
        if (requestCode != appCall.getRequestCode()) {
373
            return false;
374
        }
375
 
376
        if (attachmentStore != null) {
377
            attachmentStore.cleanupAttachmentsForCall(context, appCall.getCallId());
378
        }
379
 
380
        if (callback != null) {
381
            if (NativeProtocol.isErrorResult(data)) {
382
                Bundle errorData = NativeProtocol.getErrorDataFromResultIntent(data);
383
                Exception error = NativeProtocol.getExceptionFromErrorData(errorData);
384
 
385
                callback.onError(appCall, error, errorData);
386
            } else {
387
                Bundle successResults = NativeProtocol.getSuccessResultsFromIntent(data);
388
                callback.onComplete(appCall, successResults);
389
            }
390
        }
391
 
392
        return true;
393
    }
394
 
395
    /**
396
     * Determines whether the version of the Facebook application installed on the user's device is recent
397
     * enough to support specific features of the native Share dialog, which in turn may be used to determine
398
     * which UI, etc., to present to the user.
399
     *
400
     * @param context  the calling Context
401
     * @param features zero or more features to check for; {@link ShareDialogFeature#SHARE_DIALOG} is implicitly checked
402
     *                 if not explicitly specified
403
     * @return true if all of the specified features are supported by the currently installed version of the
404
     *         Facebook application; false if any of the features are not supported
405
     */
406
    public static boolean canPresentShareDialog(Context context, ShareDialogFeature... features) {
407
        return handleCanPresent(context, EnumSet.of(ShareDialogFeature.SHARE_DIALOG, features));
408
    }
409
 
410
    /**
411
     * Determines whether the version of the Facebook application installed on the user's device is recent
412
     * enough to support specific features of the native Message dialog, which in turn may be used to determine
413
     * which UI, etc., to present to the user.
414
     *
415
     * @param context  the calling Context
416
     * @param features zero or more features to check for; {@link com.facebook.widget.FacebookDialog.MessageDialogFeature#MESSAGE_DIALOG} is implicitly
417
     *                 checked if not explicitly specified
418
     * @return true if all of the specified features are supported by the currently installed version of the
419
     *         Facebook application; false if any of the features are not supported
420
     */
421
    public static boolean canPresentMessageDialog(Context context, MessageDialogFeature... features) {
422
        return handleCanPresent(context, EnumSet.of(MessageDialogFeature.MESSAGE_DIALOG, features));
423
    }
424
 
425
    /**
426
     * Determines whether the version of the Facebook application installed on the user's device is recent
427
     * enough to support specific features of the native Open Graph action dialog, which in turn may be used to
428
     * determine which UI, etc., to present to the user.
429
     *
430
     * @param context  the calling Context
431
     * @param features zero or more features to check for; {@link OpenGraphActionDialogFeature#OG_ACTION_DIALOG} is implicitly
432
     *                 checked if not explicitly specified
433
     * @return true if all of the specified features are supported by the currently installed version of the
434
     *         Facebook application; false if any of the features are not supported
435
     */
436
    public static boolean canPresentOpenGraphActionDialog(Context context, OpenGraphActionDialogFeature... features) {
437
        return handleCanPresent(context, EnumSet.of(OpenGraphActionDialogFeature.OG_ACTION_DIALOG, features));
438
    }
439
 
440
    /**
441
     * Determines whether the version of the Facebook application installed on the user's device is recent
442
     * enough to support specific features of the native Open Graph Message dialog, which in turn may be used to
443
     * determine which UI, etc., to present to the user.
444
     *
445
     * @param context  the calling Context
446
     * @param features zero or more features to check for; {@link com.facebook.widget.FacebookDialog.OpenGraphMessageDialogFeature#OG_MESSAGE_DIALOG} is
447
     *                 implicitly checked if not explicitly specified
448
     * @return true if all of the specified features are supported by the currently installed version of the
449
     *         Facebook application; false if any of the features are not supported
450
     */
451
    public static boolean canPresentOpenGraphMessageDialog(Context context, OpenGraphMessageDialogFeature... features) {
452
        return handleCanPresent(context, EnumSet.of(OpenGraphMessageDialogFeature.OG_MESSAGE_DIALOG, features));
453
    }
454
 
455
    private static boolean handleCanPresent(Context context, Iterable<? extends DialogFeature> features) {
456
        String actionName = getActionForFeatures(features);
457
        String applicationId = Settings.getApplicationId();
458
        if (Utility.isNullOrEmpty(applicationId)) {
459
            applicationId = Utility.getMetadataApplicationId(context);
460
        }
461
        return getProtocolVersionForNativeDialog(
462
                context,
463
                actionName,
464
                getVersionSpecForFeatures(applicationId, actionName, features)
465
        ) != NativeProtocol.NO_PROTOCOL_AVAILABLE;
466
    }
467
 
468
    private static int getProtocolVersionForNativeDialog(Context context, String action, int[] versionSpec) {
469
        return NativeProtocol.getLatestAvailableProtocolVersionForAction(context, action, versionSpec);
470
    }
471
 
472
    private static NativeAppCallAttachmentStore getAttachmentStore() {
473
        if (attachmentStore == null) {
474
            attachmentStore = new NativeAppCallAttachmentStore();
475
        }
476
        return attachmentStore;
477
    }
478
 
479
    private static int[] getVersionSpecForFeatures(
480
            String applicationId,
481
            String actionName,
482
            Iterable<? extends DialogFeature> features) {
483
        int[] intersectedRange = null; // Null is treated as a fully open Range. So it is safe to compare against.
484
        for (DialogFeature feature : features) {
485
            int[] featureVersionSpec = getVersionSpecForFeature(applicationId, actionName, feature);
486
            intersectedRange = Utility.intersectRanges(intersectedRange, featureVersionSpec);
487
        }
488
 
489
        return intersectedRange;
490
    }
491
 
492
    private static int[] getVersionSpecForFeature(String applicationId, String actionName, DialogFeature feature) {
493
        // Return the value from DialogFeatureConfig if available. Otherwise, just default to the min-version
494
        Utility.DialogFeatureConfig config = Utility.getDialogFeatureConfig(applicationId, actionName, feature.name());
495
        if (config != null) {
496
            return config.getVersionSpec();
497
        } else {
498
            return new int[]{feature.getMinVersion()};
499
        }
500
    }
501
 
502
    private static String getActionForFeatures(Iterable<? extends DialogFeature> features) {
503
        String action = null;
504
        for (DialogFeature feature : features) {
505
            // All actions in a set of DialogFeatures should have the same action
506
            // So we can break after assigning the first one
507
            action = feature.getAction();
508
            break;
509
        }
510
        return action;
511
    }
512
 
513
    private static void logDialogActivity(Activity activity, Fragment fragment, String eventName, String outcome) {
514
        AppEventsLogger logger = AppEventsLogger.newLogger(fragment != null ? fragment.getActivity() : activity);
515
        Bundle parameters = new Bundle();
516
        parameters.putString(AnalyticsEvents.PARAMETER_DIALOG_OUTCOME, outcome);
517
        logger.logSdkEvent(eventName, null, parameters);
518
    }
519
 
520
    static private String getEventName(Intent intent) {
521
        String action = intent.getStringExtra(NativeProtocol.EXTRA_PROTOCOL_ACTION);
522
        boolean hasPhotos = intent.hasExtra(NativeProtocol.EXTRA_PHOTOS);
523
        boolean hasVideo = false;
524
 
525
        Bundle extras = intent.getBundleExtra(NativeProtocol.EXTRA_PROTOCOL_METHOD_ARGS);
526
        if (extras != null) {
527
            ArrayList<String> photo = extras.getStringArrayList(NativeProtocol.METHOD_ARGS_PHOTOS);
528
            String video = extras.getString(NativeProtocol.METHOD_ARGS_VIDEO);
529
            if (photo != null && !photo.isEmpty()) {
530
                hasPhotos = true;
531
            }
532
            if (video != null && !video.isEmpty()) {
533
                hasVideo = true;
534
            }
535
        }
536
        return getEventName(action, hasPhotos, hasVideo);
537
    }
538
 
539
    static private String getEventName(String action, boolean hasPhotos, boolean hasVideo) {
540
        String eventName;
541
 
542
        if (action.equals(NativeProtocol.ACTION_FEED_DIALOG)) {
543
            eventName = hasVideo ?
544
                    AnalyticsEvents.EVENT_NATIVE_DIALOG_TYPE_VIDEO_SHARE :
545
                    hasPhotos ?
546
                    AnalyticsEvents.EVENT_NATIVE_DIALOG_TYPE_PHOTO_SHARE :
547
                    AnalyticsEvents.EVENT_NATIVE_DIALOG_TYPE_SHARE;
548
        } else if (action.equals(NativeProtocol.ACTION_MESSAGE_DIALOG)) {
549
            eventName = hasPhotos ?
550
                    AnalyticsEvents.EVENT_NATIVE_DIALOG_TYPE_PHOTO_MESSAGE :
551
                    AnalyticsEvents.EVENT_NATIVE_DIALOG_TYPE_MESSAGE;
552
        } else if (action.equals(NativeProtocol.ACTION_OGACTIONPUBLISH_DIALOG)) {
553
            eventName = AnalyticsEvents.EVENT_NATIVE_DIALOG_TYPE_OG_SHARE;
554
        } else if (action.equals(NativeProtocol.ACTION_OGMESSAGEPUBLISH_DIALOG)) {
555
            eventName = AnalyticsEvents.EVENT_NATIVE_DIALOG_TYPE_OG_MESSAGE;
556
        } else if (action.equals(NativeProtocol.ACTION_LIKE_DIALOG)) {
557
            eventName = AnalyticsEvents.EVENT_NATIVE_DIALOG_TYPE_LIKE;
558
        } else {
559
            throw new FacebookException("An unspecified action was presented");
560
        }
561
        return eventName;
562
    }
563
 
564
    /**
565
     * Provides a base class for various FacebookDialog builders. This is public primarily to allow its use elsewhere
566
     * in the Android SDK; developers are discouraged from constructing their own FacebookDialog builders as the
567
     * internal API may change.
568
     *
569
     * @param <CONCRETE> The concrete base class of the builder.
570
     */
571
    public abstract static class Builder<CONCRETE extends Builder<?>> {
572
        final protected Activity activity;
573
        final protected String applicationId;
574
        final protected PendingCall appCall;
575
        protected Fragment fragment;
576
        protected String applicationName;
577
        protected HashMap<String, Bitmap> imageAttachments = new HashMap<String, Bitmap>();
578
        protected HashMap<String, File> mediaAttachmentFiles = new HashMap<String, File>();
579
 
580
        /**
581
         * Constructor.
582
         *
583
         * @param activity the Activity which is presenting the native Share dialog; must not be null
584
         */
585
        public Builder(Activity activity) {
586
            Validate.notNull(activity, "activity");
587
 
588
            this.activity = activity;
589
            applicationId = Utility.getMetadataApplicationId(activity);
590
            appCall = new PendingCall(NativeProtocol.DIALOG_REQUEST_CODE);
591
        }
592
 
593
        /**
594
         * Sets the request code that will be passed to handleActivityResult when this activity completes; the
595
         * default is NativeProtocol.DIALOG_REQUEST_CODE.
596
         *
597
         * @param requestCode the request code
598
         * @return this instance of the builder
599
         */
600
        public CONCRETE setRequestCode(int requestCode) {
601
            this.appCall.setRequestCode(requestCode);
602
            @SuppressWarnings("unchecked")
603
            CONCRETE result = (CONCRETE) this;
604
            return result;
605
        }
606
 
607
        /**
608
         * Sets the name of the application to be displayed in the dialog. If provided, this optimizes the user
609
         * experience as a preview of a shared item, etc., can be displayed sooner.
610
         *
611
         * @param applicationName the name of the Facebook application
612
         * @return this instance of the builder
613
         */
614
        public CONCRETE setApplicationName(String applicationName) {
615
            this.applicationName = applicationName;
616
            @SuppressWarnings("unchecked")
617
            CONCRETE result = (CONCRETE) this;
618
            return result;
619
        }
620
 
621
        /**
622
         * Sets the fragment that should launch the dialog. This allows the dialog to be
623
         * launched from a Fragment, and will allow the fragment to receive the
624
         * {@link Fragment#onActivityResult(int, int, android.content.Intent) onActivityResult}
625
         * call rather than the Activity.
626
         *
627
         * @param fragment the fragment that contains this control
628
         */
629
        public CONCRETE setFragment(Fragment fragment) {
630
            this.fragment = fragment;
631
            @SuppressWarnings("unchecked")
632
            CONCRETE result = (CONCRETE) this;
633
            return result;
634
        }
635
 
636
        /**
637
         * Constructs a FacebookDialog with an Intent that is correctly populated to present the dialog within
638
         * the Facebook application.
639
         *
640
         * @return a FacebookDialog instance
641
         */
642
        public FacebookDialog build() {
643
            validate();
644
 
645
            String action = getActionForFeatures(getDialogFeatures());
646
            int protocolVersion = getProtocolVersionForNativeDialog(activity, action,
647
                    getVersionSpecForFeatures(applicationId, action, getDialogFeatures()));
648
 
649
            Bundle extras = null;
650
            if (NativeProtocol.isVersionCompatibleWithBucketedIntent(protocolVersion)) {
651
                // Facebook app supports the new bucketed protocol
652
                extras = getMethodArguments();
653
            } else {
654
                // Facebook app only supports the old flat protocol
655
                extras = setBundleExtras(new Bundle());
656
            }
657
 
658
            Intent intent = NativeProtocol.createPlatformActivityIntent(
659
                    activity,
660
                    appCall.getCallId().toString(),
661
                    action,
662
                    protocolVersion,
663
                    applicationName,
664
                    extras);
665
            if (intent == null) {
666
                logDialogActivity(activity, fragment,
667
                        getEventName(action, extras.containsKey(NativeProtocol.EXTRA_PHOTOS), false),
668
                        AnalyticsEvents.PARAMETER_DIALOG_OUTCOME_VALUE_FAILED);
669
 
670
                throw new FacebookException(
671
                        "Unable to create Intent; this likely means the Facebook app is not installed.");
672
            }
673
 
674
            appCall.setRequestIntent(intent);
675
 
676
            return new FacebookDialog(activity, fragment, appCall, getOnPresentCallback());
677
        }
678
 
679
        /**
680
         * This is public primarily to allow its use elsewhere in the Android SDK; developers are discouraged from
681
         * consuming this method as the internal API may change.
682
         */
683
        protected String getWebFallbackUrlInternal() {
684
            Iterable<? extends DialogFeature> features = getDialogFeatures();
685
            String featureName = null;
686
            String action = null;
687
            for (DialogFeature feature : features) {
688
                // All actions in a set of DialogFeatures should have the same fallback url
689
                // So we can break after assigning the first one
690
                featureName = feature.name();
691
                action = feature.getAction();
692
                break;
693
            }
694
 
695
            Utility.DialogFeatureConfig config = Utility.getDialogFeatureConfig(applicationId, action, featureName);
696
            Uri fallbackUrl;
697
            if (config == null || (fallbackUrl = config.getFallbackUrl()) == null) {
698
                return null;
699
            }
700
 
701
            // Since we're talking to the server here, let's use the latest version we know about.
702
            // We know we are going to be communicating over a bucketed protocol.
703
            Bundle methodArguments = getMethodArguments();
704
            int protocolVersion = NativeProtocol.getLatestKnownVersion();
705
            Bundle webParams = ServerProtocol.getQueryParamsForPlatformActivityIntentWebFallback(
706
                    activity,
707
                    appCall.getCallId().toString(),
708
                    protocolVersion,
709
                    applicationName,
710
                    methodArguments);
711
            if (webParams == null) {
712
                // Could not create the query parameters
713
                return null;
714
            }
715
 
716
            // Now form the Uri
717
            if (fallbackUrl.isRelative()) {
718
                fallbackUrl = Utility.buildUri(
719
                        ServerProtocol.getDialogAuthority(),
720
                        fallbackUrl.toString(),
721
                        webParams);
722
            }
723
 
724
            return fallbackUrl.toString();
725
        }
726
 
727
        /**
728
         * Determines whether the native dialog can be presented (i.e., whether the required version of the
729
         * Facebook application is installed on the device, and whether the installed version supports all of
730
         * the parameters specified for the dialog).
731
         *
732
         * @return true if the dialog can be presented; false if not
733
         */
734
        public boolean canPresent() {
735
            return handleCanPresent(activity, getDialogFeatures());
736
        }
737
 
738
        void validate() {
739
        }
740
 
741
        OnPresentCallback getOnPresentCallback() {
742
            return new OnPresentCallback() {
743
                @Override
744
                public void onPresent(Context context) throws Exception {
745
                    // We're actually being presented, so put our attachments in the content provider.
746
                    if (imageAttachments != null && imageAttachments.size() > 0) {
747
                        getAttachmentStore().addAttachmentsForCall(context, appCall.getCallId(), imageAttachments);
748
                    }
749
                    if (mediaAttachmentFiles != null && mediaAttachmentFiles.size() > 0) {
750
                        getAttachmentStore().addAttachmentFilesForCall(context, appCall.getCallId(),
751
                                mediaAttachmentFiles);
752
                    }
753
                }
754
            };
755
        }
756
 
757
        protected List<String> addImageAttachments(Collection<Bitmap> bitmaps) {
758
            ArrayList<String> attachmentUrls = new ArrayList<String>();
759
            for (Bitmap bitmap : bitmaps) {
760
                String attachmentName = UUID.randomUUID().toString();
761
 
762
                addImageAttachment(attachmentName, bitmap);
763
 
764
                String url = NativeAppCallContentProvider.getAttachmentUrl(applicationId, appCall.getCallId(),
765
                        attachmentName);
766
                attachmentUrls.add(url);
767
            }
768
 
769
            return attachmentUrls;
770
        }
771
 
772
        protected List<String> addImageAttachmentFiles(Collection<File> bitmapFiles) {
773
            ArrayList<String> attachmentUrls = new ArrayList<String>();
774
            for (File bitmapFile : bitmapFiles) {
775
                String attachmentName = UUID.randomUUID().toString();
776
 
777
                addImageAttachment(attachmentName, bitmapFile);
778
 
779
                String url = NativeAppCallContentProvider.getAttachmentUrl(applicationId, appCall.getCallId(),
780
                        attachmentName);
781
                attachmentUrls.add(url);
782
            }
783
 
784
            return attachmentUrls;
785
        }
786
 
787
        protected String addVideoAttachmentFile(File videoFile) {
788
            String attachmentName = UUID.randomUUID().toString();
789
            addVideoAttachment(attachmentName, videoFile);
790
            String url = NativeAppCallContentProvider.getAttachmentUrl(applicationId, appCall.getCallId(),
791
                    attachmentName);
792
            return url;
793
        }
794
 
795
        List<String> getImageAttachmentNames() {
796
            return new ArrayList<String>(imageAttachments.keySet());
797
        }
798
 
799
        protected Bundle setBundleExtras(Bundle extras) {
800
            // Default implementation.
801
            return extras;
802
        }
803
 
804
        protected abstract Bundle getMethodArguments();
805
 
806
        protected void putExtra(Bundle extras, String key, String value) {
807
            if (value != null) {
808
                extras.putString(key, value);
809
            }
810
        }
811
 
812
        protected abstract EnumSet<? extends DialogFeature> getDialogFeatures();
813
 
814
        protected CONCRETE addImageAttachment(String imageName, Bitmap bitmap) {
815
            imageAttachments.put(imageName, bitmap);
816
            @SuppressWarnings("unchecked")
817
            CONCRETE result = (CONCRETE) this;
818
            return result;
819
        }
820
 
821
        protected CONCRETE addImageAttachment(String imageName, File attachment) {
822
            mediaAttachmentFiles.put(imageName, attachment);
823
            @SuppressWarnings("unchecked")
824
            CONCRETE result = (CONCRETE) this;
825
            return result;
826
        }
827
 
828
        protected CONCRETE addVideoAttachment(String videoName, File attachment) {
829
            mediaAttachmentFiles.put(videoName, attachment);
830
            @SuppressWarnings("unchecked")
831
            CONCRETE result = (CONCRETE) this;
832
            return result;
833
        }
834
    }
835
 
836
    private abstract static class ShareDialogBuilderBase<CONCRETE extends ShareDialogBuilderBase<?>> extends Builder<CONCRETE> {
837
        private String name;
838
        private String caption;
839
        private String description;
840
        protected String link;
841
        private String picture;
842
        private String place;
843
        private ArrayList<String> friends;
844
        private String ref;
845
        private boolean dataErrorsFatal;
846
 
847
        /**
848
         * Constructor.
849
         *
850
         * @param activity the Activity which is presenting the native Share dialog; must not be null
851
         */
852
        public ShareDialogBuilderBase(Activity activity) {
853
            super(activity);
854
        }
855
 
856
        /**
857
         * Sets the name of the URL to be shared. This method only has effect if setLink is called.
858
         *
859
         * @param name the name
860
         * @return this instance of the builder
861
         */
862
        public CONCRETE setName(String name) {
863
            this.name = name;
864
            @SuppressWarnings("unchecked")
865
            CONCRETE result = (CONCRETE) this;
866
            return result;
867
        }
868
 
869
        /**
870
         * Sets the caption of the URL to be shared. This method only has effect if setLink is called.
871
         *
872
         * @param caption the caption
873
         * @return this instance of the builder
874
         */
875
        public CONCRETE setCaption(String caption) {
876
            this.caption = caption;
877
            @SuppressWarnings("unchecked")
878
            CONCRETE result = (CONCRETE) this;
879
            return result;
880
        }
881
 
882
        /**
883
         * Sets the description of the URL to be shared. This method only has effect if setLink is called.
884
         *
885
         * @param description the description
886
         * @return this instance of the builder
887
         */
888
        public CONCRETE setDescription(String description) {
889
            this.description = description;
890
            @SuppressWarnings("unchecked")
891
            CONCRETE result = (CONCRETE) this;
892
            return result;
893
        }
894
 
895
        /**
896
         * Sets the URL of the item to be shared.
897
         *
898
         * @param link the URL
899
         * @return this instance of the builder
900
         */
901
        public CONCRETE setLink(String link) {
902
            this.link = link;
903
            @SuppressWarnings("unchecked")
904
            CONCRETE result = (CONCRETE) this;
905
            return result;
906
        }
907
 
908
        /**
909
         * Sets the URL of the image of the URL to be shared. This method only has effect if setLink is called.
910
         *
911
         * @param picture the URL of the image
912
         * @return this instance of the builder
913
         */
914
        public CONCRETE setPicture(String picture) {
915
            this.picture = picture;
916
            @SuppressWarnings("unchecked")
917
            CONCRETE result = (CONCRETE) this;
918
            return result;
919
        }
920
 
921
        /**
922
         * Sets the place for the item to be shared.
923
         *
924
         * @param place the Facebook ID of the place
925
         * @return this instance of the builder
926
         */
927
        public CONCRETE setPlace(String place) {
928
            this.place = place;
929
            @SuppressWarnings("unchecked")
930
            CONCRETE result = (CONCRETE) this;
931
            return result;
932
        }
933
 
934
        /**
935
         * Sets the tagged friends for the item to be shared.
936
         *
937
         * @param friends a list of Facebook IDs of the friends to be tagged in the shared item
938
         * @return this instance of the builder
939
         */
940
        public CONCRETE setFriends(List<String> friends) {
941
            this.friends = (friends == null ? null : new ArrayList<String>(friends));
942
            @SuppressWarnings("unchecked")
943
            CONCRETE result = (CONCRETE) this;
944
            return result;
945
        }
946
 
947
        /**
948
         * Sets the 'ref' property of the item to be shared.
949
         *
950
         * @param ref the 'ref' property
951
         * @return this instance of the builder
952
         */
953
        public CONCRETE setRef(String ref) {
954
            this.ref = ref;
955
            @SuppressWarnings("unchecked")
956
            CONCRETE result = (CONCRETE) this;
957
            return result;
958
        }
959
 
960
        /**
961
         * Sets whether errors encountered during previewing the shared item should be considered fatal and
962
         * cause the dialog to return an error
963
         *
964
         * @param dataErrorsFatal true if data errors should be fatal; false if not
965
         * @return this instance of the builder
966
         */
967
        public CONCRETE setDataErrorsFatal(boolean dataErrorsFatal) {
968
            this.dataErrorsFatal = dataErrorsFatal;
969
            @SuppressWarnings("unchecked")
970
            CONCRETE result = (CONCRETE) this;
971
            return result;
972
        }
973
 
974
        @Override
975
        protected Bundle setBundleExtras(Bundle extras) {
976
            putExtra(extras, NativeProtocol.EXTRA_APPLICATION_ID, applicationId);
977
            putExtra(extras, NativeProtocol.EXTRA_APPLICATION_NAME, applicationName);
978
            putExtra(extras, NativeProtocol.EXTRA_TITLE, name);
979
            putExtra(extras, NativeProtocol.EXTRA_SUBTITLE, caption);
980
            putExtra(extras, NativeProtocol.EXTRA_DESCRIPTION, description);
981
            putExtra(extras, NativeProtocol.EXTRA_LINK, link);
982
            putExtra(extras, NativeProtocol.EXTRA_IMAGE, picture);
983
            putExtra(extras, NativeProtocol.EXTRA_PLACE_TAG, place);
984
            putExtra(extras, NativeProtocol.EXTRA_REF, ref);
985
 
986
            extras.putBoolean(NativeProtocol.EXTRA_DATA_FAILURES_FATAL, dataErrorsFatal);
987
            if (!Utility.isNullOrEmpty(friends)) {
988
                extras.putStringArrayList(NativeProtocol.EXTRA_FRIEND_TAGS, friends);
989
            }
990
            return extras;
991
        }
992
 
993
        @Override
994
        protected Bundle getMethodArguments() {
995
            Bundle methodArguments = new Bundle();
996
 
997
            putExtra(methodArguments, NativeProtocol.METHOD_ARGS_TITLE, name);
998
            putExtra(methodArguments, NativeProtocol.METHOD_ARGS_SUBTITLE, caption);
999
            putExtra(methodArguments, NativeProtocol.METHOD_ARGS_DESCRIPTION, description);
1000
            putExtra(methodArguments, NativeProtocol.METHOD_ARGS_LINK, link);
1001
            putExtra(methodArguments, NativeProtocol.METHOD_ARGS_IMAGE, picture);
1002
            putExtra(methodArguments, NativeProtocol.METHOD_ARGS_PLACE_TAG, place);
1003
            putExtra(methodArguments, NativeProtocol.METHOD_ARGS_REF, ref);
1004
 
1005
            methodArguments.putBoolean(NativeProtocol.METHOD_ARGS_DATA_FAILURES_FATAL, dataErrorsFatal);
1006
            if (!Utility.isNullOrEmpty(friends)) {
1007
                methodArguments.putStringArrayList(NativeProtocol.METHOD_ARGS_FRIEND_TAGS, friends);
1008
            }
1009
 
1010
            return methodArguments;
1011
        }
1012
    }
1013
 
1014
    /**
1015
     * Provides a builder which can construct a FacebookDialog instance suitable for presenting the native
1016
     * Share dialog. This builder will throw an exception if the Facebook application is not installed, so it
1017
     * should only be used if {@link FacebookDialog#canPresentShareDialog(android.content.Context,
1018
     * com.facebook.widget.FacebookDialog.ShareDialogFeature...)}  indicates the capability is available.
1019
     */
1020
    public static class ShareDialogBuilder extends ShareDialogBuilderBase<ShareDialogBuilder> {
1021
 
1022
        /**
1023
         * Constructor.
1024
         *
1025
         * @param activity the Activity which is presenting the native Share dialog; must not be null
1026
         */
1027
        public ShareDialogBuilder(Activity activity) {
1028
            super(activity);
1029
        }
1030
 
1031
        @Override
1032
        protected EnumSet<? extends DialogFeature> getDialogFeatures() {
1033
            return EnumSet.of(ShareDialogFeature.SHARE_DIALOG);
1034
        }
1035
    }
1036
 
1037
    private static abstract class PhotoDialogBuilderBase<CONCRETE extends PhotoDialogBuilderBase<?>>
1038
            extends Builder<CONCRETE> {
1039
        static int MAXIMUM_PHOTO_COUNT = 6;
1040
        private String place;
1041
        private ArrayList<String> friends;
1042
        private ArrayList<String> imageAttachmentUrls = new ArrayList<String>();
1043
 
1044
        /**
1045
         * Constructor.
1046
         *
1047
         * @param activity the Activity which is presenting the native Share dialog; must not be null
1048
         */
1049
        public PhotoDialogBuilderBase(Activity activity) {
1050
            super(activity);
1051
        }
1052
 
1053
        /**
1054
         * Sets the place for the item to be shared.
1055
         *
1056
         * @param place the Facebook ID of the place
1057
         * @return this instance of the builder
1058
         */
1059
        public CONCRETE setPlace(String place) {
1060
            this.place = place;
1061
            @SuppressWarnings("unchecked")
1062
            CONCRETE result = (CONCRETE) this;
1063
            return result;
1064
        }
1065
 
1066
        /**
1067
         * Sets the tagged friends for the item to be shared.
1068
         *
1069
         * @param friends a list of Facebook IDs of the friends to be tagged in the shared item
1070
         * @return this instance of the builder
1071
         */
1072
        public CONCRETE setFriends(List<String> friends) {
1073
            this.friends = (friends == null ? null : new ArrayList<String>(friends));
1074
            @SuppressWarnings("unchecked")
1075
            CONCRETE result = (CONCRETE) this;
1076
            return result;
1077
        }
1078
 
1079
        /**
1080
         * <p></p>Adds one or more photos to the list of photos to display in the native Share dialog, by providing
1081
         * an in-memory representation of the photos. The dialog's callback will be called once the user has
1082
         * shared the photos, but the photos themselves may be uploaded in the background by the Facebook app;
1083
         * apps wishing to be notified when the photo upload has succeeded or failed should extend the
1084
         * FacebookBroadcastReceiver class and register it in their AndroidManifest.xml.</p>
1085
         * <p>In order for the images to be provided to the Facebook application as part of the app call, the
1086
         * NativeAppCallContentProvider must be specified correctly in the application's AndroidManifest.xml.</p>
1087
         * No more than six photos may be shared at a time.
1088
         * @param photos a collection of Files representing photos to be uploaded
1089
         * @return this instance of the builder
1090
         */
1091
        public CONCRETE addPhotos(Collection<Bitmap> photos) {
1092
            imageAttachmentUrls.addAll(addImageAttachments(photos));
1093
            @SuppressWarnings("unchecked")
1094
            CONCRETE result = (CONCRETE) this;
1095
            return result;
1096
        }
1097
 
1098
        /**
1099
         * Adds one or more photos to the list of photos to display in the native Share dialog, by specifying
1100
         * their location in the file system. The dialog's callback will be called once the user has
1101
         * shared the photos, but the photos themselves may be uploaded in the background by the Facebook app;
1102
         * apps wishing to be notified when the photo upload has succeeded or failed should extend the
1103
         * FacebookBroadcastReceiver class and register it in their AndroidManifest.xml.
1104
         * No more than six photos may be shared at a time.
1105
         * @param photos a collection of Files representing photos to be uploaded
1106
         * @return this instance of the builder
1107
         */
1108
        public CONCRETE addPhotoFiles(Collection<File> photos) {
1109
            imageAttachmentUrls.addAll(addImageAttachmentFiles(photos));
1110
            @SuppressWarnings("unchecked")
1111
            CONCRETE result = (CONCRETE) this;
1112
            return result;
1113
        }
1114
 
1115
        abstract int getMaximumNumberOfPhotos();
1116
 
1117
        @Override
1118
        void validate() {
1119
            super.validate();
1120
 
1121
            if (imageAttachmentUrls.isEmpty()) {
1122
                throw new FacebookException("Must specify at least one photo.");
1123
            }
1124
 
1125
            if (imageAttachmentUrls.size() > getMaximumNumberOfPhotos()) {
1126
                throw new FacebookException(String.format("Cannot add more than %d photos.", getMaximumNumberOfPhotos()));
1127
            }
1128
        }
1129
 
1130
        @Override
1131
        protected Bundle setBundleExtras(Bundle extras) {
1132
            putExtra(extras, NativeProtocol.EXTRA_APPLICATION_ID, applicationId);
1133
            putExtra(extras, NativeProtocol.EXTRA_APPLICATION_NAME, applicationName);
1134
            putExtra(extras, NativeProtocol.EXTRA_PLACE_TAG, place);
1135
            extras.putStringArrayList(NativeProtocol.EXTRA_PHOTOS, imageAttachmentUrls);
1136
 
1137
            if (!Utility.isNullOrEmpty(friends)) {
1138
                extras.putStringArrayList(NativeProtocol.EXTRA_FRIEND_TAGS, friends);
1139
            }
1140
            return extras;
1141
        }
1142
 
1143
        @Override
1144
        protected Bundle getMethodArguments() {
1145
            Bundle methodArgs = new Bundle();
1146
 
1147
            putExtra(methodArgs, NativeProtocol.METHOD_ARGS_PLACE_TAG, place);
1148
            methodArgs.putStringArrayList(NativeProtocol.METHOD_ARGS_PHOTOS, imageAttachmentUrls);
1149
 
1150
            if (!Utility.isNullOrEmpty(friends)) {
1151
                methodArgs.putStringArrayList(NativeProtocol.METHOD_ARGS_FRIEND_TAGS, friends);
1152
            }
1153
 
1154
            return methodArgs;
1155
        }
1156
    }
1157
 
1158
    /**
1159
     * Provides a builder which can construct a FacebookDialog instance suitable for presenting the native
1160
     * Share dialog for sharing photos. This builder will throw an exception if the Facebook application is not
1161
     * installed, so it should only be used if {@link FacebookDialog#canPresentShareDialog(android.content.Context,
1162
     * com.facebook.widget.FacebookDialog.ShareDialogFeature...)}  indicates the capability is available.
1163
     */
1164
    public static class PhotoShareDialogBuilder extends PhotoDialogBuilderBase<PhotoShareDialogBuilder> {
1165
        /**
1166
         * Constructor.
1167
         *
1168
         * @param activity the Activity which is presenting the native Share dialog; must not be null
1169
         */
1170
        public PhotoShareDialogBuilder(Activity activity) {
1171
            super(activity);
1172
        }
1173
 
1174
        @Override
1175
        protected EnumSet<? extends DialogFeature> getDialogFeatures() {
1176
            return EnumSet.of(ShareDialogFeature.SHARE_DIALOG, ShareDialogFeature.PHOTOS);
1177
        }
1178
 
1179
        @Override
1180
        int getMaximumNumberOfPhotos() {
1181
            return MAXIMUM_PHOTO_COUNT;
1182
        }
1183
    }
1184
 
1185
    /**
1186
     * Provides a builder which can construct a FacebookDialog instance suitable for presenting the native
1187
     * Message dialog for sharing photos. This builder will throw an exception if the Messenger application is not
1188
     * installed, so it should only be used if {@link FacebookDialog#canPresentMessageDialog(android.content.Context,
1189
     * com.facebook.widget.FacebookDialog.MessageDialogFeature...)} indicates the capability is available.
1190
     */
1191
    public static class PhotoMessageDialogBuilder extends PhotoDialogBuilderBase<PhotoMessageDialogBuilder> {
1192
        /**
1193
         * Constructor.
1194
         *
1195
         * @param activity the Activity which is presenting the native Message dialog; must not be null
1196
         */
1197
        public PhotoMessageDialogBuilder(Activity activity) {
1198
            super(activity);
1199
        }
1200
 
1201
        @Override
1202
        protected EnumSet<? extends DialogFeature> getDialogFeatures() {
1203
            return EnumSet.of(MessageDialogFeature.MESSAGE_DIALOG, MessageDialogFeature.PHOTOS);
1204
        }
1205
 
1206
        @Override
1207
        int getMaximumNumberOfPhotos() {
1208
            return MAXIMUM_PHOTO_COUNT;
1209
        }
1210
 
1211
        /**
1212
         * setPlace is not supported for the photo message dialog, setting this method will have no effect.
1213
         *
1214
         * @param place will be ignored
1215
         * @return this instance of the builder
1216
         */
1217
        @Override
1218
        public PhotoMessageDialogBuilder setPlace(String place) {
1219
            return this;
1220
        }
1221
 
1222
        /**
1223
         * setFriends is not supported for the photo message dialog, setting this method will have no effect.
1224
         *
1225
         * @param friends will be ignored
1226
         * @return this instance of the builder
1227
         */
1228
        @Override
1229
        public PhotoMessageDialogBuilder setFriends(List<String> friends) {
1230
            return this;
1231
        }
1232
    }
1233
 
1234
    private static abstract class VideoDialogBuilderBase<CONCRETE extends VideoDialogBuilderBase<?>>
1235
            extends Builder<CONCRETE> {
1236
        private String place;
1237
        private String videoAttachmentUrl;
1238
 
1239
        /**
1240
         * Constructor.
1241
         *
1242
         * @param activity the Activity which is presenting the native Share dialog; must not be null
1243
         */
1244
        public VideoDialogBuilderBase(Activity activity) {
1245
            super(activity);
1246
        }
1247
 
1248
        /**
1249
         * Sets the place for the item to be shared.
1250
         *
1251
         * @param place the Facebook ID of the place
1252
         * @return this instance of the builder
1253
         */
1254
        public CONCRETE setPlace(String place) {
1255
            this.place = place;
1256
            @SuppressWarnings("unchecked")
1257
            CONCRETE result = (CONCRETE) this;
1258
            return result;
1259
        }
1260
 
1261
        /**
1262
         * Adds a video to display in the native Share dialog, by specifying the location in the file system.
1263
         * The dialog's callback will be called once the user has shared the video, but the video may be uploaded
1264
         * in the background by the Facebook app; apps wishing to be notified when the video upload has succeeded
1265
         * or failed should extend the FacebookBroadcastReceiver class and register it in their AndroidManifest.xml.
1266
         * @param video a Files representing the video to be uploaded
1267
         * @return this instance of the builder
1268
         */
1269
        public CONCRETE addVideoFile(File video) {
1270
            this.videoAttachmentUrl = addVideoAttachmentFile(video);
1271
            @SuppressWarnings("unchecked")
1272
            CONCRETE result = (CONCRETE) this;
1273
            return result;
1274
        }
1275
 
1276
        @Override
1277
        void validate() {
1278
            super.validate();
1279
 
1280
            if (videoAttachmentUrl == null || videoAttachmentUrl.isEmpty()) {
1281
                throw new FacebookException("Must specify at least one video.");
1282
            }
1283
        }
1284
 
1285
        @Override
1286
        protected Bundle getMethodArguments() {
1287
            Bundle methodArgs = new Bundle();
1288
            putExtra(methodArgs, NativeProtocol.METHOD_ARGS_PLACE_TAG, place);
1289
            methodArgs.putString(NativeProtocol.METHOD_ARGS_VIDEO, videoAttachmentUrl);
1290
            return methodArgs;
1291
        }
1292
    }
1293
 
1294
    /**
1295
     * Provides a builder which can construct a FacebookDialog instance suitable for presenting the native
1296
     * Share dialog for sharing videos. This builder will throw an exception if the Facebook application is not
1297
     * installed, so it should only be used if {@link FacebookDialog#canPresentShareDialog(android.content.Context,
1298
     * com.facebook.widget.FacebookDialog.ShareDialogFeature...)} indicates the capability is available.
1299
     */
1300
    public static class VideoShareDialogBuilder extends VideoDialogBuilderBase<VideoShareDialogBuilder> {
1301
        /**
1302
         * Constructor.
1303
         *
1304
         * @param activity the Activity which is presenting the native Share dialog; must not be null
1305
         */
1306
        public VideoShareDialogBuilder(Activity activity) {
1307
            super(activity);
1308
        }
1309
 
1310
        @Override
1311
        protected EnumSet<? extends DialogFeature> getDialogFeatures() {
1312
            return EnumSet.of(ShareDialogFeature.SHARE_DIALOG, ShareDialogFeature.VIDEO);
1313
        }
1314
    }
1315
 
1316
    /**
1317
     * Provides a builder which can construct a FacebookDialog instance suitable for presenting the native
1318
     * Message dialog. This builder will throw an exception if the Facebook Messenger application is not installed, so it
1319
     * should only be used if {@link FacebookDialog#canPresentMessageDialog(android.content.Context,
1320
     * com.facebook.widget.FacebookDialog.MessageDialogFeature...)}  indicates the capability is available.
1321
     * The "friends" and "place" properties will be ignored as the Facebook Messenger app does not support tagging.
1322
     */
1323
    public static class MessageDialogBuilder extends ShareDialogBuilderBase<MessageDialogBuilder> {
1324
 
1325
        /**
1326
         * Constructor.
1327
         *
1328
         * @param activity the Activity which is presenting the native Message dialog; must not be null
1329
         */
1330
        public MessageDialogBuilder(Activity activity) {
1331
            super(activity);
1332
        }
1333
 
1334
        @Override
1335
        protected EnumSet<? extends DialogFeature> getDialogFeatures() {
1336
            return EnumSet.of(MessageDialogFeature.MESSAGE_DIALOG);
1337
        }
1338
 
1339
        /**
1340
         * setPlace is not supported for the message dialog, setting this method will have no effect.
1341
         *
1342
         * @param place will be ignored
1343
         * @return this instance of the builder
1344
         */
1345
        @Override
1346
        public MessageDialogBuilder setPlace(String place) {
1347
            return this;
1348
        }
1349
 
1350
        /**
1351
         * setFriends is not supported for the message dialog, setting this method will have no effect.
1352
         *
1353
         * @param friends will be ignored
1354
         * @return this instance of the builder
1355
         */
1356
        @Override
1357
        public MessageDialogBuilder setFriends(List<String> friends) {
1358
            return this;
1359
        }
1360
    }
1361
 
1362
    private static abstract class OpenGraphDialogBuilderBase<CONCRETE extends OpenGraphDialogBuilderBase<?>>
1363
            extends Builder<CONCRETE> {
1364
 
1365
        private String previewPropertyName;
1366
        private OpenGraphAction action;
1367
        private String actionType;
1368
        private boolean dataErrorsFatal;
1369
 
1370
        /**
1371
         * Constructor.
1372
         *
1373
         * @param activity            the Activity which is presenting the native Open Graph action publish dialog;
1374
         *                            must not be null
1375
         * @param action              the Open Graph action to be published, which must contain a reference to at least one
1376
         *                            Open Graph object with the property name specified by setPreviewPropertyName; the action
1377
         *                            must have had its type specified via the {@link OpenGraphAction#setType(String)} method
1378
         * @param actionType          the type of the Open Graph action to be published, which should be the namespace-qualified
1379
         *                            name of the action type (e.g., "myappnamespace:myactiontype"); this will override the type
1380
         *                            of the action passed in.
1381
         * @param previewPropertyName the name of a property on the Open Graph action that contains the
1382
         *                            Open Graph object which will be displayed as a preview to the user
1383
         */
1384
        @Deprecated
1385
        public OpenGraphDialogBuilderBase(Activity activity, OpenGraphAction action, String actionType,
1386
                String previewPropertyName) {
1387
            super(activity);
1388
 
1389
            Validate.notNull(action, "action");
1390
            Validate.notNullOrEmpty(actionType, "actionType");
1391
            Validate.notNullOrEmpty(previewPropertyName, "previewPropertyName");
1392
            if (action.getProperty(previewPropertyName) == null) {
1393
                throw new IllegalArgumentException(
1394
                        "A property named \"" + previewPropertyName + "\" was not found on the action.  The name of " +
1395
                                "the preview property must match the name of an action property.");
1396
            }
1397
            String typeOnAction = action.getType();
1398
            if (!Utility.isNullOrEmpty(typeOnAction) && !typeOnAction.equals(actionType)) {
1399
                throw new IllegalArgumentException("'actionType' must match the type of 'action' if it is specified. " +
1400
                        "Consider using OpenGraphDialogBuilderBase(Activity activity, OpenGraphAction action, " +
1401
                        "String previewPropertyName) instead.");
1402
            }
1403
            this.action = action;
1404
            this.actionType = actionType;
1405
            this.previewPropertyName = previewPropertyName;
1406
        }
1407
 
1408
        /**
1409
         * Constructor.
1410
         *
1411
         * @param activity            the Activity which is presenting the native Open Graph action publish dialog;
1412
         *                            must not be null
1413
         * @param action              the Open Graph action to be published, which must contain a reference to at least one
1414
         *                            Open Graph object with the property name specified by setPreviewPropertyName; the action
1415
         *                            must have had its type specified via the {@link OpenGraphAction#setType(String)} method
1416
         * @param previewPropertyName the name of a property on the Open Graph action that contains the
1417
         *                            Open Graph object which will be displayed as a preview to the user
1418
         */
1419
        public OpenGraphDialogBuilderBase(Activity activity, OpenGraphAction action, String previewPropertyName) {
1420
            super(activity);
1421
 
1422
            Validate.notNull(action, "action");
1423
            Validate.notNullOrEmpty(action.getType(), "action.getType()");
1424
            Validate.notNullOrEmpty(previewPropertyName, "previewPropertyName");
1425
            if (action.getProperty(previewPropertyName) == null) {
1426
                throw new IllegalArgumentException(
1427
                        "A property named \"" + previewPropertyName + "\" was not found on the action.  The name of " +
1428
                                "the preview property must match the name of an action property.");
1429
            }
1430
 
1431
            this.action = action;
1432
            this.actionType = action.getType();
1433
            this.previewPropertyName = previewPropertyName;
1434
        }
1435
 
1436
        /**
1437
         * Sets whether errors encountered during previewing the shared item should be considered fatal and
1438
         * cause the dialog to return an error
1439
         *
1440
         * @param dataErrorsFatal true if data errors should be fatal; false if not
1441
         * @return this instance of the builder
1442
         */
1443
        public CONCRETE setDataErrorsFatal(boolean dataErrorsFatal) {
1444
            this.dataErrorsFatal = dataErrorsFatal;
1445
            @SuppressWarnings("unchecked")
1446
            CONCRETE result = (CONCRETE) this;
1447
            return result;
1448
        }
1449
 
1450
        /**
1451
         * <p>Specifies a list of images for the Open Graph action that should be uploaded prior to publishing the
1452
         * action. The action must already have been set prior to calling this method. This method will generate unique
1453
         * names for the image attachments and update the action to refer to these attachments. Note that calling
1454
         * setAction again after calling this method will not clear the image attachments already set, but the new
1455
         * action will have no reference to the existing attachments. The images will not be marked as being
1456
         * user-generated.</p>
1457
         * <p/>
1458
         * <p>In order for the images to be provided to the Facebook application as part of the app call, the
1459
         * NativeAppCallContentProvider must be specified correctly in the application's AndroidManifest.xml.</p>
1460
         *
1461
         * @param bitmaps a list of Bitmaps to be uploaded and attached to the Open Graph action
1462
         * @return this instance of the builder
1463
         */
1464
        public CONCRETE setImageAttachmentsForAction(List<Bitmap> bitmaps) {
1465
            return setImageAttachmentsForAction(bitmaps, false);
1466
        }
1467
 
1468
        /**
1469
         * <p>Specifies a list of images for the Open Graph action that should be uploaded prior to publishing the
1470
         * action. The action must already have been set prior to calling this method. This method will generate unique
1471
         * names for the image attachments and update the action to refer to these attachments. Note that calling
1472
         * setAction again after calling this method will not clear the image attachments already set, but the new
1473
         * action will have no reference to the existing attachments. The images may be marked as being
1474
         * user-generated -- refer to
1475
         * <a href="https://developers.facebook.com/docs/opengraph/howtos/adding-photos-to-stories/">this article</a>
1476
         * for more information.</p>
1477
         * <p/>
1478
         * <p>In order for the images to be provided to the Facebook application as part of the app call, the
1479
         * NativeAppCallContentProvider must be specified correctly in the application's AndroidManifest.xml.</p>
1480
         *
1481
         * @param bitmaps         a list of Bitmaps to be uploaded and attached to the Open Graph action
1482
         * @param isUserGenerated if true, specifies that the user_generated flag should be set for these images
1483
         * @return this instance of the builder
1484
         */
1485
        public CONCRETE setImageAttachmentsForAction(List<Bitmap> bitmaps,
1486
                boolean isUserGenerated) {
1487
            Validate.containsNoNulls(bitmaps, "bitmaps");
1488
            if (action == null) {
1489
                throw new FacebookException("Can not set attachments prior to setting action.");
1490
            }
1491
 
1492
            List<String> attachmentUrls = addImageAttachments(bitmaps);
1493
            updateActionAttachmentUrls(attachmentUrls, isUserGenerated);
1494
 
1495
            @SuppressWarnings("unchecked")
1496
            CONCRETE result = (CONCRETE) this;
1497
            return result;
1498
        }
1499
 
1500
        /**
1501
         * <p>Specifies a list of images for the Open Graph action that should be uploaded prior to publishing the
1502
         * action. The action must already have been set prior to calling this method.  The images will not be marked
1503
         * as being user-generated. This method will generate unique names for the image attachments and update the
1504
         * action to refer to these attachments. Note that calling setAction again after calling this method will
1505
         * not clear the image attachments already set, but the new action will have no reference to the existing
1506
         * attachments.</p>
1507
         * <p/>
1508
         * <p>In order for the images to be provided to the Facebook application as part of the app call, the
1509
         * NativeAppCallContentProvider must be specified correctly in the application's AndroidManifest.xml.</p>
1510
         *
1511
         * @param bitmapFiles a list of Files containing bitmaps to be uploaded and attached to the Open Graph action
1512
         * @return this instance of the builder
1513
         */
1514
        public CONCRETE setImageAttachmentFilesForAction(List<File> bitmapFiles) {
1515
            return setImageAttachmentFilesForAction(bitmapFiles, false);
1516
        }
1517
 
1518
        /**
1519
         * <p>Specifies a list of images for the Open Graph action that should be uploaded prior to publishing the
1520
         * action. The action must already have been set prior to calling this method. The images may be marked as being
1521
         * user-generated -- refer to
1522
         * <a href="https://developers.facebook.com/docs/opengraph/howtos/adding-photos-to-stories/">this article</a>
1523
         * for more information. This method will generate unique
1524
         * names for the image attachments and update the action to refer to these attachments. Note that calling
1525
         * setAction again after calling this method will not clear the image attachments already set, but the new
1526
         * action will have no reference to the existing attachments.</p>
1527
         * <p/>
1528
         * <p>In order for the images to be provided to the Facebook application as part of the app call, the
1529
         * NativeAppCallContentProvider must be specified correctly in the application's AndroidManifest.xml.</p>
1530
         *
1531
         * @param bitmapFiles     a list of Files containing bitmaps to be uploaded and attached to the Open Graph action
1532
         * @param isUserGenerated if true, specifies that the user_generated flag should be set for these images
1533
         * @return this instance of the builder
1534
         */
1535
        public CONCRETE setImageAttachmentFilesForAction(List<File> bitmapFiles,
1536
                boolean isUserGenerated) {
1537
            Validate.containsNoNulls(bitmapFiles, "bitmapFiles");
1538
            if (action == null) {
1539
                throw new FacebookException("Can not set attachments prior to setting action.");
1540
            }
1541
 
1542
            List<String> attachmentUrls = addImageAttachmentFiles(bitmapFiles);
1543
            updateActionAttachmentUrls(attachmentUrls, isUserGenerated);
1544
 
1545
            @SuppressWarnings("unchecked")
1546
            CONCRETE result = (CONCRETE) this;
1547
            return result;
1548
        }
1549
 
1550
        private void updateActionAttachmentUrls(List<String> attachmentUrls, boolean isUserGenerated) {
1551
            List<JSONObject> attachments = action.getImage();
1552
            if (attachments == null) {
1553
                attachments = new ArrayList<JSONObject>(attachmentUrls.size());
1554
            }
1555
 
1556
            for (String url : attachmentUrls) {
1557
                JSONObject jsonObject = new JSONObject();
1558
                try {
1559
                    jsonObject.put(NativeProtocol.IMAGE_URL_KEY, url);
1560
                    if (isUserGenerated) {
1561
                        jsonObject.put(NativeProtocol.IMAGE_USER_GENERATED_KEY, true);
1562
                    }
1563
                } catch (JSONException e) {
1564
                    throw new FacebookException("Unable to attach images", e);
1565
                }
1566
                attachments.add(jsonObject);
1567
            }
1568
            action.setImage(attachments);
1569
        }
1570
 
1571
 
1572
        /**
1573
         * <p>Specifies a list of images for an Open Graph object referenced by the action that should be uploaded
1574
         * prior to publishing the action. The images will not be marked as user-generated.
1575
         * The action must already have been set prior to calling this method, and
1576
         * the action must have a GraphObject-valued property with the specified property name. This method will
1577
         * generate unique names for the image attachments and update the graph object to refer to these
1578
         * attachments. Note that calling setObject again after calling this method, or modifying the value of the
1579
         * specified property, will not clear the image attachments already set, but the new action (or objects)
1580
         * will have no reference to the existing attachments.</p>
1581
         * <p/>
1582
         * <p>In order for the images to be provided to the Facebook application as part of the app call, the
1583
         * NativeAppCallContentProvider must be specified correctly in the application's AndroidManifest.xml.</p>
1584
         *
1585
         * @param objectProperty the name of a property on the action that corresponds to an Open Graph object;
1586
         *                       the object must be marked as a new object to be created
1587
         *                       (i.e., {@link com.facebook.model.OpenGraphObject#getCreateObject()} must return
1588
         *                       true) or an exception will be thrown
1589
         * @param bitmaps        a list of Files containing bitmaps to be uploaded and attached to the Open Graph object
1590
         * @return this instance of the builder
1591
         */
1592
        public CONCRETE setImageAttachmentsForObject(String objectProperty, List<Bitmap> bitmaps) {
1593
            return setImageAttachmentsForObject(objectProperty, bitmaps, false);
1594
        }
1595
 
1596
        /**
1597
         * <p>Specifies a list of images for an Open Graph object referenced by the action that should be uploaded
1598
         * prior to publishing the action. The images may be marked as being
1599
         * user-generated -- refer to
1600
         * <a href="https://developers.facebook.com/docs/opengraph/howtos/adding-photos-to-stories/">this article</a>
1601
         * for more information.
1602
         * The action must already have been set prior to calling this method, and
1603
         * the action must have a GraphObject-valued property with the specified property name. This method will
1604
         * generate unique names for the image attachments and update the graph object to refer to these
1605
         * attachments. Note that calling setObject again after calling this method, or modifying the value of the
1606
         * specified property, will not clear the image attachments already set, but the new action (or objects)
1607
         * will have no reference to the existing attachments.</p>
1608
         * <p/>
1609
         * <p>In order for the images to be provided to the Facebook application as part of the app call, the
1610
         * NativeAppCallContentProvider must be specified correctly in the application's AndroidManifest.xml.</p>
1611
         *
1612
         * @param objectProperty  the name of a property on the action that corresponds to an Open Graph object;
1613
         *                        the object must be marked as a new object to be created
1614
         *                        (i.e., {@link com.facebook.model.OpenGraphObject#getCreateObject()} must return
1615
         *                        true) or an exception will be thrown
1616
         * @param objectProperty  the name of a property on the action that corresponds to an Open Graph object
1617
         * @param bitmaps         a list of Files containing bitmaps to be uploaded and attached to the Open Graph object
1618
         * @param isUserGenerated if true, specifies that the user_generated flag should be set for these images
1619
         * @return this instance of the builder
1620
         */
1621
        public CONCRETE setImageAttachmentsForObject(String objectProperty, List<Bitmap> bitmaps,
1622
                boolean isUserGenerated) {
1623
            Validate.notNull(objectProperty, "objectProperty");
1624
            Validate.containsNoNulls(bitmaps, "bitmaps");
1625
            if (action == null) {
1626
                throw new FacebookException("Can not set attachments prior to setting action.");
1627
            }
1628
 
1629
            List<String> attachmentUrls = addImageAttachments(bitmaps);
1630
            updateObjectAttachmentUrls(objectProperty, attachmentUrls, isUserGenerated);
1631
 
1632
            @SuppressWarnings("unchecked")
1633
            CONCRETE result = (CONCRETE) this;
1634
            return result;
1635
        }
1636
 
1637
        /**
1638
         * <p>Specifies a list of images for an Open Graph object referenced by the action that should be uploaded
1639
         * prior to publishing the action. The images will not be marked as user-generated.
1640
         * The action must already have been set prior to calling this method, and
1641
         * the action must have a GraphObject-valued property with the specified property name. This method will
1642
         * generate unique names for the image attachments and update the graph object to refer to these
1643
         * attachments. Note that calling setObject again after calling this method, or modifying the value of the
1644
         * specified property, will not clear the image attachments already set, but the new action (or objects)
1645
         * will have no reference to the existing attachments.</p>
1646
         * <p/>
1647
         * <p>In order for the images to be provided to the Facebook application as part of the app call, the
1648
         * NativeAppCallContentProvider must be specified correctly in the application's AndroidManifest.xml.</p>
1649
         *
1650
         * @param objectProperty the name of a property on the action that corresponds to an Open Graph object;
1651
         *                       the object must be marked as a new object to be created
1652
         *                       (i.e., {@link com.facebook.model.OpenGraphObject#getCreateObject()} must return
1653
         *                       true) or an exception will be thrown
1654
         * @param bitmapFiles    a list of Bitmaps to be uploaded and attached to the Open Graph object
1655
         * @return this instance of the builder
1656
         */
1657
        public CONCRETE setImageAttachmentFilesForObject(String objectProperty,
1658
                List<File> bitmapFiles) {
1659
            return setImageAttachmentFilesForObject(objectProperty, bitmapFiles, false);
1660
        }
1661
 
1662
        /**
1663
         * <p>Specifies a list of images for an Open Graph object referenced by the action that should be uploaded
1664
         * prior to publishing the action. The images may be marked as being
1665
         * user-generated -- refer to
1666
         * <a href="https://developers.facebook.com/docs/opengraph/howtos/adding-photos-to-stories/">this article</a>
1667
         * for more information.
1668
         * The action must already have been set prior to calling this method, and
1669
         * the action must have a GraphObject-valued property with the specified property name. This method will
1670
         * generate unique names for the image attachments and update the graph object to refer to these
1671
         * attachments. Note that calling setObject again after calling this method, or modifying the value of the
1672
         * specified property, will not clear the image attachments already set, but the new action (or objects)
1673
         * will have no reference to the existing attachments.</p>
1674
         * <p/>
1675
         * <p>In order for the images to be provided to the Facebook application as part of the app call, the
1676
         * NativeAppCallContentProvider must be specified correctly in the application's AndroidManifest.xml.</p>
1677
         *
1678
         * @param objectProperty  the name of a property on the action that corresponds to an Open Graph object;
1679
         *                        the object must be marked as a new object to be created
1680
         *                        (i.e., {@link com.facebook.model.OpenGraphObject#getCreateObject()} must return
1681
         *                        true) or an exception will be thrown
1682
         * @param bitmapFiles     a list of Bitmaps to be uploaded and attached to the Open Graph object
1683
         * @param isUserGenerated if true, specifies that the user_generated flag should be set for these images
1684
         * @return this instance of the builder
1685
         */
1686
        public CONCRETE setImageAttachmentFilesForObject(String objectProperty,
1687
                List<File> bitmapFiles, boolean isUserGenerated) {
1688
            Validate.notNull(objectProperty, "objectProperty");
1689
            Validate.containsNoNulls(bitmapFiles, "bitmapFiles");
1690
            if (action == null) {
1691
                throw new FacebookException("Can not set attachments prior to setting action.");
1692
            }
1693
 
1694
            List<String> attachmentUrls = addImageAttachmentFiles(bitmapFiles);
1695
            updateObjectAttachmentUrls(objectProperty, attachmentUrls, isUserGenerated);
1696
 
1697
            @SuppressWarnings("unchecked")
1698
            CONCRETE result = (CONCRETE) this;
1699
            return result;
1700
        }
1701
 
1702
        void updateObjectAttachmentUrls(String objectProperty, List<String> attachmentUrls, boolean isUserGenerated) {
1703
            final OpenGraphObject object;
1704
            try {
1705
                object = action.getPropertyAs(objectProperty, OpenGraphObject.class);
1706
                if (object == null) {
1707
                    throw new IllegalArgumentException("Action does not contain a property '" + objectProperty + "'");
1708
                }
1709
            } catch (FacebookGraphObjectException exception) {
1710
                throw new IllegalArgumentException("Property '" + objectProperty + "' is not a graph object");
1711
            }
1712
            if (!object.getCreateObject()) {
1713
                throw new IllegalArgumentException(
1714
                        "The Open Graph object in '" + objectProperty + "' is not marked for creation");
1715
            }
1716
 
1717
            GraphObjectList<GraphObject> attachments = object.getImage();
1718
            if (attachments == null) {
1719
                attachments = GraphObject.Factory.createList(GraphObject.class);
1720
            }
1721
            for (String url : attachmentUrls) {
1722
                GraphObject graphObject = GraphObject.Factory.create();
1723
                graphObject.setProperty(NativeProtocol.IMAGE_URL_KEY, url);
1724
                if (isUserGenerated) {
1725
                    graphObject.setProperty(NativeProtocol.IMAGE_USER_GENERATED_KEY, true);
1726
                }
1727
                attachments.add(graphObject);
1728
            }
1729
            object.setImage(attachments);
1730
        }
1731
 
1732
        @Override
1733
        protected Bundle setBundleExtras(Bundle extras) {
1734
            putExtra(extras, NativeProtocol.EXTRA_PREVIEW_PROPERTY_NAME, previewPropertyName);
1735
            putExtra(extras, NativeProtocol.EXTRA_ACTION_TYPE, actionType);
1736
            extras.putBoolean(NativeProtocol.EXTRA_DATA_FAILURES_FATAL, dataErrorsFatal);
1737
 
1738
            JSONObject jsonAction = action.getInnerJSONObject();
1739
            jsonAction = flattenChildrenOfGraphObject(jsonAction);
1740
 
1741
            String jsonString = jsonAction.toString();
1742
            putExtra(extras, NativeProtocol.EXTRA_ACTION, jsonString);
1743
 
1744
            return extras;
1745
        }
1746
 
1747
        @Override
1748
        protected Bundle getMethodArguments() {
1749
            Bundle methodArgs = new Bundle();
1750
 
1751
            putExtra(methodArgs, NativeProtocol.METHOD_ARGS_PREVIEW_PROPERTY_NAME, previewPropertyName);
1752
            putExtra(methodArgs, NativeProtocol.METHOD_ARGS_ACTION_TYPE, actionType);
1753
            methodArgs.putBoolean(NativeProtocol.METHOD_ARGS_DATA_FAILURES_FATAL, dataErrorsFatal);
1754
 
1755
            JSONObject jsonAction = action.getInnerJSONObject();
1756
            jsonAction = flattenChildrenOfGraphObject(jsonAction);
1757
 
1758
            String jsonString = jsonAction.toString();
1759
            putExtra(methodArgs, NativeProtocol.METHOD_ARGS_ACTION, jsonString);
1760
 
1761
            return methodArgs;
1762
        }
1763
 
1764
        private JSONObject flattenChildrenOfGraphObject(JSONObject graphObject) {
1765
            try {
1766
                // Clone the existing object to avoid modifying it from under the caller.
1767
                graphObject = new JSONObject(graphObject.toString());
1768
 
1769
                @SuppressWarnings("unchecked")
1770
                Iterator<String> keys = graphObject.keys();
1771
                while (keys.hasNext()) {
1772
                    String key = keys.next();
1773
                    // The "image" property should not be flattened
1774
                    if (!key.equalsIgnoreCase("image")) {
1775
                        Object object = graphObject.get(key);
1776
 
1777
                        object = flattenObject(object);
1778
                        graphObject.put(key, object);
1779
                    }
1780
                }
1781
 
1782
                return graphObject;
1783
            } catch (JSONException e) {
1784
                throw new FacebookException(e);
1785
            }
1786
        }
1787
 
1788
        private Object flattenObject(Object object) throws JSONException {
1789
            if (object == null) {
1790
                return null;
1791
            }
1792
 
1793
            if (object instanceof JSONObject) {
1794
                JSONObject jsonObject = (JSONObject) object;
1795
 
1796
                // Don't flatten objects that are marked as create_object.
1797
                if (jsonObject.optBoolean(NativeProtocol.OPEN_GRAPH_CREATE_OBJECT_KEY)) {
1798
                    return object;
1799
                }
1800
                if (jsonObject.has("id")) {
1801
                    return jsonObject.getString("id");
1802
                } else if (jsonObject.has("url")) {
1803
                    return jsonObject.getString("url");
1804
                }
1805
            } else if (object instanceof JSONArray) {
1806
                JSONArray jsonArray = (JSONArray) object;
1807
                JSONArray newArray = new JSONArray();
1808
                int length = jsonArray.length();
1809
 
1810
                for (int i = 0; i < length; ++i) {
1811
                    newArray.put(flattenObject(jsonArray.get(i)));
1812
                }
1813
 
1814
                return newArray;
1815
            }
1816
 
1817
            return object;
1818
        }
1819
    }
1820
 
1821
    /**
1822
     * Provides a builder which can construct a FacebookDialog instance suitable for presenting the native
1823
     * Open Graph action publish dialog. This builder allows the caller to specify binary images for both the
1824
     * action and any Open Graph objects to be created prior to publishing the action.
1825
     * This builder will throw an exception if the Facebook application is not installed, so it
1826
     * should only be used if {@link FacebookDialog#canPresentOpenGraphActionDialog(android.content.Context,
1827
     * com.facebook.widget.FacebookDialog.OpenGraphActionDialogFeature...)} indicates the capability is available.
1828
     */
1829
    public static class OpenGraphActionDialogBuilder extends OpenGraphDialogBuilderBase<OpenGraphActionDialogBuilder> {
1830
        /**
1831
         * Constructor.
1832
         *
1833
         * @param activity            the Activity which is presenting the native Open Graph action publish dialog;
1834
         *                            must not be null
1835
         * @param action              the Open Graph action to be published, which must contain a reference to at least one
1836
         *                            Open Graph object with the property name specified by setPreviewPropertyName; the action
1837
         *                            must have had its type specified via the {@link OpenGraphAction#setType(String)} method
1838
         * @param actionType          the type of the Open Graph action to be published, which should be the namespace-qualified
1839
         *                            name of the action type (e.g., "myappnamespace:myactiontype"); this will override the type
1840
         *                            of the action passed in.
1841
         * @param previewPropertyName the name of a property on the Open Graph action that contains the
1842
         *                            Open Graph object which will be displayed as a preview to the user
1843
         */
1844
        @Deprecated
1845
        public OpenGraphActionDialogBuilder(Activity activity, OpenGraphAction action, String actionType,
1846
                String previewPropertyName) {
1847
            super(activity, action, actionType, previewPropertyName);
1848
        }
1849
 
1850
        /**
1851
         * Constructor.
1852
         *
1853
         * @param activity            the Activity which is presenting the native Open Graph action publish dialog;
1854
         *                            must not be null
1855
         * @param action              the Open Graph action to be published, which must contain a reference to at least one
1856
         *                            Open Graph object with the property name specified by setPreviewPropertyName; the action
1857
         *                            must have had its type specified via the {@link OpenGraphAction#setType(String)} method
1858
         * @param previewPropertyName the name of a property on the Open Graph action that contains the
1859
         *                            Open Graph object which will be displayed as a preview to the user
1860
         */
1861
        public OpenGraphActionDialogBuilder(Activity activity, OpenGraphAction action, String previewPropertyName) {
1862
            super(activity, action, previewPropertyName);
1863
        }
1864
 
1865
        @Override
1866
        protected EnumSet<? extends DialogFeature> getDialogFeatures() {
1867
            return EnumSet.of(OpenGraphActionDialogFeature.OG_ACTION_DIALOG);
1868
        }
1869
    }
1870
 
1871
    /**
1872
     * Provides a builder which can construct a FacebookDialog instance suitable for presenting the native
1873
     * Open Graph action message dialog. This builder allows the caller to specify binary images for both the
1874
     * action and any Open Graph objects to be created prior to publishing the action.
1875
     * This builder will throw an exception if the Facebook application is not installed, so it
1876
     * should only be used if {@link FacebookDialog#canPresentOpenGraphMessageDialog(android.content.Context,
1877
     * com.facebook.widget.FacebookDialog.OpenGraphMessageDialogFeature...)} indicates the capability is available.
1878
     */
1879
    public static class OpenGraphMessageDialogBuilder extends OpenGraphDialogBuilderBase<OpenGraphMessageDialogBuilder> {
1880
        /**
1881
         * Constructor.
1882
         *
1883
         * @param activity            the Activity which is presenting the native Open Graph action message dialog;
1884
         *                            must not be null
1885
         * @param action              the Open Graph action to be sent, which must contain a reference to at least one
1886
         *                            Open Graph object with the property name specified by setPreviewPropertyName; the action
1887
         *                            must have had its type specified via the {@link OpenGraphAction#setType(String)} method
1888
         * @param previewPropertyName the name of a property on the Open Graph action that contains the
1889
         *                            Open Graph object which will be displayed as a preview to the user
1890
         */
1891
        public OpenGraphMessageDialogBuilder(Activity activity, OpenGraphAction action, String previewPropertyName) {
1892
            super(activity, action, previewPropertyName);
1893
        }
1894
 
1895
        @Override
1896
        protected EnumSet<? extends DialogFeature> getDialogFeatures() {
1897
            return EnumSet.of(OpenGraphMessageDialogFeature.OG_MESSAGE_DIALOG);
1898
        }
1899
    }
1900
 
1901
    /**
1902
     * Encapsulates information about a call being made to the Facebook application for Android. A unique String
1903
     * call ID is used to track calls through their lifecycle.
1904
     */
1905
    public static class PendingCall implements Parcelable {
1906
        private UUID callId;
1907
        private Intent requestIntent;
1908
        private int requestCode;
1909
 
1910
        /**
1911
         * Constructor.
1912
         *
1913
         * @param requestCode the request code for this app call
1914
         */
1915
        public PendingCall(int requestCode) {
1916
            callId = UUID.randomUUID();
1917
            this.requestCode = requestCode;
1918
        }
1919
 
1920
        private PendingCall(Parcel in) {
1921
            callId = UUID.fromString(in.readString());
1922
            requestIntent = in.readParcelable(null);
1923
            requestCode = in.readInt();
1924
        }
1925
 
1926
        private void setRequestIntent(Intent requestIntent) {
1927
            this.requestIntent = requestIntent;
1928
        }
1929
 
1930
        /**
1931
         * Returns the Intent that was used to initiate this call to the Facebook application.
1932
         *
1933
         * @return the Intent
1934
         */
1935
        public Intent getRequestIntent() {
1936
            return requestIntent;
1937
        }
1938
 
1939
        /**
1940
         * Returns the unique ID of this call to the Facebook application.
1941
         *
1942
         * @return the unique ID
1943
         */
1944
        public UUID getCallId() {
1945
            return callId;
1946
        }
1947
 
1948
        private void setRequestCode(int requestCode) {
1949
            this.requestCode = requestCode;
1950
        }
1951
 
1952
        /**
1953
         * Gets the request code for this call.
1954
         *
1955
         * @return the request code that will be passed to handleActivityResult upon completion.
1956
         */
1957
        public int getRequestCode() {
1958
            return requestCode;
1959
        }
1960
 
1961
        @Override
1962
        public int describeContents() {
1963
            return 0;
1964
        }
1965
 
1966
        @Override
1967
        public void writeToParcel(Parcel parcel, int i) {
1968
            parcel.writeString(callId.toString());
1969
            parcel.writeParcelable(requestIntent, 0);
1970
            parcel.writeInt(requestCode);
1971
        }
1972
 
1973
        public static final Creator<PendingCall> CREATOR
1974
                = new Creator<PendingCall>() {
1975
            public PendingCall createFromParcel(Parcel in) {
1976
                return new PendingCall(in);
1977
            }
1978
 
1979
            public PendingCall[] newArray(int size) {
1980
                return new PendingCall[size];
1981
            }
1982
        };
1983
    }
1984
}