I'm able to receive a GCM registration id (for push notifications) thanks to this google guide and store the reg.id in a database and without authentication everything works fine.
I use web api 2, oauth 2 authentication and account manager.
1) User sign in to application, App creates an account for the user.
- Or if an account exists auto sign the user in.
2) App gets auth token, if expired retrieves it via Volley string request
3) App checks UserData of Account Manager if reg. id received before. If not App requests an reg. id from GCM and posts it to the server via Volley(AuthToken is required here) and App sets a userdata in account that reg. id has received.
With the above flow of my app which is exists only in my mind at the moment, I've some questions.
First, how can I get auth token first and move to the step 3 which is IntentService according to the Guide.
Second, let's say we managed to do first question. What happens if user login to his account from a different device? Should I update his reg. id for his new device. But what if this device was temporary and he returns to use his permanent device? Notifications will be sent to tempopary device because it was the last device he signed in!
I'm really confused and will be apreciated for anyone who lights my way. Thanks.
Edit
Instead of following Google's guide (instead of using IntentService) is it possible getting both Authorization Token and Registration Id(Token) in a AsyncTask?
Just as answer for 2nd part of question.
Relation between user and his regId should be 1 to n. So 1 user can have multiple devices, and by this multiple regId. And when you would like to send message to this user - you should send multiple messages (1 to every device). Other possible solution is using lately introduced Device Group Messaging, and IMO is preferable way for it.
Thanks for your answers, it only works if I use two async tasks and here is my solution. Any criticism or recommendation will be welcome.
I create a TokenCheckerActivity and put it between login activity and main activity.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_token_checker);
webApiUri = "url Here";
tokenContext = this;
accountManager = accountManager.get(tokenContext);
PpSharedPreferences ppSharedPreferences = new PpSharedPreferences(tokenContext);
ppAuthenticator = new PpAuthenticator(tokenContext);
account = ppAuthenticator.getCurrentAccount(ppSharedPreferences.getUsername());
new GetAuthorizationToken().execute(ppSharedPreferences.getUsername());
}
Async Tasks
/**
* Gets the authorization token and checks if GCM registration id received.
* Gets reg is if not exists.
*/
private class GetAuthorizationToken extends AsyncTask<String,Void,String>{
#Override
protected String doInBackground(String... params) {
String username = params[0];
String mAuthToken = ppAuthenticator.getPpAuthToken(username);
return mAuthToken;
}
#Override
protected void onPostExecute(String authToken) {
if(!TextUtils.isEmpty(authToken))
{
final String gcmTokenSent = accountManager.getUserData(account, AccountGeneral.GCM_REGISTRATION_ID);
if (gcmTokenSent == null || !gcmTokenSent.equals("true")) {
new GetGcmRegistrationToken().execute(authToken);
} else {
// We have the Gcm Registration Id continue to the main activity
Intent intent = new Intent(tokenContext, MainActivity.class);
startActivity(intent);
finish();
}
}
}
}
private class GetGcmRegistrationToken extends AsyncTask<String,Void,PpTokens>{
#Override
protected PpTokens doInBackground(String... params) {
PpTokens tokens = new PpTokens();
tokens.setAuthToken(params[0]);
try {
if (checkPlayServices()) {
InstanceID instanceID = InstanceID.getInstance(tokenContext);
String regToken = instanceID.getToken(getString(R.string.gcm_defaultSenderId), GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
tokens.setRegToken(regToken);
}
} catch (IOException e) {
e.printStackTrace();
}
return tokens;
}
#Override
protected void onPostExecute(PpTokens tokens) {
if (!TextUtils.isEmpty(tokens.getRegToken()))
{
sendRegistrationToServer(tokens.getRegToken(),tokens.getAuthToken());
}
}
}
private class PpTokens
{
private String authToken;
private String regToken;
public String getAuthToken() {
return authToken;
}
public void setAuthToken(String authToken) {
this.authToken = authToken;
}
public String getRegToken() {
return regToken;
}
public void setRegToken(String regToken) {
this.regToken = regToken;
}
}
Send reg id to server
private void sendRegistrationToServer(String regToken, final String authToken) {
final String tag_json_obj = "json_obj_req";
String url = webApiUri + "?gcmRegistrationToken=" + regToken;
JsonObjectRequest objectRequest = new JsonObjectRequest(Request.Method.POST, url, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
Log.d("Name for Reg Token:", response.getString("Name"));
// You should store a boolean that indicates whether the generated token has been
// sent to your server. If the boolean is false, send the token to your server,
// otherwise your server should have already received the token.
accountManager.setUserData(account, AccountGeneral.GCM_REGISTRATION_ID, "true");
Intent intent = new Intent(tokenContext, MainActivity.class);
startActivity(intent);
finish();
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
String errorMessage = JsonErrorMessageHandler.onErrorResponse(error);
accountManager.setUserData(acc, AccountGeneral.GCM_REGISTRATION_ID, "false");
}
}) {
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> headers = new HashMap<String, String>();
headers.put("Authorization", "Bearer " + authToken);
return headers;
}
};
int socketTimeout = 5000;
int maxRetries = 3;
RetryPolicy policy = new DefaultRetryPolicy(socketTimeout, maxRetries, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT);
objectRequest.setRetryPolicy(policy);
// Adding request to request queue
AppController.getInstance().addToRequestQueue(objectRequest, tag_json_obj);
}
Related
I decided to use Volley and go the RESTful route with Firebase since their listeners seem to hang when there's no internet connection. At least with Volley, it lets me know if a network request was not successful due to internet connection or not.
I need to know whether FirebaseUser auth tokens expire or not. In my app, I only allow Google and Facebook authentication, and I use the following code assuming that Firebase user auth token DO NOT expire:
private String authToken;
// Callbacks
public interface ApiCallbacks {
public void onSuccess(JSONObject response);
public void onError(String errorString);
}
private interface AuthTokenCallbacks {
public void onAuthTokenSuccess();
public void onAuthTokenError(String errorString);
}
// Request Helpers
private void getAuthToken(final AuthTokenCallbacks callbacks) {
// Lazy instantiation
if (authToken == null) {
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
if (user == null) {
callbacks.onAuthTokenError("Please log in");
} else {
user.getToken(false).addOnCompleteListener(new OnCompleteListener<GetTokenResult>() {
#Override
public void onComplete(#NonNull Task<GetTokenResult> task) {
if (task.isSuccessful()) {
authToken = task.getResult().getToken();
callbacks.onAuthTokenSuccess();
} else {
callbacks.onAuthTokenError("Please log in");
}
}
});
}
} else {
callbacks.onAuthTokenSuccess();
}
}
public void sendGetRequestTo(final String endpoint, final HashMap<String, String> arguments, final RequestQueue requestQueue, final String tag, final ApiCallbacks callbacks) {
// Only execute get request if we have an auth token
getAuthToken(new AuthTokenCallbacks() {
#Override
public void onAuthTokenSuccess() {
final String requestUrl = getRequestUrlString(endpoint, arguments);
JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, requestUrl, null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
callbacks.onSuccess(response);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
callbacks.onError(error.toString());
}
});
request.setTag(tag);
requestQueue.add(request);
}
#Override
public void onAuthTokenError(String errorString) {
callbacks.onError("Please log in");
}
});
}
Is this the correct way of doing it? I just need to know if I'm going in the right direction because I don't want future problems with my auth tokens expiring (if they do).
EDIT
I forgot to mention that my final String requestUrl = getRequestUrlString(endpoint, arguments); method basically constructs the url request string with auth=authTokenString appended at the end of my url.
Yes, they do expire (you can check out the expiration date at jwt.io). If you don't force a refresh (i.e. user.getToken(false)), the returned token will be updated only if it has expired. If you pass true to getToken(...), a new token will be created which also involves the linked providers' token validation on the firebase servers (e.g. validating against Facebook whether the user still has his/her account linked). Note that the latter counts towards your daily token service quotas, so make sure you use it only when it's necessary.
I want to implement login with Facebook for my service(which can be accessed via web as well as a native android)
This is what I am doing for the web part:
User clicks on login to Facebook
It gets directed to fb's page , where it checks for credentials
It redirects to a page on my web service, with the details
Then, my own service creates an access token for its use, and sends it as a json output to client.
I would like to know how this can be achieved using android. If I use a web view inside my app, the user will have to login to Facebook (which I want to avoid). Instead, if he is logged in on Facebook using the Facebook app, it should directly go over to permissions.
If I use the native sdk for android, I don't know how to perform the redirect to my own server and generate an access token for my own website.
If you want to grant user access token for your website (via Dashboard -> Choose a platform), then you should implement login process provided by Facebook in your website, and under your application settings page (Under your Facebook Developer account page) enable your application for web.
Quick start: https://developers.facebook.com/docs/facebook-login/web
You can do the same in native android what you have done in web.
Just Facebook Authentication for android
I have fallen under the same scenario as yours I have successfully implemented by using web service of my own.
private void doFacebookLogin(View v) {
final View snakeBarView = v;
callbackManager = CallbackManager.Factory.create();
LoginManager.getInstance().logInWithReadPermissions(this, Arrays.asList("email", "public_profile"));
LoginManager.getInstance().registerCallback(callbackManager, new FacebookCallback<LoginResult>() {
#Override
public void onSuccess(LoginResult loginResult) {
Snackbar.make(snakeBarView, "Facebook Login Success", Snackbar.LENGTH_SHORT).show();
GraphRequest.newMeRequest(
loginResult.getAccessToken(), new GraphRequest.GraphJSONObjectCallback() {
#Override
public void onCompleted(JSONObject json, GraphResponse response) {
if (response.getError() != null) {
// handle error
System.out.println("ERROR");
} else {
System.out.println("Success");
try {
String jsonresult = String.valueOf(json);
String emailId, id, name, profilePicture;
if (json.isNull("id"))
id = "";
else
id = json.getString("id");
if (json.isNull("email"))
emailId = id + "#facebook.com";
else
emailId = json.getString("email");
if (json.isNull("name"))
name = "";
else
name = json.getString("name");
profilePicture = getString(R.string.fbProfilePicUrl) + id + "/picture?type=large";
Registration registration = new Registration();
registration.Name = name;
registration.EmailId = emailId.toUpperCase();
registration.ProfilePicture = profilePicture;
registration.PhoneNumber = "";
registration.Area = "";
registration.Password = "";
ghilliHelper.signupOrLoginPreStep(registration);
} catch (JSONException e) {
e.printStackTrace();
}
}
}
}).executeAsync();
}
#Override
public void onCancel() {
Snackbar.make(snakeBarView, "Facebook Login Cancelled", Snackbar.LENGTH_SHORT).show();
}
#Override
public void onError(FacebookException e) {
Snackbar.make(snakeBarView, "Facebook Login Error", Snackbar.LENGTH_SHORT).show();
}
});
}
After successfull facebook login get email id and call your service from app.
On your service.
Check the email id is exist or not
if email id does not exist create an account and return a JSON as success login
else return JSON as success login.
Web Service
[HttpPost]
public async Task<string> SocialMediaLogin(Registration reg)
{
LoginResult logResult = new LoginResult();
try
{
CloudTableClient tableclient = _storageAccount.CreateCloudTableClient();
CloudTable table = tableclient.GetTableReference(_registrationTable);
if (!table.Exists())
{
logResult.Failure = true;
}
else
{
TableQuery<Registration> query = new TableQuery<Registration>().Where(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, reg.EmailId.ToUpper()));
Registration reg_obj2 = new Registration();
IEnumerable<Registration> ienum_obj = table.ExecuteQuery(query);
if (ienum_obj.Count() != 0)
{
foreach (var a in ienum_obj)
{
logResult.Success = true;
logResult.UserDetails = a;
}
}
else
{
RegistrationResult signupResult = await SocialMediaSignup(reg); //Create an Account
if (signupResult.Success)
{
logResult.Success = true;
logResult.UserDetails = signupResult.UserDetails;
}
else if (signupResult.Failure)
{
logResult.Failure = true;
}
}
}
}
catch (Exception)
{
logResult.Failure = true;
}
var resultString = JsonConvert.SerializeObject(logResult);
return resultString;
}
I am trying to understand the code behind Google's GCM quickstart example. Specifically, I don't understand how the code checks whether registration is already done.
MainActivity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
...
if (checkPlayServices()) {
// Start IntentService to register this application with GCM.
Intent intent = new Intent(this, RegistrationIntentService.class);
startService(intent);
}
}
RegistrationIntentService:
#Override
protected void onHandleIntent(Intent intent)
{
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
try {
// [START register_for_gcm]
// Initially this call goes out to the network to retrieve the token, subsequent calls
// are local.
// [START get_token]
InstanceID instanceID = InstanceID.getInstance(this);
// R.string.gcm_defaultSenderId (the Sender ID) is typically derived from google-services.json.
// See https://developers.google.com/cloud-messaging/android/start for details on this file.
String token = instanceID.getToken(getString(R.string.gcm_defaultSenderId),
GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
// [END get_token]
Log.i(TAG, "GCM Registration Token: " + token);
// TODO: Implement this method to send any registration to your app's servers.
sendRegistrationToServer(token);
// You should store a boolean that indicates whether the generated token has been
// sent to your server. If the boolean is false, send the token to your server,
// otherwise your server should have already received the token.
sharedPreferences.edit().putBoolean(AppSharedPreferences.SENT_TOKEN_TO_SERVER, true).apply();
// [END register_for_gcm]
} catch (Exception e) {
Log.d(TAG, "Failed to complete token refresh", e);
// If an exception happens while fetching the new token or updating our registration data
// on a third-party server, this ensures that we'll attempt the update at a later time.
sharedPreferences.edit().putBoolean(AppSharedPreferences.SENT_TOKEN_TO_SERVER, false).apply();
}
// Notify UI that registration has completed, so the progress indicator can be hidden.
Intent registrationComplete = new Intent(QuickstartPreferences.REGISTRATION_COMPLETE);
LocalBroadcastManager.getInstance(this).sendBroadcast(registrationComplete);
}
In RegistrationIntentService, the comments say that initially the call goes to retrieve the token, but subsequent calls are local. Does this mean that it will simply check to see if the app already has the token and not make the call anymore? I really don't understand this portion and I don't see anywhere in this example code where it checks for the existence of the token.
For that logic, you can refer to my working sample code:
public class MainActivity extends AppCompatActivity {
private final Context mContext = this;
private final String SENDER_ID = "425...."; // Project Number at https://console.developers.google.com/project/....
private final String SHARD_PREF = "com.example.gcmclient_preferences";
private final String GCM_TOKEN = "gcmtoken";
private final String LOG_TAG = "GCM";
public static TextView mTextView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SharedPreferences appPrefs = mContext.getSharedPreferences(SHARD_PREF, Context.MODE_PRIVATE);
String token = appPrefs.getString(GCM_TOKEN, "");
if (token.isEmpty()) {
try {
getGCMToken();
} catch (Exception e) {
e.printStackTrace();
}
}
mTextView = (TextView) findViewById(R.id.textView);
}
private void getGCMToken() {
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
try {
InstanceID instanceID = InstanceID.getInstance(mContext);
String token = instanceID.getToken(SENDER_ID, GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
if (token != null && !token.isEmpty()) {
SharedPreferences appPrefs = mContext.getSharedPreferences(SHARD_PREF, Context.MODE_PRIVATE);
SharedPreferences.Editor prefsEditor = appPrefs.edit();
prefsEditor.putString(GCM_TOKEN, token);
prefsEditor.apply();
}
Log.i(LOG_TAG, token);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}.execute();
}
}
You can read my answer at the following for more information
Adding Google Cloud Messagin (GCM) for Android - Registration process
Hope this helps!
InstanceID which is part of the Google Play Services library will check if there is a cached token and return that when getToken is called. If there is no cached token then it will go out to the network to retrieve a new token and return that.
Thus your application has to handle the possibility of that calling InstanceID.getToken will result in a network call. Hence the reason it is called in an IntentService.
I have an activity that call an Otto Event called LoadAuthenticateEvent this event then goes to my ClientManager.java where the following code is:
#Subscribe
public void onLoadAuthenticateEvent(LoadAuthenticateEvent loadAuthenticateEvent) {
// GCM cannot register on the main thread
String deviceID = "";
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
String differentId = GCMRegistrationUtil.registerDevice(mContext);
Log.d(TAG, "Device Id: " + differentId);
}
});
thread.start();
String email = loadAuthenticateEvent.getEmail();
String password = loadAuthenticateEvent.getPassword();
Callback<User> callback = new Callback<User>() {
#Override
public void success(User user, Response response) {
sClient.setOrganization(user.getRole().getOrganization().getSubdomain());
mBus.post(new LoadedMeEvent(user));
}
#Override
public void failure(RetrofitError retrofitError) {
mBus.post(new LoadedErrorEvent(retrofitError));
}
};
sClient.authenticate(email, password, deviceID, PLATFORM, callback);
}
The problem is that the server needs the deviceID, but GCM requires that a call be asynchronous and not on the main thread, how should I go about implementing this where I can properly get the deviceID and then pass it to sClient? Since it is possible that deviceID might be empty.
The best way is to contact GCM is through services.
Creates a IntentService to catch the intent released from the
activity
onHandleIntent(Intent intent)
Device sends service request GCM and receives the tokenID.
InstanceID instanceID = InstanceID.getInstance(this);
String token = instanceID.getToken(getString(R.string.gcm_defaultSenderId),
GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
Implement this method to send any registration to your app's servers.
sendRegistrationToserver(token)
Notify UI that registrationComplete
Intent registrationComplete = new Intent(GcmUtils.REGISTRATION_COMPLETE);
LocalBroadcastManager.getInstance(this).sendBroadcast(registrationComplete);
(Optional) Subscribe to topic channels
private void subscribeTopics(String token, ArrayList topics_gcm)throws IOException {
for (String topic : topics_gcm) {
GcmPubSub pubSub = GcmPubSub.getInstance(this);
pubSub.subscribe(token, topic, null);
}
}
Full IntentService:
public class RegistrationIntentService extends IntentService {
private static final String TAG = "RegIntentService";
public RegistrationIntentService() {
super(TAG);
}
#Override
protected void onHandleIntent(Intent intent) {
ArrayList<String> topics_gcm = intent.getStringArrayListExtra("topics_gcm");
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
try {
// In the (unlikely) event that multiple refresh operations occur simultaneously,
// ensure that they are processed sequentially.
synchronized (TAG) {
// Initially this call goes out to the network to retrieve the token, subsequent calls
// are local.
// [START get_token]
InstanceID instanceID = InstanceID.getInstance(this);
String token = instanceID.getToken(getString(R.string.gcm_defaultSenderId),
GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
// [END get_token]
Log.i(TAG, "GCM Registration Token: " + token);
// TODO: Implement this method to send any registration to your app's servers.
//sendRegistrationToServer(token);
// TODO: Subscribe to topic channels
//subscribeTopics(token, topics_gcm);
// You should store a boolean that indicates whether the generated token has been
// sent to your server. If the boolean is false, send the token to your server,
// otherwise your server should have already received the token.
sharedPreferences.edit().putBoolean(GcmUtils.SENT_TOKEN_TO_SERVER, true).apply();
// [END register_for_gcm]
}
} catch (Exception e) {
Log.d(TAG, "Failed to complete token refresh", e);
// If an exception happens while fetching the new token or updating our registration data
// on a third-party server, this ensures that we'll attempt the update at a later time.
sharedPreferences.edit().putBoolean(GcmUtils.SENT_TOKEN_TO_SERVER, false).apply();
}
// Notify UI that registration has completed, so the progress indicator can be hidden.
Intent registrationComplete = new Intent(GcmUtils.REGISTRATION_COMPLETE);
LocalBroadcastManager.getInstance(this).sendBroadcast(registrationComplete);
}
/**
* Persist registration to third-party servers.
*
* Modify this method to associate the user's GCM registration token with any server-side account
* maintained by your application.
*
* #param token The new token.
*/
private void sendRegistrationToServer(String token) {
// Add custom implementation, as needed.
}
/**
* Subscribe to any GCM topics of interest, as defined by the TOPICS constant.
*
* #param token GCM token
* #throws IOException if unable to reach the GCM PubSub service
*/
// [START subscribe_topics]
private void subscribeTopics(String token, ArrayList<String> topics_gcm) throws IOException {
for (String topic : topics_gcm) {
GcmPubSub pubSub = GcmPubSub.getInstance(this);
pubSub.subscribe(token, topic, null);
}
}
// [END subscribe_topics]
}
Start IntentService from any context: activity, service....
Intent intent = new Intent(getContext(), egistrationIntentService.class);
intent.putCharSequenceArrayListExtra("topics_gcm", topcics_gcm);
getContext().startService(intent);
If you want to make the sClient call on the UI thread (not sure if that is a good solution for you), use a Handler to call it once your GCM registration ID is returned by GCM.
The accepted answer here has sample code to help you out.
I ended up using an AsyncTask for this specific thing like so:
private void registerInBackground() {
new AsyncTask<Void, Void, String>() {
#Override
protected String doInBackground(Void... params) {
String regId = "";
try {
if (gcm == null) {
gcm = GoogleCloudMessaging.getInstance(mContext);
}
regId = gcm.register(GCMConfig.getSenderId());
storeRegistrationId(mContext, regId);
} catch (IOException e) {
Log.e(TAG, "Error: ", e);
e.printStackTrace();
}
Log.d(TAG, "GCM AsyncTask completed");
return regId;
}
#Override
protected void onPostExecute(String result) {
// Get the message
Log.d(TAG, "Registered with GCM Result: " + result);
}
}.execute(null, null, null);
}
This worked really well inside the GCMRegistration class
I am using the Facebook SDK to post messages on walls.
Now I need to fetch the Facebook friends list. Can anybody help me with this?
-- Edit --
try {
Facebook mFacebook = new Facebook(Constants.FB_APP_ID);
AsyncFacebookRunner mAsyncRunner = new AsyncFacebookRunner(mFacebook);
Bundle bundle = new Bundle();
bundle.putString("fields", "birthday");
mFacebook.request("me/friends", bundle);
} catch(Exception e){
Log.e(Constants.LOGTAG, " " + CLASSTAG + " Exception = "+e.getMessage());
}
When I execute my activity, I'm not seeing anything, but in LogCat there is a debug message like:
06-04 17:43:13.863: DEBUG/Facebook-Util(409): GET URL: https://graph.facebook.com/me/friends?format=json&fields=birthday
And when I tried to access this url directly from the browser, I'm getting the following error response:
{
error: {
type: "OAuthException"
message: "An active access token must be used to query information about the current user."
}
}
You are about half way there. You've sent the request, but you haven't defined anything to receive the response with your results. You can extend BaseRequestListener class and implement its onComplete method to do that. Something like this:
public class FriendListRequestListener extends BaseRequestListener {
public void onComplete(final String response) {
_error = null;
try {
JSONObject json = Util.parseJson(response);
final JSONArray friends = json.getJSONArray("data");
FacebookActivity.this.runOnUiThread(new Runnable() {
public void run() {
// Do stuff here with your friends array,
// which is an array of JSONObjects.
}
});
} catch (JSONException e) {
_error = "JSON Error in response";
} catch (FacebookError e) {
_error = "Facebook Error: " + e.getMessage();
}
if (_error != null)
{
FacebookActivity.this.runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(getApplicationContext(), "Error occurred: " +
_error, Toast.LENGTH_LONG).show();
}
});
}
}
}
Then in your request you can specify the request listener to use for receiving the response from the request, like this:
mFacebook.request("me/friends", bundle, new FriendListRequestListener());
Using FQL Query
String fqlQuery = "SELECT uid, name, pic_square FROM user WHERE uid IN " +
"(SELECT uid2 FROM friend WHERE uid1 = me() LIMIT 25)";
Bundle params = new Bundle();
params.putString("q", fqlQuery);
Session session = Session.getActiveSession();
Request request = new Request(session,"/fql", params,HttpMethod.GET, new Request.Callback(){
public void onCompleted(Response response) {
Log.i(TAG, "Result: " + response.toString());
try{
GraphObject graphObject = response.getGraphObject();
JSONObject jsonObject = graphObject.getInnerJSONObject();
Log.d("data", jsonObject.toString(0));
JSONArray array = jsonObject.getJSONArray("data");
for(int i=0;i<array.length();i++)
{
JSONObject friend = array.getJSONObject(i);
Log.d("uid",friend.getString("uid"));
Log.d("name", friend.getString("name"));
Log.d("pic_square",friend.getString("pic_square"));
}
}catch(JSONException e){
e.printStackTrace();
}
}
});
Request.executeBatchAsync(request);
I was dealing with that and I found the answer.
The problem is that you want to access to your data without previous registration with your facebook token.
First, you must to define your Facebook variable:
Facebook mFacebook = new Facebook(getString(R.string.fb_id));
Later, define your AsyncFacebookRunner:
final AsyncFacebookRunner mAsyncRunner = new AsyncFacebookRunner(mFacebook);
Ok, now you must to authorize your request, with autorize method. Note that you must implement callback methods on DialogListener(), put attention on onComplete() method. On that method you must to run the friend fetch request. Now your request will pass because now you are authenticated.
Now the code:
mFacebook.authorize(this, fb_perms, new DialogListener(){
/**
* Triggered on a successful Facebook registration.
*/
public void onComplete(Bundle values) {
mAsyncRunner.request("me/friends", new FriendListRequestListener());
}
/**
* Triggered on a FacebookError.
*/
public void onFacebookError(FacebookError e) {
}
/**
* Triggered on a DialogError.
*/
public void onError(DialogError e) {
}
/**
* Triggered when the User cancels the Facebook Login.
*/
public void onCancel() {
}
});
You can use the FriendListRequestListener class that was post by #Kon
I hope this helps.
Cheers!
Hi Please check below link
Facebook API for Android: how to get extended info regarding user`s friends?
Post on user's friends facebook wall through android application
I faced the same problem yesterday.
I wondering if you have overriden the onActivityResult() function?
private Facebook mFacebook=null;
...
...
...
#Override
protected void onActivityResult(int requestCode,
int resultCode,
Intent data) {
mFacebook.authorizeCallback(requestCode, resultCode, data);
}
Check this answer in order to know how to get Facebook friend list and who of these friends have installed your app using new Facebook SDK 3.0 for Android.
I'm doing something like this is working good....
jsonObj = Util.parseJson(facebook.request("me/friends"));
JSONArray jArray = jsonObj.getJSONArray("data");
for(int i=0;i<jArray.length();i++){
JSONObject json_data = jArray.getJSONObject(i);
Log.v("THIS IS DATA", i+" : "+jArray.getJSONObject(i));
}
Hope it is helpful...
If somebody is reading this in late 2015, the answer and code is quite simple. Take a look at official documentation here (in the code window click on Android SDK).
Keep in mind that you need to have user_friends permission.
Bro tip for android-rx users - get your friends synchronously, then process message and return Observable:
public Observable<List<Friend>> execute() {
return Observable.defer(new Func0<Observable<List<Friend>>>() {
#Override
public Observable<List<Friend>> call() {
AccessToken a = AccessToken.getCurrentAccessToken();
if (Utils.isValid(a)) {
try {
GraphResponse res = new GraphRequest(AccessToken.getCurrentAccessToken(), "me/friends").executeAndWait();
// process result
return Observable.just(your friend list);
} catch (Exception e) {
// notify user or load again
}
}
// invalid access token -> notify user
}
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
}