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;
18
 
19
import android.app.Activity;
20
import android.content.BroadcastReceiver;
21
import android.content.Context;
22
import android.content.Intent;
23
import android.content.IntentFilter;
24
import android.os.Bundle;
25
import android.support.v4.content.LocalBroadcastManager;
26
import android.util.Log;
27
import com.facebook.internal.LikeActionController;
28
import com.facebook.internal.NativeProtocol;
29
import com.facebook.internal.PendingCallStore;
30
import com.facebook.internal.Utility;
31
import com.facebook.widget.FacebookDialog;
32
 
33
import java.util.UUID;
34
 
35
/**
36
 * This class helps to create, automatically open (if applicable), save, and
37
 * restore the Active Session in a way that is similar to Android UI lifecycles.
38
 * <p>
39
 * When using this class, clients MUST call all the public methods from the
40
 * respective methods in either an Activity or Fragment. Failure to call all the
41
 * methods can result in improperly initialized or uninitialized Sessions.
42
 * <p>
43
 * This class should also be used by Activities that will be displaying native dialogs
44
 * provided by the Facebook application, in order to handle processing of the activity
45
 * results generated by those dialogs.
46
 */
47
public class UiLifecycleHelper {
48
    private static final String DIALOG_CALL_ID_SAVE_KEY =
49
            "com.facebook.UiLifecycleHelper.pendingFacebookDialogCallKey";
50
 
51
    private final static String ACTIVITY_NULL_MESSAGE = "activity cannot be null";
52
 
53
    private final Activity activity;
54
    private final Session.StatusCallback callback;
55
    private final BroadcastReceiver receiver;
56
    private final LocalBroadcastManager broadcastManager;
57
    // Members related to handling FacebookDialog calls
58
    private UUID pendingFacebookDialogCallId;
59
    private PendingCallStore pendingFacebookDialogCallStore;
60
 
61
    private AppEventsLogger appEventsLogger;
62
 
63
    /**
64
     * Creates a new UiLifecycleHelper.
65
     *
66
     * @param activity the Activity associated with the helper. If calling from a Fragment,
67
     *                 use {@link android.support.v4.app.Fragment#getActivity()}
68
     * @param callback the callback for Session status changes, can be null
69
     */
70
    public UiLifecycleHelper(Activity activity, Session.StatusCallback callback) {
71
        if (activity == null) {
72
            throw new IllegalArgumentException(ACTIVITY_NULL_MESSAGE);
73
        }
74
 
75
        this.activity = activity;
76
        this.callback = callback;
77
        this.receiver = new ActiveSessionBroadcastReceiver();
78
        this.broadcastManager = LocalBroadcastManager.getInstance(activity);
79
        this.pendingFacebookDialogCallStore = PendingCallStore.getInstance();
80
 
81
        // initialize SDK
82
        Settings.sdkInitialize(activity);
83
    }
84
 
85
    /**
86
     * To be called from an Activity or Fragment's onCreate method.
87
     *
88
     * @param savedInstanceState the previously saved state
89
     */
90
    public void onCreate(Bundle savedInstanceState) {
91
        Session session = Session.getActiveSession();
92
        if (session == null) {
93
            if (savedInstanceState != null) {
94
                session = Session.restoreSession(activity, null, callback, savedInstanceState);
95
            }
96
            if (session == null) {
97
                session = new Session(activity);
98
            }
99
            Session.setActiveSession(session);
100
        }
101
        if (savedInstanceState != null) {
102
            String callIdString = savedInstanceState.getString(DIALOG_CALL_ID_SAVE_KEY);
103
            if (callIdString != null) {
104
                pendingFacebookDialogCallId = UUID.fromString(callIdString);
105
            }
106
            pendingFacebookDialogCallStore.restoreFromSavedInstanceState(savedInstanceState);
107
        }
108
    }
109
 
110
    /**
111
     * To be called from an Activity or Fragment's onResume method.
112
     */
113
    public void onResume() {
114
        Session session = Session.getActiveSession();
115
        if (session != null) {
116
            if (callback != null) {
117
                session.addCallback(callback);
118
            }
119
            if (SessionState.CREATED_TOKEN_LOADED.equals(session.getState())) {
120
                session.openForRead(null);
121
            }
122
        }
123
 
124
        // add the broadcast receiver
125
        IntentFilter filter = new IntentFilter();
126
        filter.addAction(Session.ACTION_ACTIVE_SESSION_SET);
127
        filter.addAction(Session.ACTION_ACTIVE_SESSION_UNSET);
128
 
129
        // Add a broadcast receiver to listen to when the active Session
130
        // is set or unset, and add/remove our callback as appropriate
131
        broadcastManager.registerReceiver(receiver, filter);
132
    }
133
 
134
    /**
135
     * To be called from an Activity or Fragment's onActivityResult method.
136
     *
137
     * @param requestCode the request code
138
     * @param resultCode the result code
139
     * @param data the result data
140
     */
141
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
142
        onActivityResult(requestCode, resultCode, data, null);
143
    }
144
 
145
    /**
146
     * To be called from an Activity or Fragment's onActivityResult method, when the results of a FacebookDialog
147
     * call are expected.
148
     *
149
     * @param requestCode the request code
150
     * @param resultCode the result code
151
     * @param data the result data
152
     * @param facebookDialogCallback the callback for handling FacebookDialog results, can be null
153
     */
154
    public void onActivityResult(int requestCode, int resultCode, Intent data,
155
                FacebookDialog.Callback facebookDialogCallback) {
156
        Session session = Session.getActiveSession();
157
        if (session != null) {
158
            session.onActivityResult(activity, requestCode, resultCode, data);
159
        }
160
 
161
        if (LikeActionController.handleOnActivityResult(activity, requestCode, resultCode, data)) {
162
            return;
163
        }
164
 
165
        handleFacebookDialogActivityResult(requestCode, resultCode, data, facebookDialogCallback);
166
    }
167
 
168
    /**
169
     * To be called from an Activity or Fragment's onSaveInstanceState method.
170
     *
171
     * @param outState the bundle to save state in
172
     */
173
    public void onSaveInstanceState(Bundle outState) {
174
        Session.saveSession(Session.getActiveSession(), outState);
175
        if (pendingFacebookDialogCallId != null) {
176
            outState.putString(DIALOG_CALL_ID_SAVE_KEY, pendingFacebookDialogCallId.toString());
177
        }
178
        pendingFacebookDialogCallStore.saveInstanceState(outState);
179
    }
180
 
181
    /**
182
     * To be called from an Activity or Fragment's onPause method.
183
     */
184
    public void onPause() {
185
        // remove the broadcast receiver
186
        broadcastManager.unregisterReceiver(receiver);
187
 
188
        if (callback != null) {
189
            Session session = Session.getActiveSession();
190
            if (session != null) {
191
                session.removeCallback(callback);
192
            }
193
        }
194
    }
195
 
196
    /**
197
     * To be called from an Activity or Fragment's onStop method.
198
     */
199
    public void onStop() {
200
        AppEventsLogger.onContextStop();
201
    }
202
 
203
    /**
204
     * To be called from an Activity or Fragment's onDestroy method.
205
     */
206
    public void onDestroy() {
207
    }
208
 
209
    /**
210
     * Register that we are expecting results from a call to the Facebook application (e.g., from a native
211
     * dialog provided by the Facebook app). Activity results forwarded to onActivityResults will be parsed
212
     * and handled if they correspond to this call. Only a single pending FacebookDialog call can be tracked
213
     * at a time; attempting to track another one will cancel the first one.
214
     * @param appCall an PendingCall object containing the call ID
215
     */
216
    public void trackPendingDialogCall(FacebookDialog.PendingCall pendingCall) {
217
        if (pendingFacebookDialogCallId != null) {
218
            // If one is already pending, cancel it; we don't allow multiple pending calls.
219
            Log.i("Facebook", "Tracking new app call while one is still pending; canceling pending call.");
220
            cancelPendingAppCall(null);
221
        }
222
        if (pendingCall != null){
223
            pendingFacebookDialogCallId = pendingCall.getCallId();
224
            pendingFacebookDialogCallStore.trackPendingCall(pendingCall);
225
        }
226
    }
227
 
228
    /**
229
     * Retrieves an instance of AppEventsLogger that can be used for the current Session, if any. Different
230
     * instances may be returned if the current Session changes, so this value should not be cached for long
231
     * periods of time -- always call getAppEventsLogger to get the right logger for the current Session. If
232
     * no Session is currently available, this method will return null.
233
     *
234
     * To ensure delivery of app events across Activity lifecycle events, calling Activities should be sure to
235
     * call the onStop method.
236
     *
237
     * @return an AppEventsLogger to use for logging app events
238
     */
239
    public AppEventsLogger getAppEventsLogger() {
240
        Session session = Session.getActiveSession();
241
        if (session == null) {
242
            return null;
243
        }
244
 
245
        if (appEventsLogger == null || !appEventsLogger.isValidForSession(session)) {
246
            if (appEventsLogger != null) {
247
                // Pretend we got stopped so the old logger will persist its results now, in case we get stopped
248
                // before events get flushed.
249
                AppEventsLogger.onContextStop();
250
            }
251
            appEventsLogger = AppEventsLogger.newLogger(activity, session);
252
        }
253
 
254
        return appEventsLogger;
255
    }
256
 
257
    /**
258
     * The BroadcastReceiver implementation that either adds or removes the callback
259
     * from the active Session object as it's SET or UNSET.
260
     */
261
    private class ActiveSessionBroadcastReceiver extends BroadcastReceiver {
262
        @Override
263
        public void onReceive(Context context, Intent intent) {
264
            if (Session.ACTION_ACTIVE_SESSION_SET.equals(intent.getAction())) {
265
                Session session = Session.getActiveSession();
266
                if (session != null && callback != null) {
267
                    session.addCallback(callback);
268
                }
269
            } else if (Session.ACTION_ACTIVE_SESSION_UNSET.equals(intent.getAction())) {
270
                Session session = Session.getActiveSession();
271
                if (session != null && callback != null) {
272
                    session.removeCallback(callback);
273
                }
274
            }
275
        }
276
    }
277
 
278
    private boolean handleFacebookDialogActivityResult(int requestCode, int resultCode, Intent data,
279
            FacebookDialog.Callback facebookDialogCallback) {
280
        if (pendingFacebookDialogCallId == null) {
281
            return false;
282
        }
283
        FacebookDialog.PendingCall pendingCall =
284
                pendingFacebookDialogCallStore.getPendingCallById(pendingFacebookDialogCallId);
285
        if (pendingCall == null || pendingCall.getRequestCode() != requestCode) {
286
            return false;
287
        }
288
 
289
        if (data == null) {
290
            // We understand the request code, but have no Intent. This can happen if the called Activity crashes
291
            // before it can be started; we treat this as a cancellation because we have no other information.
292
            cancelPendingAppCall(facebookDialogCallback);
293
            return true;
294
        }
295
 
296
        UUID callId = NativeProtocol.getCallIdFromIntent(data);
297
 
298
        // Was this result for the call we are waiting on?
299
        if (callId != null && pendingFacebookDialogCallId.equals(callId)) {
300
            // Yes, we can handle it normally.
301
            FacebookDialog.handleActivityResult(activity, pendingCall, requestCode, data,
302
                    facebookDialogCallback);
303
        } else {
304
            // No, send a cancellation error to the pending call and ignore the result, because we
305
            // don't know what to do with it.
306
            cancelPendingAppCall(facebookDialogCallback);
307
        }
308
 
309
        stopTrackingPendingAppCall();
310
        return true;
311
    }
312
 
313
    private void cancelPendingAppCall(FacebookDialog.Callback facebookDialogCallback) {
314
        if (pendingFacebookDialogCallId == null) {
315
            return;
316
        }
317
 
318
        FacebookDialog.PendingCall pendingCall =
319
                pendingFacebookDialogCallStore.getPendingCallById(pendingFacebookDialogCallId);
320
        if (pendingCall == null) {
321
            return;
322
        }
323
 
324
        if (facebookDialogCallback != null) {
325
            Intent pendingIntent = pendingCall.getRequestIntent();
326
 
327
            Intent cancelIntent = new Intent();
328
            cancelIntent.putExtra(NativeProtocol.EXTRA_PROTOCOL_CALL_ID,
329
                    pendingIntent.getStringExtra(NativeProtocol.EXTRA_PROTOCOL_CALL_ID));
330
            cancelIntent.putExtra(NativeProtocol.EXTRA_PROTOCOL_ACTION,
331
                    pendingIntent.getStringExtra(NativeProtocol.EXTRA_PROTOCOL_ACTION));
332
            cancelIntent.putExtra(NativeProtocol.EXTRA_PROTOCOL_VERSION,
333
                    pendingIntent.getIntExtra(NativeProtocol.EXTRA_PROTOCOL_VERSION, 0));
334
            cancelIntent.putExtra(NativeProtocol.STATUS_ERROR_TYPE, NativeProtocol.ERROR_UNKNOWN_ERROR);
335
 
336
            FacebookDialog.handleActivityResult(activity, pendingCall,
337
                    pendingCall.getRequestCode(), cancelIntent, facebookDialogCallback);
338
        }
339
 
340
        stopTrackingPendingAppCall();
341
    }
342
 
343
    private void stopTrackingPendingAppCall() {
344
        pendingFacebookDialogCallStore.stopTrackingPendingCall(pendingFacebookDialogCallId);
345
        pendingFacebookDialogCallId = null;
346
    }
347
}