| 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.android;
|
|
|
18 |
|
|
|
19 |
import android.Manifest;
|
|
|
20 |
import android.app.Activity;
|
|
|
21 |
import android.content.*;
|
|
|
22 |
import android.content.pm.PackageInfo;
|
|
|
23 |
import android.content.pm.PackageManager;
|
|
|
24 |
import android.content.pm.PackageManager.NameNotFoundException;
|
|
|
25 |
import android.content.pm.ResolveInfo;
|
|
|
26 |
import android.content.pm.Signature;
|
|
|
27 |
import android.net.Uri;
|
|
|
28 |
import android.os.*;
|
|
|
29 |
import com.facebook.*;
|
|
|
30 |
import com.facebook.Session.StatusCallback;
|
|
|
31 |
|
|
|
32 |
import java.io.FileNotFoundException;
|
|
|
33 |
import java.io.IOException;
|
|
|
34 |
import java.lang.ref.WeakReference;
|
|
|
35 |
import java.net.MalformedURLException;
|
|
|
36 |
import java.util.Arrays;
|
|
|
37 |
import java.util.Collections;
|
|
|
38 |
import java.util.List;
|
|
|
39 |
|
|
|
40 |
/**
|
|
|
41 |
* THIS CLASS SHOULD BE CONSIDERED DEPRECATED.
|
|
|
42 |
* <p/>
|
|
|
43 |
* All public members of this class are intentionally deprecated.
|
|
|
44 |
* New code should instead use
|
|
|
45 |
* {@link Session} to manage session state,
|
|
|
46 |
* {@link Request} to make API requests, and
|
|
|
47 |
* {@link com.facebook.widget.WebDialog} to make dialog requests.
|
|
|
48 |
* <p/>
|
|
|
49 |
* Adding @Deprecated to this class causes warnings in other deprecated classes
|
|
|
50 |
* that reference this one. That is the only reason this entire class is not
|
|
|
51 |
* deprecated.
|
|
|
52 |
*
|
|
|
53 |
* @devDocDeprecated
|
|
|
54 |
*/
|
|
|
55 |
public class Facebook {
|
|
|
56 |
|
|
|
57 |
// Strings used in the authorization flow
|
|
|
58 |
@Deprecated
|
|
|
59 |
public static final String REDIRECT_URI = "fbconnect://success";
|
|
|
60 |
@Deprecated
|
|
|
61 |
public static final String CANCEL_URI = "fbconnect://cancel";
|
|
|
62 |
@Deprecated
|
|
|
63 |
public static final String TOKEN = "access_token";
|
|
|
64 |
@Deprecated
|
|
|
65 |
public static final String EXPIRES = "expires_in";
|
|
|
66 |
@Deprecated
|
|
|
67 |
public static final String SINGLE_SIGN_ON_DISABLED = "service_disabled";
|
|
|
68 |
|
|
|
69 |
@Deprecated
|
|
|
70 |
public static final Uri ATTRIBUTION_ID_CONTENT_URI =
|
|
|
71 |
Uri.parse("content://com.facebook.katana.provider.AttributionIdProvider");
|
|
|
72 |
@Deprecated
|
|
|
73 |
public static final String ATTRIBUTION_ID_COLUMN_NAME = "aid";
|
|
|
74 |
|
|
|
75 |
@Deprecated
|
|
|
76 |
public static final int FORCE_DIALOG_AUTH = -1;
|
|
|
77 |
|
|
|
78 |
private static final String LOGIN = "oauth";
|
|
|
79 |
|
|
|
80 |
// Used as default activityCode by authorize(). See authorize() below.
|
|
|
81 |
private static final int DEFAULT_AUTH_ACTIVITY_CODE = 32665;
|
|
|
82 |
|
|
|
83 |
// Facebook server endpoints: may be modified in a subclass for testing
|
|
|
84 |
@Deprecated
|
|
|
85 |
protected static String DIALOG_BASE_URL = "https://m.facebook.com/dialog/";
|
|
|
86 |
@Deprecated
|
|
|
87 |
protected static String GRAPH_BASE_URL = "https://graph.facebook.com/";
|
|
|
88 |
@Deprecated
|
|
|
89 |
protected static String RESTSERVER_URL = "https://api.facebook.com/restserver.php";
|
|
|
90 |
|
|
|
91 |
private final Object lock = new Object();
|
|
|
92 |
|
|
|
93 |
private String accessToken = null;
|
|
|
94 |
private long accessExpiresMillisecondsAfterEpoch = 0;
|
|
|
95 |
private long lastAccessUpdateMillisecondsAfterEpoch = 0;
|
|
|
96 |
private String mAppId;
|
|
|
97 |
|
|
|
98 |
private Activity pendingAuthorizationActivity;
|
|
|
99 |
private String[] pendingAuthorizationPermissions;
|
|
|
100 |
private Session pendingOpeningSession;
|
|
|
101 |
|
|
|
102 |
private volatile Session session; // must synchronize this.sync to write
|
|
|
103 |
private boolean sessionInvalidated; // must synchronize this.sync to access
|
|
|
104 |
private SetterTokenCachingStrategy tokenCache;
|
|
|
105 |
private volatile Session userSetSession;
|
|
|
106 |
|
|
|
107 |
// If the last time we extended the access token was more than 24 hours ago
|
|
|
108 |
// we try to refresh the access token again.
|
|
|
109 |
final private static long REFRESH_TOKEN_BARRIER = 24L * 60L * 60L * 1000L;
|
|
|
110 |
|
|
|
111 |
/**
|
|
|
112 |
* Constructor for Facebook object.
|
|
|
113 |
*
|
|
|
114 |
* @param appId
|
|
|
115 |
* Your Facebook application ID. Found at
|
|
|
116 |
* www.facebook.com/developers/apps.php.
|
|
|
117 |
*/
|
|
|
118 |
@Deprecated
|
|
|
119 |
public Facebook(String appId) {
|
|
|
120 |
if (appId == null) {
|
|
|
121 |
throw new IllegalArgumentException("You must specify your application ID when instantiating "
|
|
|
122 |
+ "a Facebook object. See README for details.");
|
|
|
123 |
}
|
|
|
124 |
mAppId = appId;
|
|
|
125 |
}
|
|
|
126 |
|
|
|
127 |
/**
|
|
|
128 |
* Default authorize method. Grants only basic permissions.
|
|
|
129 |
* <p/>
|
|
|
130 |
* See authorize() below for @params.
|
|
|
131 |
* <p/>
|
|
|
132 |
* This method is deprecated. See {@link Facebook} and {@link Session} for more info.
|
|
|
133 |
*/
|
|
|
134 |
@Deprecated
|
|
|
135 |
public void authorize(Activity activity, final DialogListener listener) {
|
|
|
136 |
authorize(activity, new String[]{}, DEFAULT_AUTH_ACTIVITY_CODE, SessionLoginBehavior.SSO_WITH_FALLBACK,
|
|
|
137 |
listener);
|
|
|
138 |
}
|
|
|
139 |
|
|
|
140 |
/**
|
|
|
141 |
* Authorize method that grants custom permissions.
|
|
|
142 |
* <p/>
|
|
|
143 |
* See authorize() below for @params.
|
|
|
144 |
* <p/>
|
|
|
145 |
* This method is deprecated. See {@link Facebook} and {@link Session} for more info.
|
|
|
146 |
*/
|
|
|
147 |
@Deprecated
|
|
|
148 |
public void authorize(Activity activity, String[] permissions, final DialogListener listener) {
|
|
|
149 |
authorize(activity, permissions, DEFAULT_AUTH_ACTIVITY_CODE, SessionLoginBehavior.SSO_WITH_FALLBACK, listener);
|
|
|
150 |
}
|
|
|
151 |
|
|
|
152 |
/**
|
|
|
153 |
* Full authorize method.
|
|
|
154 |
* <p/>
|
|
|
155 |
* Starts either an Activity or a dialog which prompts the user to log in to
|
|
|
156 |
* Facebook and grant the requested permissions to the given application.
|
|
|
157 |
* <p/>
|
|
|
158 |
* This method will, when possible, use Facebook's single sign-on for
|
|
|
159 |
* Android to obtain an access token. This involves proxying a call through
|
|
|
160 |
* the Facebook for Android stand-alone application, which will handle the
|
|
|
161 |
* authentication flow, and return an OAuth access token for making API
|
|
|
162 |
* calls.
|
|
|
163 |
* <p/>
|
|
|
164 |
* Because this process will not be available for all users, if single
|
|
|
165 |
* sign-on is not possible, this method will automatically fall back to the
|
|
|
166 |
* OAuth 2.0 User-Agent flow. In this flow, the user credentials are handled
|
|
|
167 |
* by Facebook in an embedded WebView, not by the client application. As
|
|
|
168 |
* such, the dialog makes a network request and renders HTML content rather
|
|
|
169 |
* than a native UI. The access token is retrieved from a redirect to a
|
|
|
170 |
* special URL that the WebView handles.
|
|
|
171 |
* <p/>
|
|
|
172 |
* Note that User credentials could be handled natively using the OAuth 2.0
|
|
|
173 |
* Username and Password Flow, but this is not supported by this SDK.
|
|
|
174 |
* <p/>
|
|
|
175 |
* See http://developers.facebook.com/docs/authentication/ and
|
|
|
176 |
* http://wiki.oauth.net/OAuth-2 for more details.
|
|
|
177 |
* <p/>
|
|
|
178 |
* Note that this method is asynchronous and the callback will be invoked in
|
|
|
179 |
* the original calling thread (not in a background thread).
|
|
|
180 |
* <p/>
|
|
|
181 |
* Also note that requests may be made to the API without calling authorize
|
|
|
182 |
* first, in which case only public information is returned.
|
|
|
183 |
* <p/>
|
|
|
184 |
* IMPORTANT: Note that single sign-on authentication will not function
|
|
|
185 |
* correctly if you do not include a call to the authorizeCallback() method
|
|
|
186 |
* in your onActivityResult() function! Please see below for more
|
|
|
187 |
* information. single sign-on may be disabled by passing FORCE_DIALOG_AUTH
|
|
|
188 |
* as the activityCode parameter in your call to authorize().
|
|
|
189 |
* <p/>
|
|
|
190 |
* This method is deprecated. See {@link Facebook} and {@link Session} for more info.
|
|
|
191 |
*
|
|
|
192 |
* @param activity
|
|
|
193 |
* The Android activity in which we want to display the
|
|
|
194 |
* authorization dialog.
|
|
|
195 |
* @param permissions
|
|
|
196 |
* A list of permissions required for this application: e.g.
|
|
|
197 |
* "read_stream", "publish_stream", "offline_access", etc. see
|
|
|
198 |
* http://developers.facebook.com/docs/authentication/permissions
|
|
|
199 |
* This parameter should not be null -- if you do not require any
|
|
|
200 |
* permissions, then pass in an empty String array.
|
|
|
201 |
* @param activityCode
|
|
|
202 |
* Single sign-on requires an activity result to be called back
|
|
|
203 |
* to the client application -- if you are waiting on other
|
|
|
204 |
* activities to return data, pass a custom activity code here to
|
|
|
205 |
* avoid collisions. If you would like to force the use of legacy
|
|
|
206 |
* dialog-based authorization, pass FORCE_DIALOG_AUTH for this
|
|
|
207 |
* parameter. Otherwise just omit this parameter and Facebook
|
|
|
208 |
* will use a suitable default. See
|
|
|
209 |
* http://developer.android.com/reference/android/
|
|
|
210 |
* app/Activity.html for more information.
|
|
|
211 |
* @param listener
|
|
|
212 |
* Callback interface for notifying the calling application when
|
|
|
213 |
* the authentication dialog has completed, failed, or been
|
|
|
214 |
* canceled.
|
|
|
215 |
*/
|
|
|
216 |
@Deprecated
|
|
|
217 |
public void authorize(Activity activity, String[] permissions, int activityCode, final DialogListener listener) {
|
|
|
218 |
SessionLoginBehavior behavior = (activityCode >= 0) ? SessionLoginBehavior.SSO_WITH_FALLBACK
|
|
|
219 |
: SessionLoginBehavior.SUPPRESS_SSO;
|
|
|
220 |
|
|
|
221 |
authorize(activity, permissions, activityCode, behavior, listener);
|
|
|
222 |
}
|
|
|
223 |
|
|
|
224 |
/**
|
|
|
225 |
* Full authorize method.
|
|
|
226 |
*
|
|
|
227 |
* Starts either an Activity or a dialog which prompts the user to log in to
|
|
|
228 |
* Facebook and grant the requested permissions to the given application.
|
|
|
229 |
*
|
|
|
230 |
* This method will, when possible, use Facebook's single sign-on for
|
|
|
231 |
* Android to obtain an access token. This involves proxying a call through
|
|
|
232 |
* the Facebook for Android stand-alone application, which will handle the
|
|
|
233 |
* authentication flow, and return an OAuth access token for making API
|
|
|
234 |
* calls.
|
|
|
235 |
*
|
|
|
236 |
* Because this process will not be available for all users, if single
|
|
|
237 |
* sign-on is not possible, this method will automatically fall back to the
|
|
|
238 |
* OAuth 2.0 User-Agent flow. In this flow, the user credentials are handled
|
|
|
239 |
* by Facebook in an embedded WebView, not by the client application. As
|
|
|
240 |
* such, the dialog makes a network request and renders HTML content rather
|
|
|
241 |
* than a native UI. The access token is retrieved from a redirect to a
|
|
|
242 |
* special URL that the WebView handles.
|
|
|
243 |
*
|
|
|
244 |
* Note that User credentials could be handled natively using the OAuth 2.0
|
|
|
245 |
* Username and Password Flow, but this is not supported by this SDK.
|
|
|
246 |
*
|
|
|
247 |
* See http://developers.facebook.com/docs/authentication/ and
|
|
|
248 |
* http://wiki.oauth.net/OAuth-2 for more details.
|
|
|
249 |
*
|
|
|
250 |
* Note that this method is asynchronous and the callback will be invoked in
|
|
|
251 |
* the original calling thread (not in a background thread).
|
|
|
252 |
*
|
|
|
253 |
* Also note that requests may be made to the API without calling authorize
|
|
|
254 |
* first, in which case only public information is returned.
|
|
|
255 |
*
|
|
|
256 |
* IMPORTANT: Note that single sign-on authentication will not function
|
|
|
257 |
* correctly if you do not include a call to the authorizeCallback() method
|
|
|
258 |
* in your onActivityResult() function! Please see below for more
|
|
|
259 |
* information. single sign-on may be disabled by passing FORCE_DIALOG_AUTH
|
|
|
260 |
* as the activityCode parameter in your call to authorize().
|
|
|
261 |
*
|
|
|
262 |
* @param activity
|
|
|
263 |
* The Android activity in which we want to display the
|
|
|
264 |
* authorization dialog.
|
|
|
265 |
* @param permissions
|
|
|
266 |
* A list of permissions required for this application: e.g.
|
|
|
267 |
* "read_stream", "publish_stream", "offline_access", etc. see
|
|
|
268 |
* http://developers.facebook.com/docs/authentication/permissions
|
|
|
269 |
* This parameter should not be null -- if you do not require any
|
|
|
270 |
* permissions, then pass in an empty String array.
|
|
|
271 |
* @param activityCode
|
|
|
272 |
* Single sign-on requires an activity result to be called back
|
|
|
273 |
* to the client application -- if you are waiting on other
|
|
|
274 |
* activities to return data, pass a custom activity code here to
|
|
|
275 |
* avoid collisions. If you would like to force the use of legacy
|
|
|
276 |
* dialog-based authorization, pass FORCE_DIALOG_AUTH for this
|
|
|
277 |
* parameter. Otherwise just omit this parameter and Facebook
|
|
|
278 |
* will use a suitable default. See
|
|
|
279 |
* http://developer.android.com/reference/android/
|
|
|
280 |
* app/Activity.html for more information.
|
|
|
281 |
* @param behavior
|
|
|
282 |
* The {@link SessionLoginBehavior SessionLoginBehavior} that
|
|
|
283 |
* specifies what behaviors should be attempted during
|
|
|
284 |
* authorization.
|
|
|
285 |
* @param listener
|
|
|
286 |
* Callback interface for notifying the calling application when
|
|
|
287 |
* the authentication dialog has completed, failed, or been
|
|
|
288 |
* canceled.
|
|
|
289 |
*/
|
|
|
290 |
private void authorize(Activity activity, String[] permissions, int activityCode,
|
|
|
291 |
SessionLoginBehavior behavior, final DialogListener listener) {
|
|
|
292 |
checkUserSession("authorize");
|
|
|
293 |
pendingOpeningSession = new Session.Builder(activity).
|
|
|
294 |
setApplicationId(mAppId).
|
|
|
295 |
setTokenCachingStrategy(getTokenCache()).
|
|
|
296 |
build();
|
|
|
297 |
pendingAuthorizationActivity = activity;
|
|
|
298 |
pendingAuthorizationPermissions = (permissions != null) ? permissions : new String[0];
|
|
|
299 |
|
|
|
300 |
StatusCallback callback = new StatusCallback() {
|
|
|
301 |
@Override
|
|
|
302 |
public void call(Session callbackSession, SessionState state, Exception exception) {
|
|
|
303 |
// Invoke user-callback.
|
|
|
304 |
onSessionCallback(callbackSession, state, exception, listener);
|
|
|
305 |
}
|
|
|
306 |
};
|
|
|
307 |
|
|
|
308 |
Session.OpenRequest openRequest = new Session.OpenRequest(activity).
|
|
|
309 |
setCallback(callback).
|
|
|
310 |
setLoginBehavior(behavior).
|
|
|
311 |
setRequestCode(activityCode).
|
|
|
312 |
setPermissions(Arrays.asList(pendingAuthorizationPermissions));
|
|
|
313 |
openSession(pendingOpeningSession, openRequest, pendingAuthorizationPermissions.length > 0);
|
|
|
314 |
}
|
|
|
315 |
|
|
|
316 |
private void openSession(Session session, Session.OpenRequest openRequest, boolean isPublish) {
|
|
|
317 |
openRequest.setIsLegacy(true);
|
|
|
318 |
if (isPublish) {
|
|
|
319 |
session.openForPublish(openRequest);
|
|
|
320 |
} else {
|
|
|
321 |
session.openForRead(openRequest);
|
|
|
322 |
}
|
|
|
323 |
}
|
|
|
324 |
|
|
|
325 |
@SuppressWarnings("deprecation")
|
|
|
326 |
private void onSessionCallback(Session callbackSession, SessionState state, Exception exception,
|
|
|
327 |
DialogListener listener) {
|
|
|
328 |
Bundle extras = callbackSession.getAuthorizationBundle();
|
|
|
329 |
|
|
|
330 |
if (state == SessionState.OPENED) {
|
|
|
331 |
Session sessionToClose = null;
|
|
|
332 |
|
|
|
333 |
synchronized (Facebook.this.lock) {
|
|
|
334 |
if (callbackSession != Facebook.this.session) {
|
|
|
335 |
sessionToClose = Facebook.this.session;
|
|
|
336 |
Facebook.this.session = callbackSession;
|
|
|
337 |
Facebook.this.sessionInvalidated = false;
|
|
|
338 |
}
|
|
|
339 |
}
|
|
|
340 |
|
|
|
341 |
if (sessionToClose != null) {
|
|
|
342 |
sessionToClose.close();
|
|
|
343 |
}
|
|
|
344 |
|
|
|
345 |
listener.onComplete(extras);
|
|
|
346 |
} else if (exception != null) {
|
|
|
347 |
if (exception instanceof FacebookOperationCanceledException) {
|
|
|
348 |
listener.onCancel();
|
|
|
349 |
} else if ((exception instanceof FacebookAuthorizationException) && (extras != null)
|
|
|
350 |
&& extras.containsKey(Session.WEB_VIEW_ERROR_CODE_KEY)
|
|
|
351 |
&& extras.containsKey(Session.WEB_VIEW_FAILING_URL_KEY)) {
|
|
|
352 |
DialogError error = new DialogError(exception.getMessage(),
|
|
|
353 |
extras.getInt(Session.WEB_VIEW_ERROR_CODE_KEY),
|
|
|
354 |
extras.getString(Session.WEB_VIEW_FAILING_URL_KEY));
|
|
|
355 |
listener.onError(error);
|
|
|
356 |
} else {
|
|
|
357 |
FacebookError error = new FacebookError(exception.getMessage());
|
|
|
358 |
listener.onFacebookError(error);
|
|
|
359 |
}
|
|
|
360 |
}
|
|
|
361 |
}
|
|
|
362 |
|
|
|
363 |
/**
|
|
|
364 |
* Helper to validate a service intent by resolving and checking the
|
|
|
365 |
* provider's package signature.
|
|
|
366 |
*
|
|
|
367 |
* @param context
|
|
|
368 |
* @param intent
|
|
|
369 |
* @return true if the service intent resolution happens successfully and
|
|
|
370 |
* the signatures match.
|
|
|
371 |
*/
|
|
|
372 |
private boolean validateServiceIntent(Context context, Intent intent) {
|
|
|
373 |
ResolveInfo resolveInfo = context.getPackageManager().resolveService(intent, 0);
|
|
|
374 |
if (resolveInfo == null) {
|
|
|
375 |
return false;
|
|
|
376 |
}
|
|
|
377 |
|
|
|
378 |
return validateAppSignatureForPackage(context, resolveInfo.serviceInfo.packageName);
|
|
|
379 |
}
|
|
|
380 |
|
|
|
381 |
/**
|
|
|
382 |
* Query the signature for the application that would be invoked by the
|
|
|
383 |
* given intent and verify that it matches the FB application's signature.
|
|
|
384 |
*
|
|
|
385 |
* @param context
|
|
|
386 |
* @param packageName
|
|
|
387 |
* @return true if the app's signature matches the expected signature.
|
|
|
388 |
*/
|
|
|
389 |
private boolean validateAppSignatureForPackage(Context context, String packageName) {
|
|
|
390 |
|
|
|
391 |
PackageInfo packageInfo;
|
|
|
392 |
try {
|
|
|
393 |
packageInfo = context.getPackageManager().getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
|
|
|
394 |
} catch (NameNotFoundException e) {
|
|
|
395 |
return false;
|
|
|
396 |
}
|
|
|
397 |
|
|
|
398 |
for (Signature signature : packageInfo.signatures) {
|
|
|
399 |
if (signature.toCharsString().equals(FB_APP_SIGNATURE)) {
|
|
|
400 |
return true;
|
|
|
401 |
}
|
|
|
402 |
}
|
|
|
403 |
return false;
|
|
|
404 |
}
|
|
|
405 |
|
|
|
406 |
/**
|
|
|
407 |
* IMPORTANT: If you are using the deprecated authorize() method,
|
|
|
408 |
* this method must be invoked at the top of the calling
|
|
|
409 |
* activity's onActivityResult() function or Facebook authentication will
|
|
|
410 |
* not function properly!
|
|
|
411 |
* <p/>
|
|
|
412 |
* If your calling activity does not currently implement onActivityResult(),
|
|
|
413 |
* you must implement it and include a call to this method if you intend to
|
|
|
414 |
* use the authorize() method in this SDK.
|
|
|
415 |
* <p/>
|
|
|
416 |
* For more information, see
|
|
|
417 |
* http://developer.android.com/reference/android/app/
|
|
|
418 |
* Activity.html#onActivityResult(int, int, android.content.Intent)
|
|
|
419 |
* <p/>
|
|
|
420 |
* This method is deprecated. See {@link Facebook} and {@link Session} for more info.
|
|
|
421 |
*/
|
|
|
422 |
@Deprecated
|
|
|
423 |
public void authorizeCallback(int requestCode, int resultCode, Intent data) {
|
|
|
424 |
checkUserSession("authorizeCallback");
|
|
|
425 |
Session pending = this.pendingOpeningSession;
|
|
|
426 |
if (pending != null) {
|
|
|
427 |
if (pending.onActivityResult(this.pendingAuthorizationActivity, requestCode, resultCode, data)) {
|
|
|
428 |
this.pendingOpeningSession = null;
|
|
|
429 |
this.pendingAuthorizationActivity = null;
|
|
|
430 |
this.pendingAuthorizationPermissions = null;
|
|
|
431 |
}
|
|
|
432 |
}
|
|
|
433 |
}
|
|
|
434 |
|
|
|
435 |
/**
|
|
|
436 |
* Refresh OAuth access token method. Binds to Facebook for Android
|
|
|
437 |
* stand-alone application application to refresh the access token. This
|
|
|
438 |
* method tries to connect to the Facebook App which will handle the
|
|
|
439 |
* authentication flow, and return a new OAuth access token. This method
|
|
|
440 |
* will automatically replace the old token with a new one. Note that this
|
|
|
441 |
* method is asynchronous and the callback will be invoked in the original
|
|
|
442 |
* calling thread (not in a background thread).
|
|
|
443 |
* <p/>
|
|
|
444 |
* This method is deprecated. See {@link Facebook} and {@link Session} for more info.
|
|
|
445 |
*
|
|
|
446 |
* @param context
|
|
|
447 |
* The Android Context that will be used to bind to the Facebook
|
|
|
448 |
* RefreshToken Service
|
|
|
449 |
* @param serviceListener
|
|
|
450 |
* Callback interface for notifying the calling application when
|
|
|
451 |
* the refresh request has completed or failed (can be null). In
|
|
|
452 |
* case of a success a new token can be found inside the result
|
|
|
453 |
* Bundle under Facebook.ACCESS_TOKEN key.
|
|
|
454 |
* @return true if the binding to the RefreshToken Service was created
|
|
|
455 |
*/
|
|
|
456 |
@Deprecated
|
|
|
457 |
public boolean extendAccessToken(Context context, ServiceListener serviceListener) {
|
|
|
458 |
checkUserSession("extendAccessToken");
|
|
|
459 |
Intent intent = new Intent();
|
|
|
460 |
|
|
|
461 |
intent.setClassName("com.facebook.katana", "com.facebook.katana.platform.TokenRefreshService");
|
|
|
462 |
|
|
|
463 |
// Verify that the application whose package name is
|
|
|
464 |
// com.facebook.katana
|
|
|
465 |
// has the expected FB app signature.
|
|
|
466 |
if (!validateServiceIntent(context, intent)) {
|
|
|
467 |
return false;
|
|
|
468 |
}
|
|
|
469 |
|
|
|
470 |
return context.bindService(intent, new TokenRefreshServiceConnection(context, serviceListener),
|
|
|
471 |
Context.BIND_AUTO_CREATE);
|
|
|
472 |
}
|
|
|
473 |
|
|
|
474 |
/**
|
|
|
475 |
* Calls extendAccessToken if shouldExtendAccessToken returns true.
|
|
|
476 |
* <p/>
|
|
|
477 |
* This method is deprecated. See {@link Facebook} and {@link Session} for more info.
|
|
|
478 |
*
|
|
|
479 |
* @return the same value as extendAccessToken if the the token requires
|
|
|
480 |
* refreshing, true otherwise
|
|
|
481 |
*/
|
|
|
482 |
@Deprecated
|
|
|
483 |
public boolean extendAccessTokenIfNeeded(Context context, ServiceListener serviceListener) {
|
|
|
484 |
checkUserSession("extendAccessTokenIfNeeded");
|
|
|
485 |
if (shouldExtendAccessToken()) {
|
|
|
486 |
return extendAccessToken(context, serviceListener);
|
|
|
487 |
}
|
|
|
488 |
return true;
|
|
|
489 |
}
|
|
|
490 |
|
|
|
491 |
/**
|
|
|
492 |
* Check if the access token requires refreshing.
|
|
|
493 |
* <p/>
|
|
|
494 |
* This method is deprecated. See {@link Facebook} and {@link Session} for more info.
|
|
|
495 |
*
|
|
|
496 |
* @return true if the last time a new token was obtained was over 24 hours
|
|
|
497 |
* ago.
|
|
|
498 |
*/
|
|
|
499 |
@Deprecated
|
|
|
500 |
public boolean shouldExtendAccessToken() {
|
|
|
501 |
checkUserSession("shouldExtendAccessToken");
|
|
|
502 |
return isSessionValid()
|
|
|
503 |
&& (System.currentTimeMillis() - lastAccessUpdateMillisecondsAfterEpoch >= REFRESH_TOKEN_BARRIER);
|
|
|
504 |
}
|
|
|
505 |
|
|
|
506 |
/**
|
|
|
507 |
* Handles connection to the token refresh service (this service is a part
|
|
|
508 |
* of Facebook App).
|
|
|
509 |
*/
|
|
|
510 |
private class TokenRefreshServiceConnection implements ServiceConnection {
|
|
|
511 |
|
|
|
512 |
final Messenger messageReceiver = new Messenger(
|
|
|
513 |
new TokenRefreshConnectionHandler(Facebook.this, this));
|
|
|
514 |
|
|
|
515 |
final ServiceListener serviceListener;
|
|
|
516 |
final Context applicationsContext;
|
|
|
517 |
|
|
|
518 |
Messenger messageSender = null;
|
|
|
519 |
|
|
|
520 |
public TokenRefreshServiceConnection(Context applicationsContext, ServiceListener serviceListener) {
|
|
|
521 |
this.applicationsContext = applicationsContext;
|
|
|
522 |
this.serviceListener = serviceListener;
|
|
|
523 |
}
|
|
|
524 |
|
|
|
525 |
@Override
|
|
|
526 |
public void onServiceConnected(ComponentName className, IBinder service) {
|
|
|
527 |
messageSender = new Messenger(service);
|
|
|
528 |
refreshToken();
|
|
|
529 |
}
|
|
|
530 |
|
|
|
531 |
@Override
|
|
|
532 |
public void onServiceDisconnected(ComponentName arg) {
|
|
|
533 |
serviceListener.onError(new Error("Service disconnected"));
|
|
|
534 |
try {
|
|
|
535 |
// We returned an error so there's no point in
|
|
|
536 |
// keeping the binding open.
|
|
|
537 |
applicationsContext.unbindService(TokenRefreshServiceConnection.this);
|
|
|
538 |
} catch (IllegalArgumentException ex) {
|
|
|
539 |
// Do nothing, the connection was already unbound
|
|
|
540 |
}
|
|
|
541 |
}
|
|
|
542 |
|
|
|
543 |
private void refreshToken() {
|
|
|
544 |
Bundle requestData = new Bundle();
|
|
|
545 |
requestData.putString(TOKEN, accessToken);
|
|
|
546 |
|
|
|
547 |
Message request = Message.obtain();
|
|
|
548 |
request.setData(requestData);
|
|
|
549 |
request.replyTo = messageReceiver;
|
|
|
550 |
|
|
|
551 |
try {
|
|
|
552 |
messageSender.send(request);
|
|
|
553 |
} catch (RemoteException e) {
|
|
|
554 |
serviceListener.onError(new Error("Service connection error"));
|
|
|
555 |
}
|
|
|
556 |
}
|
|
|
557 |
}
|
|
|
558 |
|
|
|
559 |
// Creating a static Handler class to reduce the possibility of a memory leak.
|
|
|
560 |
// Handler objects for the same thread all share a common Looper object, which they post messages
|
|
|
561 |
// to and read from. As messages contain target Handler, as long as there are messages with target
|
|
|
562 |
// handler in the message queue, the handler cannot be garbage collected. If handler is not static,
|
|
|
563 |
// the instance of the containing class also cannot be garbage collected even if it is destroyed.
|
|
|
564 |
private static class TokenRefreshConnectionHandler extends Handler {
|
|
|
565 |
WeakReference<Facebook> facebookWeakReference;
|
|
|
566 |
WeakReference<TokenRefreshServiceConnection> connectionWeakReference;
|
|
|
567 |
|
|
|
568 |
TokenRefreshConnectionHandler(Facebook facebook, TokenRefreshServiceConnection connection) {
|
|
|
569 |
super();
|
|
|
570 |
facebookWeakReference = new WeakReference<Facebook>(facebook);
|
|
|
571 |
connectionWeakReference = new WeakReference<TokenRefreshServiceConnection>(connection);
|
|
|
572 |
}
|
|
|
573 |
|
|
|
574 |
@Override
|
|
|
575 |
@SuppressWarnings("deprecation")
|
|
|
576 |
public void handleMessage(Message msg) {
|
|
|
577 |
Facebook facebook = facebookWeakReference.get();
|
|
|
578 |
TokenRefreshServiceConnection connection = connectionWeakReference.get();
|
|
|
579 |
if (facebook == null || connection == null) {
|
|
|
580 |
return;
|
|
|
581 |
}
|
|
|
582 |
|
|
|
583 |
String token = msg.getData().getString(TOKEN);
|
|
|
584 |
// Legacy functions in Facebook class (and ServiceListener implementors) expect expires_in in
|
|
|
585 |
// milliseconds from epoch
|
|
|
586 |
long expiresAtMsecFromEpoch = msg.getData().getLong(EXPIRES) * 1000L;
|
|
|
587 |
|
|
|
588 |
if (token != null) {
|
|
|
589 |
facebook.setAccessToken(token);
|
|
|
590 |
facebook.setAccessExpires(expiresAtMsecFromEpoch);
|
|
|
591 |
|
|
|
592 |
Session refreshSession = facebook.session;
|
|
|
593 |
if (refreshSession != null) {
|
|
|
594 |
// Session.internalRefreshToken expects the original bundle with expires_in in seconds from
|
|
|
595 |
// epoch.
|
|
|
596 |
LegacyHelper.extendTokenCompleted(refreshSession, msg.getData());
|
|
|
597 |
}
|
|
|
598 |
|
|
|
599 |
if (connection.serviceListener != null) {
|
|
|
600 |
// To avoid confusion we should return the expiration time in
|
|
|
601 |
// the same format as the getAccessExpires() function - that
|
|
|
602 |
// is in milliseconds.
|
|
|
603 |
Bundle resultBundle = (Bundle) msg.getData().clone();
|
|
|
604 |
resultBundle.putLong(EXPIRES, expiresAtMsecFromEpoch);
|
|
|
605 |
|
|
|
606 |
connection.serviceListener.onComplete(resultBundle);
|
|
|
607 |
}
|
|
|
608 |
} else if (connection.serviceListener != null) { // extract errors only if
|
|
|
609 |
// client wants them
|
|
|
610 |
String error = msg.getData().getString("error");
|
|
|
611 |
if (msg.getData().containsKey("error_code")) {
|
|
|
612 |
int errorCode = msg.getData().getInt("error_code");
|
|
|
613 |
connection.serviceListener.onFacebookError(new FacebookError(error, null, errorCode));
|
|
|
614 |
} else {
|
|
|
615 |
connection.serviceListener.onError(new Error(error != null ? error : "Unknown service error"));
|
|
|
616 |
}
|
|
|
617 |
}
|
|
|
618 |
|
|
|
619 |
// The refreshToken function should be called rarely,
|
|
|
620 |
// so there is no point in keeping the binding open.
|
|
|
621 |
connection.applicationsContext.unbindService(connection);
|
|
|
622 |
}
|
|
|
623 |
}
|
|
|
624 |
|
|
|
625 |
/**
|
|
|
626 |
* Invalidate the current user session by removing the access token in
|
|
|
627 |
* memory, clearing the browser cookie, and calling auth.expireSession
|
|
|
628 |
* through the API.
|
|
|
629 |
* <p/>
|
|
|
630 |
* Note that this method blocks waiting for a network response, so do not
|
|
|
631 |
* call it in a UI thread.
|
|
|
632 |
* <p/>
|
|
|
633 |
* This method is deprecated. See {@link Facebook} and {@link Session} for more info.
|
|
|
634 |
*
|
|
|
635 |
* @param context
|
|
|
636 |
* The Android context in which the logout should be called: it
|
|
|
637 |
* should be the same context in which the login occurred in
|
|
|
638 |
* order to clear any stored cookies
|
|
|
639 |
* @throws IOException
|
|
|
640 |
* @throws MalformedURLException
|
|
|
641 |
* @return JSON string representation of the auth.expireSession response
|
|
|
642 |
* ("true" if successful)
|
|
|
643 |
*/
|
|
|
644 |
@Deprecated
|
|
|
645 |
public String logout(Context context) throws MalformedURLException, IOException {
|
|
|
646 |
return logoutImpl(context);
|
|
|
647 |
}
|
|
|
648 |
|
|
|
649 |
String logoutImpl(Context context) throws MalformedURLException, IOException {
|
|
|
650 |
checkUserSession("logout");
|
|
|
651 |
Bundle b = new Bundle();
|
|
|
652 |
b.putString("method", "auth.expireSession");
|
|
|
653 |
String response = request(b);
|
|
|
654 |
|
|
|
655 |
long currentTimeMillis = System.currentTimeMillis();
|
|
|
656 |
Session sessionToClose = null;
|
|
|
657 |
|
|
|
658 |
synchronized (this.lock) {
|
|
|
659 |
sessionToClose = session;
|
|
|
660 |
|
|
|
661 |
session = null;
|
|
|
662 |
accessToken = null;
|
|
|
663 |
accessExpiresMillisecondsAfterEpoch = 0;
|
|
|
664 |
lastAccessUpdateMillisecondsAfterEpoch = currentTimeMillis;
|
|
|
665 |
sessionInvalidated = false;
|
|
|
666 |
}
|
|
|
667 |
|
|
|
668 |
if (sessionToClose != null) {
|
|
|
669 |
sessionToClose.closeAndClearTokenInformation();
|
|
|
670 |
}
|
|
|
671 |
|
|
|
672 |
return response;
|
|
|
673 |
}
|
|
|
674 |
|
|
|
675 |
/**
|
|
|
676 |
* Make a request to Facebook's old (pre-graph) API with the given
|
|
|
677 |
* parameters. One of the parameter keys must be "method" and its value
|
|
|
678 |
* should be a valid REST server API method.
|
|
|
679 |
* <p/>
|
|
|
680 |
* See http://developers.facebook.com/docs/reference/rest/
|
|
|
681 |
* <p/>
|
|
|
682 |
* Note that this method blocks waiting for a network response, so do not
|
|
|
683 |
* call it in a UI thread.
|
|
|
684 |
* <p/>
|
|
|
685 |
* Example: <code>
|
|
|
686 |
* Bundle parameters = new Bundle();
|
|
|
687 |
* parameters.putString("method", "auth.expireSession");
|
|
|
688 |
* String response = request(parameters);
|
|
|
689 |
* </code>
|
|
|
690 |
* <p/>
|
|
|
691 |
* This method is deprecated. See {@link Facebook} and {@link Request} for more info.
|
|
|
692 |
*
|
|
|
693 |
* @param parameters
|
|
|
694 |
* Key-value pairs of parameters to the request. Refer to the
|
|
|
695 |
* documentation: one of the parameters must be "method".
|
|
|
696 |
* @throws IOException
|
|
|
697 |
* if a network error occurs
|
|
|
698 |
* @throws MalformedURLException
|
|
|
699 |
* if accessing an invalid endpoint
|
|
|
700 |
* @throws IllegalArgumentException
|
|
|
701 |
* if one of the parameters is not "method"
|
|
|
702 |
* @return JSON string representation of the response
|
|
|
703 |
*/
|
|
|
704 |
@Deprecated
|
|
|
705 |
public String request(Bundle parameters) throws MalformedURLException, IOException {
|
|
|
706 |
if (!parameters.containsKey("method")) {
|
|
|
707 |
throw new IllegalArgumentException("API method must be specified. "
|
|
|
708 |
+ "(parameters must contain key \"method\" and value). See"
|
|
|
709 |
+ " http://developers.facebook.com/docs/reference/rest/");
|
|
|
710 |
}
|
|
|
711 |
return requestImpl(null, parameters, "GET");
|
|
|
712 |
}
|
|
|
713 |
|
|
|
714 |
/**
|
|
|
715 |
* Make a request to the Facebook Graph API without any parameters.
|
|
|
716 |
* <p/>
|
|
|
717 |
* See http://developers.facebook.com/docs/api
|
|
|
718 |
* <p/>
|
|
|
719 |
* Note that this method blocks waiting for a network response, so do not
|
|
|
720 |
* call it in a UI thread.
|
|
|
721 |
* <p/>
|
|
|
722 |
* This method is deprecated. See {@link Facebook} and {@link Request} for more info.
|
|
|
723 |
*
|
|
|
724 |
* @param graphPath
|
|
|
725 |
* Path to resource in the Facebook graph, e.g., to fetch data
|
|
|
726 |
* about the currently logged authenticated user, provide "me",
|
|
|
727 |
* which will fetch http://graph.facebook.com/me
|
|
|
728 |
* @throws IOException
|
|
|
729 |
* @throws MalformedURLException
|
|
|
730 |
* @return JSON string representation of the response
|
|
|
731 |
*/
|
|
|
732 |
@Deprecated
|
|
|
733 |
public String request(String graphPath) throws MalformedURLException, IOException {
|
|
|
734 |
return requestImpl(graphPath, new Bundle(), "GET");
|
|
|
735 |
}
|
|
|
736 |
|
|
|
737 |
/**
|
|
|
738 |
* Make a request to the Facebook Graph API with the given string parameters
|
|
|
739 |
* using an HTTP GET (default method).
|
|
|
740 |
* <p/>
|
|
|
741 |
* See http://developers.facebook.com/docs/api
|
|
|
742 |
* <p/>
|
|
|
743 |
* Note that this method blocks waiting for a network response, so do not
|
|
|
744 |
* call it in a UI thread.
|
|
|
745 |
* <p/>
|
|
|
746 |
* This method is deprecated. See {@link Facebook} and {@link Request} for more info.
|
|
|
747 |
*
|
|
|
748 |
* @param graphPath
|
|
|
749 |
* Path to resource in the Facebook graph, e.g., to fetch data
|
|
|
750 |
* about the currently logged authenticated user, provide "me",
|
|
|
751 |
* which will fetch http://graph.facebook.com/me
|
|
|
752 |
* @param parameters
|
|
|
753 |
* key-value string parameters, e.g. the path "search" with
|
|
|
754 |
* parameters "q" : "facebook" would produce a query for the
|
|
|
755 |
* following graph resource:
|
|
|
756 |
* https://graph.facebook.com/search?q=facebook
|
|
|
757 |
* @throws IOException
|
|
|
758 |
* @throws MalformedURLException
|
|
|
759 |
* @return JSON string representation of the response
|
|
|
760 |
*/
|
|
|
761 |
@Deprecated
|
|
|
762 |
public String request(String graphPath, Bundle parameters) throws MalformedURLException, IOException {
|
|
|
763 |
return requestImpl(graphPath, parameters, "GET");
|
|
|
764 |
}
|
|
|
765 |
|
|
|
766 |
/**
|
|
|
767 |
* Synchronously make a request to the Facebook Graph API with the given
|
|
|
768 |
* HTTP method and string parameters. Note that binary data parameters (e.g.
|
|
|
769 |
* pictures) are not yet supported by this helper function.
|
|
|
770 |
* <p/>
|
|
|
771 |
* See http://developers.facebook.com/docs/api
|
|
|
772 |
* <p/>
|
|
|
773 |
* Note that this method blocks waiting for a network response, so do not
|
|
|
774 |
* call it in a UI thread.
|
|
|
775 |
* <p/>
|
|
|
776 |
* This method is deprecated. See {@link Facebook} and {@link Request} for more info.
|
|
|
777 |
*
|
|
|
778 |
* @param graphPath
|
|
|
779 |
* Path to resource in the Facebook graph, e.g., to fetch data
|
|
|
780 |
* about the currently logged authenticated user, provide "me",
|
|
|
781 |
* which will fetch http://graph.facebook.com/me
|
|
|
782 |
* @param params
|
|
|
783 |
* Key-value string parameters, e.g. the path "search" with
|
|
|
784 |
* parameters {"q" : "facebook"} would produce a query for the
|
|
|
785 |
* following graph resource:
|
|
|
786 |
* https://graph.facebook.com/search?q=facebook
|
|
|
787 |
* @param httpMethod
|
|
|
788 |
* http verb, e.g. "GET", "POST", "DELETE"
|
|
|
789 |
* @throws IOException
|
|
|
790 |
* @throws MalformedURLException
|
|
|
791 |
* @return JSON string representation of the response
|
|
|
792 |
*/
|
|
|
793 |
@Deprecated
|
|
|
794 |
public String request(String graphPath, Bundle params, String httpMethod) throws FileNotFoundException,
|
|
|
795 |
MalformedURLException, IOException {
|
|
|
796 |
return requestImpl(graphPath, params, httpMethod);
|
|
|
797 |
}
|
|
|
798 |
|
|
|
799 |
// Internal call to avoid deprecated warnings.
|
|
|
800 |
@SuppressWarnings("deprecation")
|
|
|
801 |
String requestImpl(String graphPath, Bundle params, String httpMethod) throws FileNotFoundException,
|
|
|
802 |
MalformedURLException, IOException {
|
|
|
803 |
params.putString("format", "json");
|
|
|
804 |
if (isSessionValid()) {
|
|
|
805 |
params.putString(TOKEN, getAccessToken());
|
|
|
806 |
}
|
|
|
807 |
String url = (graphPath != null) ? GRAPH_BASE_URL + graphPath : RESTSERVER_URL;
|
|
|
808 |
return Util.openUrl(url, httpMethod, params);
|
|
|
809 |
}
|
|
|
810 |
|
|
|
811 |
/**
|
|
|
812 |
* Generate a UI dialog for the request action in the given Android context.
|
|
|
813 |
* <p/>
|
|
|
814 |
* Note that this method is asynchronous and the callback will be invoked in
|
|
|
815 |
* the original calling thread (not in a background thread).
|
|
|
816 |
*
|
|
|
817 |
* This method is deprecated. See {@link com.facebook.widget.WebDialog}.
|
|
|
818 |
*
|
|
|
819 |
* @param context
|
|
|
820 |
* The Android context in which we will generate this dialog.
|
|
|
821 |
* @param action
|
|
|
822 |
* String representation of the desired method: e.g. "login",
|
|
|
823 |
* "stream.publish", ...
|
|
|
824 |
* @param listener
|
|
|
825 |
* Callback interface to notify the application when the dialog
|
|
|
826 |
* has completed.
|
|
|
827 |
*/
|
|
|
828 |
@Deprecated
|
|
|
829 |
public void dialog(Context context, String action, DialogListener listener) {
|
|
|
830 |
dialog(context, action, new Bundle(), listener);
|
|
|
831 |
}
|
|
|
832 |
|
|
|
833 |
/**
|
|
|
834 |
* Generate a UI dialog for the request action in the given Android context
|
|
|
835 |
* with the provided parameters.
|
|
|
836 |
* <p/>
|
|
|
837 |
* Note that this method is asynchronous and the callback will be invoked in
|
|
|
838 |
* the original calling thread (not in a background thread).
|
|
|
839 |
*
|
|
|
840 |
* This method is deprecated. See {@link com.facebook.widget.WebDialog}.
|
|
|
841 |
*
|
|
|
842 |
* @param context
|
|
|
843 |
* The Android context in which we will generate this dialog.
|
|
|
844 |
* @param action
|
|
|
845 |
* String representation of the desired method: e.g. "feed" ...
|
|
|
846 |
* @param parameters
|
|
|
847 |
* String key-value pairs to be passed as URL parameters.
|
|
|
848 |
* @param listener
|
|
|
849 |
* Callback interface to notify the application when the dialog
|
|
|
850 |
* has completed.
|
|
|
851 |
*/
|
|
|
852 |
@Deprecated
|
|
|
853 |
public void dialog(Context context, String action, Bundle parameters, final DialogListener listener) {
|
|
|
854 |
parameters.putString("display", "touch");
|
|
|
855 |
parameters.putString("redirect_uri", REDIRECT_URI);
|
|
|
856 |
|
|
|
857 |
if (action.equals(LOGIN)) {
|
|
|
858 |
parameters.putString("type", "user_agent");
|
|
|
859 |
parameters.putString("client_id", mAppId);
|
|
|
860 |
} else {
|
|
|
861 |
parameters.putString("app_id", mAppId);
|
|
|
862 |
// We do not want to add an access token when displaying the auth dialog.
|
|
|
863 |
if (isSessionValid()) {
|
|
|
864 |
parameters.putString(TOKEN, getAccessToken());
|
|
|
865 |
}
|
|
|
866 |
}
|
|
|
867 |
|
|
|
868 |
if (context.checkCallingOrSelfPermission(Manifest.permission.INTERNET) != PackageManager.PERMISSION_GRANTED) {
|
|
|
869 |
Util.showAlert(context, "Error", "Application requires permission to access the Internet");
|
|
|
870 |
} else {
|
|
|
871 |
new FbDialog(context, action, parameters, listener).show();
|
|
|
872 |
}
|
|
|
873 |
}
|
|
|
874 |
|
|
|
875 |
/**
|
|
|
876 |
* Returns whether the current access token is valid
|
|
|
877 |
*
|
|
|
878 |
* @return boolean - whether this object has an non-expired session token
|
|
|
879 |
*/
|
|
|
880 |
@Deprecated
|
|
|
881 |
public boolean isSessionValid() {
|
|
|
882 |
return (getAccessToken() != null)
|
|
|
883 |
&& ((getAccessExpires() == 0) || (System.currentTimeMillis() < getAccessExpires()));
|
|
|
884 |
}
|
|
|
885 |
|
|
|
886 |
/**
|
|
|
887 |
* Allows the user to set a Session for the Facebook class to use.
|
|
|
888 |
* If a Session is set here, then one should not use the authorize, logout,
|
|
|
889 |
* or extendAccessToken methods which alter the Session object since that may
|
|
|
890 |
* result in undefined behavior. Using those methods after setting the
|
|
|
891 |
* session here will result in exceptions being thrown.
|
|
|
892 |
*
|
|
|
893 |
* @param session the Session object to use, cannot be null
|
|
|
894 |
*/
|
|
|
895 |
@Deprecated
|
|
|
896 |
public void setSession(Session session) {
|
|
|
897 |
if (session == null) {
|
|
|
898 |
throw new IllegalArgumentException("session cannot be null");
|
|
|
899 |
}
|
|
|
900 |
synchronized (this.lock) {
|
|
|
901 |
this.userSetSession = session;
|
|
|
902 |
}
|
|
|
903 |
}
|
|
|
904 |
|
|
|
905 |
private void checkUserSession(String methodName) {
|
|
|
906 |
if (userSetSession != null) {
|
|
|
907 |
throw new UnsupportedOperationException(
|
|
|
908 |
String.format("Cannot call %s after setSession has been called.", methodName));
|
|
|
909 |
}
|
|
|
910 |
}
|
|
|
911 |
|
|
|
912 |
/**
|
|
|
913 |
* Get the underlying Session object to use with 3.0 api.
|
|
|
914 |
*
|
|
|
915 |
* @return Session - underlying session
|
|
|
916 |
*/
|
|
|
917 |
@Deprecated
|
|
|
918 |
public final Session getSession() {
|
|
|
919 |
while (true) {
|
|
|
920 |
String cachedToken = null;
|
|
|
921 |
Session oldSession = null;
|
|
|
922 |
|
|
|
923 |
synchronized (this.lock) {
|
|
|
924 |
if (userSetSession != null) {
|
|
|
925 |
return userSetSession;
|
|
|
926 |
}
|
|
|
927 |
if ((session != null) || !sessionInvalidated) {
|
|
|
928 |
return session;
|
|
|
929 |
}
|
|
|
930 |
|
|
|
931 |
cachedToken = accessToken;
|
|
|
932 |
oldSession = session;
|
|
|
933 |
}
|
|
|
934 |
|
|
|
935 |
if (cachedToken == null) {
|
|
|
936 |
return null;
|
|
|
937 |
}
|
|
|
938 |
|
|
|
939 |
// At this point we do not have a valid session, but mAccessToken is
|
|
|
940 |
// non-null.
|
|
|
941 |
// So we can try building a session based on that.
|
|
|
942 |
List<String> permissions;
|
|
|
943 |
if (oldSession != null) {
|
|
|
944 |
permissions = oldSession.getPermissions();
|
|
|
945 |
} else if (pendingAuthorizationPermissions != null) {
|
|
|
946 |
permissions = Arrays.asList(pendingAuthorizationPermissions);
|
|
|
947 |
} else {
|
|
|
948 |
permissions = Collections.<String>emptyList();
|
|
|
949 |
}
|
|
|
950 |
|
|
|
951 |
Session newSession = new Session.Builder(pendingAuthorizationActivity).
|
|
|
952 |
setApplicationId(mAppId).
|
|
|
953 |
setTokenCachingStrategy(getTokenCache()).
|
|
|
954 |
build();
|
|
|
955 |
if (newSession.getState() != SessionState.CREATED_TOKEN_LOADED) {
|
|
|
956 |
return null;
|
|
|
957 |
}
|
|
|
958 |
Session.OpenRequest openRequest =
|
|
|
959 |
new Session.OpenRequest(pendingAuthorizationActivity).setPermissions(permissions);
|
|
|
960 |
openSession(newSession, openRequest, !permissions.isEmpty());
|
|
|
961 |
|
|
|
962 |
Session invalidatedSession = null;
|
|
|
963 |
Session returnSession = null;
|
|
|
964 |
|
|
|
965 |
synchronized (this.lock) {
|
|
|
966 |
if (sessionInvalidated || (session == null)) {
|
|
|
967 |
invalidatedSession = session;
|
|
|
968 |
returnSession = session = newSession;
|
|
|
969 |
sessionInvalidated = false;
|
|
|
970 |
}
|
|
|
971 |
}
|
|
|
972 |
|
|
|
973 |
if (invalidatedSession != null) {
|
|
|
974 |
invalidatedSession.close();
|
|
|
975 |
}
|
|
|
976 |
|
|
|
977 |
if (returnSession != null) {
|
|
|
978 |
return returnSession;
|
|
|
979 |
}
|
|
|
980 |
// Else token state changed between the synchronized blocks, so
|
|
|
981 |
// retry..
|
|
|
982 |
}
|
|
|
983 |
}
|
|
|
984 |
|
|
|
985 |
/**
|
|
|
986 |
* Retrieve the OAuth 2.0 access token for API access: treat with care.
|
|
|
987 |
* Returns null if no session exists.
|
|
|
988 |
*
|
|
|
989 |
* @return String - access token
|
|
|
990 |
*/
|
|
|
991 |
@Deprecated
|
|
|
992 |
public String getAccessToken() {
|
|
|
993 |
Session s = getSession();
|
|
|
994 |
if (s != null) {
|
|
|
995 |
return s.getAccessToken();
|
|
|
996 |
} else {
|
|
|
997 |
return null;
|
|
|
998 |
}
|
|
|
999 |
}
|
|
|
1000 |
|
|
|
1001 |
/**
|
|
|
1002 |
* Retrieve the current session's expiration time (in milliseconds since
|
|
|
1003 |
* Unix epoch), or 0 if the session doesn't expire or doesn't exist.
|
|
|
1004 |
*
|
|
|
1005 |
* @return long - session expiration time
|
|
|
1006 |
*/
|
|
|
1007 |
@Deprecated
|
|
|
1008 |
public long getAccessExpires() {
|
|
|
1009 |
Session s = getSession();
|
|
|
1010 |
if (s != null) {
|
|
|
1011 |
return s.getExpirationDate().getTime();
|
|
|
1012 |
} else {
|
|
|
1013 |
return accessExpiresMillisecondsAfterEpoch;
|
|
|
1014 |
}
|
|
|
1015 |
}
|
|
|
1016 |
|
|
|
1017 |
/**
|
|
|
1018 |
* Retrieve the last time the token was updated (in milliseconds since
|
|
|
1019 |
* the Unix epoch), or 0 if the token has not been set.
|
|
|
1020 |
*
|
|
|
1021 |
* @return long - timestamp of the last token update.
|
|
|
1022 |
*/
|
|
|
1023 |
@Deprecated
|
|
|
1024 |
public long getLastAccessUpdate() {
|
|
|
1025 |
return lastAccessUpdateMillisecondsAfterEpoch;
|
|
|
1026 |
}
|
|
|
1027 |
|
|
|
1028 |
/**
|
|
|
1029 |
* Restore the token, expiration time, and last update time from cached values.
|
|
|
1030 |
* These should be values obtained from getAccessToken(), getAccessExpires, and
|
|
|
1031 |
* getLastAccessUpdate() respectively.
|
|
|
1032 |
* <p/>
|
|
|
1033 |
* This method is deprecated. See {@link Facebook} and {@link Session} for more info.
|
|
|
1034 |
*
|
|
|
1035 |
* @param accessToken - access token
|
|
|
1036 |
* @param accessExpires - access token expiration time
|
|
|
1037 |
* @param lastAccessUpdate - timestamp of the last token update
|
|
|
1038 |
*/
|
|
|
1039 |
@Deprecated
|
|
|
1040 |
public void setTokenFromCache(String accessToken, long accessExpires, long lastAccessUpdate) {
|
|
|
1041 |
checkUserSession("setTokenFromCache");
|
|
|
1042 |
synchronized (this.lock) {
|
|
|
1043 |
this.accessToken = accessToken;
|
|
|
1044 |
accessExpiresMillisecondsAfterEpoch = accessExpires;
|
|
|
1045 |
lastAccessUpdateMillisecondsAfterEpoch = lastAccessUpdate;
|
|
|
1046 |
}
|
|
|
1047 |
}
|
|
|
1048 |
|
|
|
1049 |
/**
|
|
|
1050 |
* Set the OAuth 2.0 access token for API access.
|
|
|
1051 |
* <p/>
|
|
|
1052 |
* This method is deprecated. See {@link Facebook} and {@link Session} for more info.
|
|
|
1053 |
*
|
|
|
1054 |
* @param token
|
|
|
1055 |
* - access token
|
|
|
1056 |
*/
|
|
|
1057 |
@Deprecated
|
|
|
1058 |
public void setAccessToken(String token) {
|
|
|
1059 |
checkUserSession("setAccessToken");
|
|
|
1060 |
synchronized (this.lock) {
|
|
|
1061 |
accessToken = token;
|
|
|
1062 |
lastAccessUpdateMillisecondsAfterEpoch = System.currentTimeMillis();
|
|
|
1063 |
sessionInvalidated = true;
|
|
|
1064 |
}
|
|
|
1065 |
}
|
|
|
1066 |
|
|
|
1067 |
/**
|
|
|
1068 |
* Set the current session's expiration time (in milliseconds since Unix
|
|
|
1069 |
* epoch), or 0 if the session doesn't expire.
|
|
|
1070 |
* <p/>
|
|
|
1071 |
* This method is deprecated. See {@link Facebook} and {@link Session} for more info.
|
|
|
1072 |
*
|
|
|
1073 |
* @param timestampInMsec
|
|
|
1074 |
* - timestamp in milliseconds
|
|
|
1075 |
*/
|
|
|
1076 |
@Deprecated
|
|
|
1077 |
public void setAccessExpires(long timestampInMsec) {
|
|
|
1078 |
checkUserSession("setAccessExpires");
|
|
|
1079 |
synchronized (this.lock) {
|
|
|
1080 |
accessExpiresMillisecondsAfterEpoch = timestampInMsec;
|
|
|
1081 |
lastAccessUpdateMillisecondsAfterEpoch = System.currentTimeMillis();
|
|
|
1082 |
sessionInvalidated = true;
|
|
|
1083 |
}
|
|
|
1084 |
}
|
|
|
1085 |
|
|
|
1086 |
/**
|
|
|
1087 |
* Set the current session's duration (in seconds since Unix epoch), or "0"
|
|
|
1088 |
* if session doesn't expire.
|
|
|
1089 |
* <p/>
|
|
|
1090 |
* This method is deprecated. See {@link Facebook} and {@link Session} for more info.
|
|
|
1091 |
*
|
|
|
1092 |
* @param expiresInSecsFromNow
|
|
|
1093 |
* - duration in seconds (or 0 if the session doesn't expire)
|
|
|
1094 |
*/
|
|
|
1095 |
@Deprecated
|
|
|
1096 |
public void setAccessExpiresIn(String expiresInSecsFromNow) {
|
|
|
1097 |
checkUserSession("setAccessExpiresIn");
|
|
|
1098 |
if (expiresInSecsFromNow != null) {
|
|
|
1099 |
long expires = expiresInSecsFromNow.equals("0") ? 0 : System.currentTimeMillis()
|
|
|
1100 |
+ Long.parseLong(expiresInSecsFromNow) * 1000L;
|
|
|
1101 |
setAccessExpires(expires);
|
|
|
1102 |
}
|
|
|
1103 |
}
|
|
|
1104 |
|
|
|
1105 |
/**
|
|
|
1106 |
* This method is deprecated. See {@link Facebook} and {@link Session} for more info.
|
|
|
1107 |
*
|
|
|
1108 |
* @return the String representing application ID
|
|
|
1109 |
*/
|
|
|
1110 |
@Deprecated
|
|
|
1111 |
public String getAppId() {
|
|
|
1112 |
return mAppId;
|
|
|
1113 |
}
|
|
|
1114 |
|
|
|
1115 |
/**
|
|
|
1116 |
* This method is deprecated. See {@link Facebook} and {@link Session} for more info.
|
|
|
1117 |
*
|
|
|
1118 |
* @param appId the String representing the application ID
|
|
|
1119 |
*/
|
|
|
1120 |
@Deprecated
|
|
|
1121 |
public void setAppId(String appId) {
|
|
|
1122 |
checkUserSession("setAppId");
|
|
|
1123 |
synchronized (this.lock) {
|
|
|
1124 |
mAppId = appId;
|
|
|
1125 |
sessionInvalidated = true;
|
|
|
1126 |
}
|
|
|
1127 |
}
|
|
|
1128 |
|
|
|
1129 |
private TokenCachingStrategy getTokenCache() {
|
|
|
1130 |
// Intentionally not volatile/synchronized--it is okay if we race to
|
|
|
1131 |
// create more than one of these.
|
|
|
1132 |
if (tokenCache == null) {
|
|
|
1133 |
tokenCache = new SetterTokenCachingStrategy();
|
|
|
1134 |
}
|
|
|
1135 |
return tokenCache;
|
|
|
1136 |
}
|
|
|
1137 |
|
|
|
1138 |
private static String[] stringArray(List<String> list) {
|
|
|
1139 |
int size = (list != null) ? list.size() : 0;
|
|
|
1140 |
String[] array = new String[size];
|
|
|
1141 |
|
|
|
1142 |
if (list != null) {
|
|
|
1143 |
for (int i = 0; i < array.length; i++) {
|
|
|
1144 |
array[i] = list.get(i);
|
|
|
1145 |
}
|
|
|
1146 |
}
|
|
|
1147 |
|
|
|
1148 |
return array;
|
|
|
1149 |
}
|
|
|
1150 |
|
|
|
1151 |
private static List<String> stringList(String[] array) {
|
|
|
1152 |
if (array != null) {
|
|
|
1153 |
return Arrays.asList(array);
|
|
|
1154 |
} else {
|
|
|
1155 |
return Collections.emptyList();
|
|
|
1156 |
}
|
|
|
1157 |
}
|
|
|
1158 |
|
|
|
1159 |
private class SetterTokenCachingStrategy extends TokenCachingStrategy {
|
|
|
1160 |
|
|
|
1161 |
@Override
|
|
|
1162 |
public Bundle load() {
|
|
|
1163 |
Bundle bundle = new Bundle();
|
|
|
1164 |
|
|
|
1165 |
if (accessToken != null) {
|
|
|
1166 |
TokenCachingStrategy.putToken(bundle, accessToken);
|
|
|
1167 |
TokenCachingStrategy.putExpirationMilliseconds(bundle, accessExpiresMillisecondsAfterEpoch);
|
|
|
1168 |
TokenCachingStrategy.putPermissions(bundle, stringList(pendingAuthorizationPermissions));
|
|
|
1169 |
TokenCachingStrategy.putSource(bundle, AccessTokenSource.WEB_VIEW);
|
|
|
1170 |
TokenCachingStrategy.putLastRefreshMilliseconds(bundle, lastAccessUpdateMillisecondsAfterEpoch);
|
|
|
1171 |
}
|
|
|
1172 |
|
|
|
1173 |
return bundle;
|
|
|
1174 |
}
|
|
|
1175 |
|
|
|
1176 |
@Override
|
|
|
1177 |
public void save(Bundle bundle) {
|
|
|
1178 |
accessToken = TokenCachingStrategy.getToken(bundle);
|
|
|
1179 |
accessExpiresMillisecondsAfterEpoch = TokenCachingStrategy.getExpirationMilliseconds(bundle);
|
|
|
1180 |
pendingAuthorizationPermissions = stringArray(TokenCachingStrategy.getPermissions(bundle));
|
|
|
1181 |
lastAccessUpdateMillisecondsAfterEpoch = TokenCachingStrategy.getLastRefreshMilliseconds(bundle);
|
|
|
1182 |
}
|
|
|
1183 |
|
|
|
1184 |
@Override
|
|
|
1185 |
public void clear() {
|
|
|
1186 |
accessToken = null;
|
|
|
1187 |
}
|
|
|
1188 |
}
|
|
|
1189 |
|
|
|
1190 |
/**
|
|
|
1191 |
* Get Attribution ID for app install conversion tracking.
|
|
|
1192 |
* <p/>
|
|
|
1193 |
* This method is deprecated. See {@link Facebook} and {@link Settings} for more info.
|
|
|
1194 |
*
|
|
|
1195 |
* @param contentResolver
|
|
|
1196 |
* @return Attribution ID that will be used for conversion tracking. It will be null only if
|
|
|
1197 |
* the user has not installed or logged in to the Facebook app.
|
|
|
1198 |
*/
|
|
|
1199 |
@Deprecated
|
|
|
1200 |
public static String getAttributionId(ContentResolver contentResolver) {
|
|
|
1201 |
return Settings.getAttributionId(contentResolver);
|
|
|
1202 |
}
|
|
|
1203 |
|
|
|
1204 |
/**
|
|
|
1205 |
* Get the auto install publish setting. If true, an install event will be published during authorize(), unless
|
|
|
1206 |
* it has occurred previously or the app does not have install attribution enabled on the application's developer
|
|
|
1207 |
* config page.
|
|
|
1208 |
* <p/>
|
|
|
1209 |
* This method is deprecated. See {@link Facebook} and {@link Settings} for more info.
|
|
|
1210 |
*
|
|
|
1211 |
* @return a Boolean indicating whether installation of the app should be auto-published.
|
|
|
1212 |
*/
|
|
|
1213 |
@Deprecated
|
|
|
1214 |
public boolean getShouldAutoPublishInstall() {
|
|
|
1215 |
return Settings.getShouldAutoPublishInstall();
|
|
|
1216 |
}
|
|
|
1217 |
|
|
|
1218 |
/**
|
|
|
1219 |
* Sets whether auto publishing of installs will occur.
|
|
|
1220 |
* <p/>
|
|
|
1221 |
* This method is deprecated. See {@link Facebook} and {@link Settings} for more info.
|
|
|
1222 |
*
|
|
|
1223 |
* @param value a Boolean indicating whether installation of the app should be auto-published.
|
|
|
1224 |
*/
|
|
|
1225 |
@Deprecated
|
|
|
1226 |
public void setShouldAutoPublishInstall(boolean value) {
|
|
|
1227 |
Settings.setShouldAutoPublishInstall(value);
|
|
|
1228 |
}
|
|
|
1229 |
|
|
|
1230 |
/**
|
|
|
1231 |
* Callback interface for dialog requests.
|
|
|
1232 |
* <p/>
|
|
|
1233 |
* THIS CLASS SHOULD BE CONSIDERED DEPRECATED.
|
|
|
1234 |
* <p/>
|
|
|
1235 |
* All public members of this class are intentionally deprecated.
|
|
|
1236 |
* New code should instead use
|
|
|
1237 |
* {@link com.facebook.widget.WebDialog}
|
|
|
1238 |
* <p/>
|
|
|
1239 |
* Adding @Deprecated to this class causes warnings in other deprecated classes
|
|
|
1240 |
* that reference this one. That is the only reason this entire class is not
|
|
|
1241 |
* deprecated.
|
|
|
1242 |
*
|
|
|
1243 |
* @devDocDeprecated
|
|
|
1244 |
*/
|
|
|
1245 |
public static interface DialogListener {
|
|
|
1246 |
|
|
|
1247 |
/**
|
|
|
1248 |
* Called when a dialog completes.
|
|
|
1249 |
*
|
|
|
1250 |
* Executed by the thread that initiated the dialog.
|
|
|
1251 |
*
|
|
|
1252 |
* @param values
|
|
|
1253 |
* Key-value string pairs extracted from the response.
|
|
|
1254 |
*/
|
|
|
1255 |
public void onComplete(Bundle values);
|
|
|
1256 |
|
|
|
1257 |
/**
|
|
|
1258 |
* Called when a Facebook responds to a dialog with an error.
|
|
|
1259 |
*
|
|
|
1260 |
* Executed by the thread that initiated the dialog.
|
|
|
1261 |
*
|
|
|
1262 |
*/
|
|
|
1263 |
public void onFacebookError(FacebookError e);
|
|
|
1264 |
|
|
|
1265 |
/**
|
|
|
1266 |
* Called when a dialog has an error.
|
|
|
1267 |
*
|
|
|
1268 |
* Executed by the thread that initiated the dialog.
|
|
|
1269 |
*
|
|
|
1270 |
*/
|
|
|
1271 |
public void onError(DialogError e);
|
|
|
1272 |
|
|
|
1273 |
/**
|
|
|
1274 |
* Called when a dialog is canceled by the user.
|
|
|
1275 |
*
|
|
|
1276 |
* Executed by the thread that initiated the dialog.
|
|
|
1277 |
*
|
|
|
1278 |
*/
|
|
|
1279 |
public void onCancel();
|
|
|
1280 |
|
|
|
1281 |
}
|
|
|
1282 |
|
|
|
1283 |
/**
|
|
|
1284 |
* Callback interface for service requests.
|
|
|
1285 |
* <p/>
|
|
|
1286 |
* THIS CLASS SHOULD BE CONSIDERED DEPRECATED.
|
|
|
1287 |
* <p/>
|
|
|
1288 |
* All public members of this class are intentionally deprecated.
|
|
|
1289 |
* New code should instead use
|
|
|
1290 |
* {@link Session} to manage session state.
|
|
|
1291 |
* <p/>
|
|
|
1292 |
* Adding @Deprecated to this class causes warnings in other deprecated classes
|
|
|
1293 |
* that reference this one. That is the only reason this entire class is not
|
|
|
1294 |
* deprecated.
|
|
|
1295 |
*
|
|
|
1296 |
* @devDocDeprecated
|
|
|
1297 |
*/
|
|
|
1298 |
public static interface ServiceListener {
|
|
|
1299 |
|
|
|
1300 |
/**
|
|
|
1301 |
* Called when a service request completes.
|
|
|
1302 |
*
|
|
|
1303 |
* @param values
|
|
|
1304 |
* Key-value string pairs extracted from the response.
|
|
|
1305 |
*/
|
|
|
1306 |
public void onComplete(Bundle values);
|
|
|
1307 |
|
|
|
1308 |
/**
|
|
|
1309 |
* Called when a Facebook server responds to the request with an error.
|
|
|
1310 |
*/
|
|
|
1311 |
public void onFacebookError(FacebookError e);
|
|
|
1312 |
|
|
|
1313 |
/**
|
|
|
1314 |
* Called when a Facebook Service responds to the request with an error.
|
|
|
1315 |
*/
|
|
|
1316 |
public void onError(Error e);
|
|
|
1317 |
|
|
|
1318 |
}
|
|
|
1319 |
|
|
|
1320 |
@Deprecated
|
|
|
1321 |
public static final String FB_APP_SIGNATURE =
|
|
|
1322 |
"30820268308201d102044a9c4610300d06092a864886f70d0101040500307a310"
|
|
|
1323 |
+ "b3009060355040613025553310b30090603550408130243413112301006035504"
|
|
|
1324 |
+ "07130950616c6f20416c746f31183016060355040a130f46616365626f6f6b204"
|
|
|
1325 |
+ "d6f62696c653111300f060355040b130846616365626f6f6b311d301b06035504"
|
|
|
1326 |
+ "03131446616365626f6f6b20436f72706f726174696f6e3020170d30393038333"
|
|
|
1327 |
+ "13231353231365a180f32303530303932353231353231365a307a310b30090603"
|
|
|
1328 |
+ "55040613025553310b30090603550408130243413112301006035504071309506"
|
|
|
1329 |
+ "16c6f20416c746f31183016060355040a130f46616365626f6f6b204d6f62696c"
|
|
|
1330 |
+ "653111300f060355040b130846616365626f6f6b311d301b06035504031314466"
|
|
|
1331 |
+ "16365626f6f6b20436f72706f726174696f6e30819f300d06092a864886f70d01"
|
|
|
1332 |
+ "0101050003818d0030818902818100c207d51df8eb8c97d93ba0c8c1002c928fa"
|
|
|
1333 |
+ "b00dc1b42fca5e66e99cc3023ed2d214d822bc59e8e35ddcf5f44c7ae8ade50d7"
|
|
|
1334 |
+ "e0c434f500e6c131f4a2834f987fc46406115de2018ebbb0d5a3c261bd97581cc"
|
|
|
1335 |
+ "fef76afc7135a6d59e8855ecd7eacc8f8737e794c60a761c536b72b11fac8e603"
|
|
|
1336 |
+ "f5da1a2d54aa103b8a13c0dbc10203010001300d06092a864886f70d010104050"
|
|
|
1337 |
+ "0038181005ee9be8bcbb250648d3b741290a82a1c9dc2e76a0af2f2228f1d9f9c"
|
|
|
1338 |
+ "4007529c446a70175c5a900d5141812866db46be6559e2141616483998211f4a6"
|
|
|
1339 |
+ "73149fb2232a10d247663b26a9031e15f84bc1c74d141ff98a02d76f85b2c8ab2"
|
|
|
1340 |
+ "571b6469b232d8e768a7f7ca04f7abe4a775615916c07940656b58717457b42bd"
|
|
|
1341 |
+ "928a2";
|
|
|
1342 |
|
|
|
1343 |
}
|