I'm unable to get an access token to provide an email address. I'm using GoogleAuthUtil. (the result says the token is invalid)
I use this:
mGoogleApiClient = new GoogleApiClient.Builder (cordova.getActivity().getApplicationContext())
.addConnectionCallbacks (this)
.addOnConnectionFailedListener (this)
.addApi (Games.API)
.addScope (Games.SCOPE_GAMES)
.addApi(Plus.API)
.addScope(Plus.SCOPE_PLUS_PROFILE)
.addScope(Plus.SCOPE_PLUS_LOGIN)
.build ();
And this:
scope = "oauth2:" + Scopes.PROFILE + " " + "email";
Dropping " email" works to give me a valid access token -- but there's no email address in the response.
Here is my code.
package com.flyingsoftgames.googleplayservices;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.Scopes;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.games.Players;
import com.google.android.gms.games.Games;
import com.google.android.gms.auth.GoogleAuthException;
import com.google.android.gms.auth.GoogleAuthUtil;
import com.google.android.gms.auth.UserRecoverableAuthException;
import com.google.android.gms.auth.GooglePlayServicesAvailabilityException;
import com.google.android.gms.plus.Account;
import com.google.android.gms.plus.Plus;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaInterface;
import org.apache.cordova.CordovaWebView;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.PluginResult;
import org.apache.cordova.PluginResult.Status;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender.SendIntentException;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import org.apache.cordova.*;
import java.io.IOException;
import android.util.Log;
public class GooglePlayServices extends CordovaPlugin implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
private static final String LOG_TAG = "GooglePlayServices";
private static final int REQ_SIGN_IN_REQUIRED = 55664;
public static GoogleApiClient mGoogleApiClient = null;
public CallbackContext tryConnectCallback = null;
public String accessToken = "";
private int connectionAttempts = 0;
#Override public void onConnectionFailed (ConnectionResult result) {
String errormessage = result.toString();
Log.w (LOG_TAG, errormessage);
connectionAttempts += 1;
if (!result.hasResolution() || connectionAttempts >= 2) {
Log.w (LOG_TAG, "Error: no resolution. Google Play Services connection failed.");
tryConnectCallback.error ("Error: " + errormessage + "."); tryConnectCallback = null;
return;
}
try {
result.startResolutionForResult (cordova.getActivity(), result.getErrorCode());
} catch (SendIntentException e) {
// There was an error with the resolution intent. Try again.
mGoogleApiClient.connect ();
}
}
#Override public void onConnected (Bundle connectionHint) {
String mAccountName = Plus.AccountApi.getAccountName(mGoogleApiClient);
new RetrieveTokenTask().execute (mAccountName);
Games.setViewForPopups (mGoogleApiClient, webView);
}
public void onActivityResult (int requestCode, int responseCode, Intent intent) {
if (!mGoogleApiClient.isConnecting()) mGoogleApiClient.connect ();
}
#Override public void onConnectionSuspended (int cause) {
mGoogleApiClient.connect ();
}
public boolean execute (String action, JSONArray inputs, CallbackContext callbackContext) throws JSONException {
if ("getPlayerId".equals(action)) {
String playerId = Games.Players.getCurrentPlayerId (mGoogleApiClient);
callbackContext.sendPluginResult (new PluginResult (PluginResult.Status.OK, playerId));
} else if ("tryConnect".equals(action)) {
tryConnect (callbackContext);
} else if ("getAccessToken".equals(action)) {
callbackContext.sendPluginResult (new PluginResult (PluginResult.Status.OK, accessToken));
}
return true;
}
// tryConnect runs the callback with a value of false if Google Play Services isn't available.
public void tryConnect (CallbackContext callbackContext) {
boolean isGpsAvailable = (GooglePlayServicesUtil.isGooglePlayServicesAvailable(cordova.getActivity()) == ConnectionResult.SUCCESS);
if (!isGpsAvailable) {
callbackContext.sendPluginResult (new PluginResult (PluginResult.Status.OK, false));
return;
}
tryConnectCallback = callbackContext;
mGoogleApiClient = new GoogleApiClient.Builder (cordova.getActivity().getApplicationContext())
.addConnectionCallbacks (this)
.addOnConnectionFailedListener (this)
.addApi (Games.API)
.addScope (Games.SCOPE_GAMES)
.addApi(Plus.API)
.addScope(Plus.SCOPE_PLUS_PROFILE)
.addScope(Plus.SCOPE_PLUS_LOGIN)
.build ();
mGoogleApiClient.connect ();
}
private class RetrieveTokenTask extends AsyncTask<String, Void, String> {
#Override protected String doInBackground (String... params) {
String accountName = params[0];
String scope = "oauth2:" + Scopes.PROFILE + " " + "email";
Context context = cordova.getActivity().getApplicationContext();
try {
accessToken = GoogleAuthUtil.getToken (context, accountName, scope);
} catch (IOException e) {
String errormessage = e.getMessage();
Log.e (LOG_TAG, errormessage);
if (tryConnectCallback != null) tryConnectCallback.error ("Error: " + errormessage + "."); tryConnectCallback = null;
} catch (UserRecoverableAuthException e) {
cordova.getActivity().startActivityForResult (e.getIntent(), REQ_SIGN_IN_REQUIRED);
} catch (GoogleAuthException e) {
String errormessage = e.getMessage();
Log.e (LOG_TAG, errormessage);
if (tryConnectCallback != null) tryConnectCallback.error ("Error: " + errormessage + "."); tryConnectCallback = null;
}
return accessToken;
}
#Override protected void onPostExecute (String newAccessToken) {
super.onPostExecute (newAccessToken);
accessToken = newAccessToken;
if (tryConnectCallback != null) {
String playerId = Games.Players.getCurrentPlayerId (mGoogleApiClient);
tryConnectCallback.sendPluginResult (new PluginResult (PluginResult.Status.OK, playerId));
tryConnectCallback = null;
}
}
}
}
It seems that clearing an app's permission in https://security.google.com/settings/security/permissions?hl=en does trigger a re-authorization screen, but the access token that is received from .getToken is stale. GoogleAuthUtil.clearToken (context, accessToken); needs to be used to clear the old token, or the old token must time out. Thus, we can do:
accessToken = GoogleAuthUtil.getToken(context, accountName, scope);
GoogleAuthUtil.clearToken (context, accessToken);
accessToken = GoogleAuthUtil.getToken(context, accountName, scope);
However:
It seems that this only resolves after a few iterations of re-launching the app, because GoogleAuthUtil.getToken is asynchronous when new permissions are required, and two SEPARATE permission screens pop up: one from Google (triggered from GoogleAuthUtil.getToken and one from Google+ (triggered from GoogleApiClient.Builder).
Edit:
Using the AccountPicker class eliminates the need to use GoogleApiClient.Builder!
See:
https://github.com/agamemnus/googleplaytoken
Related
I am trying to learn how to implement safetynet with the reference of a fellow member in stackover. SafetyNet: package name always return null
The first section of the code is a complete code of SafetyNetVerifier
package com.example.stack;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.FragmentActivity;
import android.util.Base64;
import android.util.Log;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.safetynet.SafetyNet;
import com.google.android.gms.safetynet.SafetyNetApi;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.Random;
public class SafetyNetVerifier implements GoogleApiClient.OnConnectionFailedListener {
private final Random mRandom = new SecureRandom();
private String mResult;
private GoogleApiClient mGoogleApiClient;
private FragmentActivity activity;
public SafetyNetVerifier(FragmentActivity activity) {
this.activity = activity;
buildGoogleApiClient();
sendSafetyNetRequest();
}
private byte[] getRequestNonce(String data) {
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
byte[] bytes = new byte[24];
mRandom.nextBytes(bytes);
try {
byteStream.write(bytes);
byteStream.write(data.getBytes());
} catch (IOException e) {
return null;
}
return byteStream.toByteArray();
}
protected synchronized void buildGoogleApiClient() {
mGoogleApiClient = new GoogleApiClient.Builder(activity)
.addApi(SafetyNet.API)
.enableAutoManage(activity, this)
.build();
}
private void sendSafetyNetRequest() {
Log.e("hqthao", "Sending SafetyNet API request.");
String nonceData = "Safety Net Sample: " + System.currentTimeMillis();
byte[] nonce = getRequestNonce(nonceData);
SafetyNet.SafetyNetApi.attest(mGoogleApiClient, nonce)
.setResultCallback(new ResultCallback<SafetyNetApi.AttestationResult>() {
#Override
public void onResult(SafetyNetApi.AttestationResult result) {
Status status = result.getStatus();
if (status.isSuccess()) {
mResult = result.getJwsResult();
Log.e("hqthao", "Success! SafetyNet result:\n" + mResult + "\n");
SafetyNetResponse response = parseJsonWebSignature(mResult);
Log.e("hqthao", response.toString());
}
}
});
}
#Nullable
private SafetyNetResponse parseJsonWebSignature(String jwsResult) {
if (jwsResult == null) {
return null;
}
//the JWT (JSON WEB TOKEN) is just a 3 base64 encoded parts concatenated by a . character
final String[] jwtParts = jwsResult.split("\\.");
if (jwtParts.length == 3) {
//we're only really interested in the body/payload
String decodedPayload = new String(Base64.decode(jwtParts[1], Base64.DEFAULT));
return SafetyNetResponse.parse(decodedPayload);
} else {
return null;
}
}
#Override
public void onConnectionFailed(#NonNull ConnectionResult connectionResult) {
Log.e("hqthao", "Error connecting to Google Play Services." + connectionResult.getErrorMessage());
}
}
When i tried to debug, it will always stop at
SafetyNet.SafetyNetApi.attest(mGoogleApiClient, nonce)
May I know why is it so? I look at the Safetynet example provide by google and they will usually pair the API Key with the nonce. How can i change mGoogleApiClient to a API KEY?
private void sendSafetyNetRequest() {
Log.e("hqthao", "Sending SafetyNet API request.");
String nonceData = "Safety Net Sample: " + System.currentTimeMillis();
byte[] nonce = getRequestNonce(nonceData);
SafetyNet.SafetyNetApi.attest(mGoogleApiClient, nonce)
.setResultCallback(new ResultCallback<SafetyNetApi.AttestationResult>() {
#Override
public void onResult(SafetyNetApi.AttestationResult result) {
Status status = result.getStatus();
if (status.isSuccess()) {
mResult = result.getJwsResult();
Log.e("hqthao", "Success! SafetyNet result:\n" + mResult + "\n");
SafetyNetResponse response = parseJsonWebSignature(mResult);
Log.e("hqthao", response.toString());
}
}
});
}
You should use the SafetyNetClient interface to test. The sample code is as follows:
SafetyNetClient client = SafetyNet.getClient(getActivity());
Task<SafetyNetApi.AttestationResponse> task = client.attest(nonce, BuildConfig.API_KEY);
You can refer to the latest code on GitHub:
https://github.com/googlesamples/android-play-safetynet/blob/master/client/java/SafetyNetSample/Application/src/main/java/com/example/android/safetynetsample/SafetyNetSampleFragment.java
From my understanding, you will need an instance of SafetyNetClient to execute the attest method and not SafetyNetApi.attest, which is deprecated and now disabled
In order to get the Client, use:
SafetyNet.getClient(this).attest()
See here and the example here for more details
Hi we are trying to get Google Play subscriptions information connected to one of our Android apps. The first step is authorize and get subscriptions json
Has anyone succeed to work with Google Play Developer API? Authorization? Subscriptions list?
Below is our experience
When I use authScope = "https://www.googleapis.com/auth/androidpublisher";
(took it from Google Example)
private static final String authScope = "https://www.googleapis.com/auth/androidpublisher";
am.getAuthToken(
myAccount, // Account retrieved using getAccountsByType()
authScope, // Auth scope
options, // Authenticator-specific options
this, // Your activity
new OnTokenAcquired(), // Callback called when a token is successfully acquired
new Handler(new OnError())); // Callback called if an error occurs
private class OnTokenAcquired implements AccountManagerCallback<Bundle> {
#Override
public void run(AccountManagerFuture<Bundle> accountManagerFuture) {
addProcessMessage("OnTokenAcquired");
// Get the result of the operation from the AccountManagerFuture.
try {
Bundle bundle = accountManagerFuture.getResult();
// The token is a named value in the bundle. The name of the value
// is stored in the constant AccountManager.KEY_AUTHTOKEN.
String token = bundle.getString(AccountManager.KEY_AUTHTOKEN);
addProcessMessage("OnTokenAcquired token=" + token);
processAcessSubscriptions(token);
} catch ( Exception ae ) {
Log.e(TAG, "OnTokenAcquired Exception=" + ae.getMessage(), ae);
setProcessFailed(ae.getMessage());
}
}
}
I can’t get token, because I get exception below
OnTokenAcquired Exception=Unknown
android.accounts.AuthenticatorException: Unknown
at android.accounts.AccountManager.convertErrorToException(AccountManager.java:2213)
at android.accounts.AccountManager.-wrap0(AccountManager.java)
at android.accounts.AccountManager$AmsTask$Response.onError(AccountManager.java:2056)
at android.accounts.IAccountManagerResponse$Stub.onTransact(IAccountManagerResponse.java:69)
at android.os.Binder.execTransact(Binder.java:565)
When I use authScope = "Manage your tasks";
(took it from another Google Example)
private static final String authScope = "Manage your tasks";
I'm receiving token, but on ProductPurchase product = purchases.products().get(PACKAGE_NAME, TEST_SKU, token).execute();
I am getting getProducts Exception=400 Bad Request
See below
HttpTransport httpTransport = AndroidHttp.newCompatibleTransport();
JacksonFactory jsonFactory = JacksonFactory.getDefaultInstance();
AssetManager assetManager = getAssets();
InputStream is = assetManager.open(CLIENT_SECRET_JSON);
GoogleCredential credential = GoogleCredential.fromStream(is).createScoped(Collections.singleton(AndroidPublisherScopes.ANDROIDPUBLISHER));
AndroidPublisher publisher = new AndroidPublisher.Builder(httpTransport, jsonFactory, credential).setApplicationName(APP_NAME).build();
AndroidPublisher.Purchases purchases = publisher.purchases();
ProductPurchase product = purchases.products().get(PACKAGE_NAME, TEST_SKU, token).execute();
getProducts Exception=400 Bad Request
{
"code" : 400,
"errors" : [ {
"domain" : "global",
"message" : "Invalid Value",
"reason" : "invalid"
} ],
"message" : "Invalid Value"
}
com.google.api.client.googleapis.json.GoogleJsonResponseException: 400 Bad Request
{
"code" : 400,
"errors" : [ {
"domain" : "global",
"message" : "Invalid Value",
"reason" : "invalid"
} ],
"message" : "Invalid Value"
}
at com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest.newExceptionOnError(AbstractGoogleJsonClientRequest.java:113)
at com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest.newExceptionOnError(AbstractGoogleJsonClientRequest.java:40)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest$1.interceptResponse(AbstractGoogleClientRequest.java:321)
at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:1065)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:419)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:352)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.execute(AbstractGoogleClientRequest.java:469)
Main Activity
package com.lemi.affilatemanager;
import android.Manifest;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AccountManagerCallback;
import android.accounts.AccountManagerFuture;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.google.api.client.extensions.android.http.AndroidHttp;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.services.androidpublisher.AndroidPublisher;
import com.google.api.services.androidpublisher.AndroidPublisherScopes;
import com.google.api.services.androidpublisher.model.ProductPurchase;
import java.io.InputStream;
import java.util.Collections;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private static final int DIALOG_ACCOUNTS = 11;
public static final String CLIENT_SECRET_JSON = "service_account.json";//"client_secret.json";
public static final String APP_NAME = "AffilateManager";
public static final String PACKAGE_NAME = "com.lemi.keywordmarketingautoresponder";
private AccountManager accountManager;
private Account myAccount;
protected static final int PERMISSION_REQUEST = 10;
private TextView resultText;
private StringBuilder testMessage = new StringBuilder();
private ProgressBar progressBar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
accountManager = AccountManager.get(this);
setContentView(R.layout.activity_main);
resultText = (TextView) findViewById(R.id.test_result);
progressBar = (ProgressBar) findViewById(R.id.progress_bar);
Button startProcessButton = (Button) findViewById(R.id.start_process_btn);
startProcessButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
test();
}
});
}
#Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case DIALOG_ACCOUNTS:
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Select a Google account");
final Account[] accounts = accountManager.getAccountsByType("com.google");
final int size = accounts.length;
String[] names = new String[size];
for (int i = 0; i < size; i++) {
names[i] = accounts[i].name;
}
builder.setItems(names, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// Stuff to do when the account is selected by the user
myAccount = accounts[which];
processGetAuthToken();
}
});
return builder.create();
}
return null;
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
Log.i(TAG, "onRequestPermissionsResult requestCode=" + requestCode);
switch (requestCode) {
case PERMISSION_REQUEST:
test();
break;
default:
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
private void test () {
boolean canReadContacts = ContextCompat.checkSelfPermission(this, Manifest.permission.GET_ACCOUNTS) == PackageManager.PERMISSION_GRANTED;
Log.i(TAG, "test canReadContacts=" + canReadContacts);
if ( !canReadContacts ) {
ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.GET_ACCOUNTS}, PERMISSION_REQUEST);
return;
}
startProcess();
if ( getAccount() ) {
processGetAuthToken();
}
}
private void startProcess () {
progressBar.setVisibility(View.VISIBLE);
testMessage.delete(0, testMessage.length());
resultText.setText(testMessage);
}
private void setProcessFailed ( final String message ) {
runOnUiThread(new Runnable() {
#Override
public void run() {
progressBar.setVisibility(View.INVISIBLE);
addProcessMessage(message);
}});
}
private void addProcessMessage ( String message ) {
Log.i(TAG, message);
testMessage.append("\n").append(message);
resultText.setText(testMessage);
}
private boolean getAccount () {
Account[] accounts = accountManager.getAccountsByType("com.google");
if ( accounts == null || accounts.length < 1 || accounts.length > 1) {
showDialog(DIALOG_ACCOUNTS);
return false;
}
myAccount = accounts[0];
return true;
}
private static final String authScope = "https://www.googleapis.com/auth/androidpublisher";
//private static final String authScope = "Manage your tasks";//https://www.googleapis.com/auth/androidpublisher";
//private static final String authScope = "Financial Access Enabled Account";
private void processGetAuthToken() {
addProcessMessage("Start processGetAuthToken process...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
AccountManager am = AccountManager.get(this);
Bundle options = new Bundle();
//am.invalidateAuthToken(accountType, authTocken);
am.getAuthToken(
myAccount, // Account retrieved using getAccountsByType()
authScope, // Auth scope
options, // Authenticator-specific options
this, // Your activity
new OnTokenAcquired(), // Callback called when a token is successfully acquired
new Handler(new OnError())); // Callback called if an error occurs
}
private static final String TEST_SKU = "subs:com.lemi.keywordmarketingautoresponder:keywordmarketingautoresponder.1month.1keyword";
private void processAcessSubscriptions( String token ) {
addProcessMessage("processAcessSubscriptions token=" + token);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//AndroidPublisher.Purchases ap = null;
try {
//NetHttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
HttpTransport httpTransport = AndroidHttp.newCompatibleTransport();
JacksonFactory jsonFactory = JacksonFactory.getDefaultInstance();
AssetManager assetManager = getAssets();
InputStream is = assetManager.open(CLIENT_SECRET_JSON);
GoogleCredential credential = GoogleCredential.fromStream(is).createScoped(Collections.singleton(AndroidPublisherScopes.ANDROIDPUBLISHER));
AndroidPublisher publisher = new AndroidPublisher.Builder(httpTransport, jsonFactory, credential).setApplicationName(APP_NAME).build();
addProcessMessage("processAcessSubscriptions publisher=" + publisher);
getProducts(token, publisher);
} catch (Exception e) {
Log.e(TAG, "processAcessSubscriptions Exception:" + e.getMessage(), e);
setProcessFailed(e.getMessage());
}
}
private void getProducts(final String token, final AndroidPublisher publisher) {
new Thread(new Runnable() {
public void run() {
try {
AndroidPublisher.Purchases purchases = publisher.purchases();
addProcessMessage("getProducts purchases=" + purchases);
ProductPurchase product = purchases.products().get(PACKAGE_NAME, TEST_SKU, token).execute();
Integer purchaseState = product.getPurchaseState();
addProcessMessage("getProducts purchaseState=" + purchaseState);
long time = product.getPurchaseTimeMillis();
int consuptionState = product.getConsumptionState();
String developerPayload = product.getDeveloperPayload();
addProcessMessage("getProducts time=" + time + " consuptionState=" + consuptionState + " developerPayload=" + developerPayload);
} catch ( Exception e ) {
Log.e(TAG, "getProducts Exception=" + e.getMessage(), e);
setProcessFailed(e.getMessage());
}
}
}).start();
}
private class OnTokenAcquired implements AccountManagerCallback<Bundle> {
#Override
public void run(AccountManagerFuture<Bundle> accountManagerFuture) {
addProcessMessage("OnTokenAcquired");
// Get the result of the operation from the AccountManagerFuture.
try {
Bundle bundle = accountManagerFuture.getResult();
// The token is a named value in the bundle. The name of the value
// is stored in the constant AccountManager.KEY_AUTHTOKEN.
String token = bundle.getString(AccountManager.KEY_AUTHTOKEN);
addProcessMessage("OnTokenAcquired token=" + token);
processAcessSubscriptions(token);
} catch ( Exception ae ) {
Log.e(TAG, "OnTokenAcquired Exception=" + ae.getMessage(), ae);
setProcessFailed(ae.getMessage());
}
}
}
private class OnError implements Handler.Callback {
#Override
public boolean handleMessage(Message message) {
Log.i(TAG, "OnError handleMessage=" + message);
setProcessFailed(message.toString());
return false;
}
}
}
I have the following code and basically I am wondering how I can use this location method distanceBetween() to find the objects in my .JSON file that are under 500km away. I tried to craft together an if statement and a distanceBetween usage but it doesn't recognize it and I know my syntax has some real problems. Is this the right way to be trying to do this? Any help or tips or code is greatly greatly appreciated!
package com.dredaydesigns.radiostationfinder;
import android.R;
import android.app.Activity;
import android.location.Location;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationServices;
import com.google.gson.JsonParser;
import com.squareup.okhttp.Call;
import com.squareup.okhttp.Callback;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.w3c.dom.Text;
import java.io.IOException;
import butterknife.Bind;
import butterknife.ButterKnife;
public class MainActivity extends Activity implements GoogleApiClient.ConnectionCallbacks {
// String HTTPRadioURL = "https://transition.fcc.gov/fcc-bin/fmq?state=&call=&city=&arn=&serv=FC&vac=3&freq=0.0&fre2=107.9&facid=&asrn=&class=&dkt=&list=0&dist=100&dlat2="
// + latitude + "&mlat2=&slat2=&NS=N&dlon2="
// + longitude +"&mlon2=&slon2=&EW=W&size=9";
public static final String TAG = MainActivity.class.getSimpleName();
private RadioData mRadioData;
private GoogleApiClient mGoogleApiClient;
private Location mLastLocation;
#Bind(R.id.longitudeLabel) TextView mLongitudeLabel;
#Bind(R.id.latitudeLabel) TextView mLatitudeLabel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.list_content);
ButterKnife.bind(this);
double latitude = 32;
double longitude = -96;
double latitudeStations;
double longitudeStations;
final RadioData[] mRadioData = new RadioData[1];
//String radioFinderURL = "http://data.fcc.gov/lpfmapi/rest/v1/lat/" + latitude + "/long/" + longitude + "?format=json&secondchannel=true";
//String HTTPRadioURL = "https://transition.fcc.gov/fcc-bin/fmq?state=&call=&city=&arn=&serv=FC&vac=3&freq=0.0&fre2=107.9&facid=&asrn=&class=&dkt=&list=0&dist=100&dlat2="
// + latitude + "&mlat2=&slat2=&NS=N&dlon2="
// + longitude +"&mlon2=&slon2=&EW=W&size=9";
String radioFinderURL = "http://dredaycreative.com/json/radioData.json";
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(radioFinderURL)
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
#Override
public void onFailure(Request request, IOException e) {
}
#Override
public void onResponse(Response response) throws IOException {
try {
String jsonData = response.body().string();
Log.v(TAG,jsonData);
if (response.isSuccessful()) {
mRadioData[0] = getCurrentDetails(jsonData);
}
} catch (IOException e) {
Log.e(TAG, "Exception Caught: ", e);
}
catch(JSONException e){
Log.e(TAG, "Exception Caught:", e);
}
}
});
}
private RadioData getCurrentDetails(String jsonData) throws JSONException {
JSONObject radioData = new JSONObject(jsonData);
String callSign;
double latitudeStations;
double longitudeStations;
int frequency;
JSONArray jsonArray = radioData.getJSONArray("array");
for(int i =0; i<jsonArray.length(); i++){
callSign = radioData.getJSONObject(i+"")
.getJSONArray("array").getJSONObject(i).getString("FIELD1");
frequency = Integer.parseInt(radioData.getJSONObject(i + "")
.getJSONArray("array").getJSONObject(i).getString("FIELD2"));
longitudeStations = Double.parseDouble(radioData.getJSONObject(i+"")
.getJSONArray("array").getJSONObject(i).getString("FIELD20"));
latitudeStations = Double.parseDouble(radioData.getJSONObject(i+"")
.getJSONArray("array").getJSONObject(i).getString("FIELD24"));
Log.i(TAG, "From JSON: " + callSign + frequency + latitudeStations + longitudeStations);
if {
distanceBetween(double latitude, double longitude, double latitudeStations, double longitudeStations, float[] results) <100km
RadioData currently = new RadioData();
}
}
return new RadioData();
}
protected synchronized void buildGoogleApiClient() {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener((GoogleApiClient.OnConnectionFailedListener) this)
.addApi(LocationServices.API)
.build();
}
#Override
public void onConnected(Bundle bundle) {
mLastLocation = LocationServices.FusedLocationApi.getLastLocation(
mGoogleApiClient);
if (mLastLocation != null) {
mLatitudeLabel.setText(String.valueOf(mLastLocation.getLatitude()));
mLongitudeLabel.setText(String.valueOf(mLastLocation.getLongitude()));
}
}
#Override
public void onConnectionSuspended(int i) {
}
}
UPDATED CODER AFTER COMMENT AT 7:55PMCMT 8/17/15
Okay thank you for your help! I switched it up in this way am I getting warmer, ha?
private RadioData getCurrentDetails(String jsonData) throws JSONException {
JSONObject radioData = new JSONObject(jsonData);
static void distanceBetween;
String callSign;
double latitudeStations;
double longitudeStations;
int frequency;
private int mRadius = distanceBetween(double latitude, double longitude, double latitudeStations, double longitudeStations, float[] results)
callSign = radioData.getJSONObject(i + "")
.getJSONObject(i).getString("FIELD1");
frequency = Integer.parseInt(radioData.getJSONObject(i + "")
.getJSONObject(i).getString("FIELD2"));
longitudeStations = Double.parseDouble(radioData.getJSONObject(i + "")
.getJSONObject(i).getString("FIELD20"));
latitudeStations = Double.parseDouble(radioData.getJSONObject(i + "")
.getJSONObject(i).getString("FIELD24"));
Log.i(TAG, "From JSON: " + callSign + frequency + latitudeStations + longitudeStations);
if{
500 >= mRadius;
RadioData radioData = new RadioData();
}
}
return new RadioData();
}
The user is being presented with multiple permission screens for the same permissions in my app/game.
First, I use mGoogleApiClient = new GoogleApiClient.Builder ( .. to get the user's email address. A "Google+" permissions screen pops up.
Then, I use GoogleAuthUtil.getToken(context, accountName, scope); to get a token, where accountName is the email address. A "Google" permissions screen pops up.
Is there any way I can avoid two permission screens?
Full code is below.
package com.flyingsoftgames.googleplayservices;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.Scopes;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.games.Players;
import com.google.android.gms.games.Games;
import com.google.android.gms.auth.GoogleAuthException;
import com.google.android.gms.auth.GoogleAuthUtil;
import com.google.android.gms.auth.UserRecoverableAuthException;
import com.google.android.gms.auth.GooglePlayServicesAvailabilityException;
import com.google.android.gms.plus.Account;
import com.google.android.gms.plus.Plus;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaInterface;
import org.apache.cordova.CordovaWebView;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.PluginResult;
import org.apache.cordova.PluginResult.Status;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender.SendIntentException;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import org.apache.cordova.*;
import java.io.IOException;
import android.util.Log;
public class GooglePlayServices extends CordovaPlugin implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
private static final String LOG_TAG = "GooglePlayServices";
private static final int REQ_SIGN_IN_REQUIRED = 55664;
public static GoogleApiClient mGoogleApiClient = null;
public CallbackContext tryConnectCallback = null;
public String accessToken = "";
private int connectionAttempts = 0;
#Override public void onConnectionFailed (ConnectionResult result) {
String errormessage = result.toString();
Log.w (LOG_TAG, errormessage);
connectionAttempts += 1;
if (!result.hasResolution() || connectionAttempts >= 2) {
Log.w (LOG_TAG, "Error: no resolution. Google Play Services connection failed.");
tryConnectCallback.error ("Error: " + errormessage + "."); tryConnectCallback = null;
return;
}
try {
result.startResolutionForResult (cordova.getActivity(), result.getErrorCode());
} catch (SendIntentException e) {
// There was an error with the resolution intent. Try again.
mGoogleApiClient.connect ();
}
}
#Override public void onConnected (Bundle connectionHint) {
String mAccountName = Plus.AccountApi.getAccountName(mGoogleApiClient);
new RetrieveTokenTask().execute (mAccountName);
Games.setViewForPopups (mGoogleApiClient, webView);
}
public void onActivityResult (int requestCode, int responseCode, Intent intent) {
if (!mGoogleApiClient.isConnecting()) mGoogleApiClient.connect ();
}
#Override public void onConnectionSuspended (int cause) {
mGoogleApiClient.connect ();
}
public boolean execute (String action, JSONArray inputs, CallbackContext callbackContext) throws JSONException {
if ("getPlayerId".equals(action)) {
String playerId = Games.Players.getCurrentPlayerId (mGoogleApiClient);
callbackContext.sendPluginResult (new PluginResult (PluginResult.Status.OK, playerId));
} else if ("tryConnect".equals(action)) {
tryConnect (callbackContext);
} else if ("getAccessToken".equals(action)) {
callbackContext.sendPluginResult (new PluginResult (PluginResult.Status.OK, accessToken));
}
return true;
}
// tryConnect runs the callback with a value of false if Google Play Services isn't available.
public void tryConnect (CallbackContext callbackContext) {
boolean isGpsAvailable = (GooglePlayServicesUtil.isGooglePlayServicesAvailable(cordova.getActivity()) == ConnectionResult.SUCCESS);
if (!isGpsAvailable) {
callbackContext.sendPluginResult (new PluginResult (PluginResult.Status.OK, false));
return;
}
tryConnectCallback = callbackContext;
mGoogleApiClient = new GoogleApiClient.Builder (cordova.getActivity().getApplicationContext())
.addConnectionCallbacks (this)
.addOnConnectionFailedListener (this)
.addApi (Games.API)
.addScope (Games.SCOPE_GAMES)
.addApi(Plus.API)
.addScope(Plus.SCOPE_PLUS_PROFILE)
.addScope(Plus.SCOPE_PLUS_LOGIN)
.build ();
mGoogleApiClient.connect ();
}
private class RetrieveTokenTask extends AsyncTask<String, Void, String> {
#Override protected String doInBackground (String... params) {
String accountName = params[0];
String scope = "oauth2:https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile";
Context context = cordova.getActivity().getApplicationContext();
try {
accessToken = GoogleAuthUtil.getToken(context, accountName, scope);
Log.e (LOG_TAG, "127: " + accessToken);
} catch (IOException e) {
String errormessage = e.getMessage();
Log.e (LOG_TAG, errormessage);
if (tryConnectCallback != null) tryConnectCallback.error ("Error: " + errormessage + "."); tryConnectCallback = null;
} catch (UserRecoverableAuthException e) {
cordova.getActivity().startActivityForResult (e.getIntent(), REQ_SIGN_IN_REQUIRED);
} catch (GoogleAuthException e) {
String errormessage = e.getMessage();
Log.e (LOG_TAG, errormessage);
if (tryConnectCallback != null) tryConnectCallback.error ("Error: " + errormessage + "."); tryConnectCallback = null;
}
return accessToken;
}
#Override protected void onPostExecute (String newAccessToken) {
super.onPostExecute (newAccessToken);
accessToken = newAccessToken;
if (tryConnectCallback != null) {
String playerId = Games.Players.getCurrentPlayerId (mGoogleApiClient);
tryConnectCallback.sendPluginResult (new PluginResult (PluginResult.Status.OK, playerId));
tryConnectCallback = null;
}
}
}
}
I figured out that if I use just AccountPicker.newChooseAccountIntent, I do not need to do mGoogleApiClient = new GoogleApiClient.Builder: https://github.com/agamemnus/googleplaytoken.
I need to retrieve the email addresses that the user has stored in his gmail account. In my app, the user can now decide to invite a friend of him. I want that the application (if the user tell me "ok") presents a list of the user's contacts email addresses stored in gmail, among which he can choose one or more...
I know that exists Authentication and Authorization for Google APIs". Is it the right way? And, how to use them in Android?
I hope this will help for someone like me, because I have searched a lot for this and finally done with the below.
I have used GData java client library for Google Contacts API v3.
package com.example.cand;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.MalformedURLException;
import java.net.URL;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.Menu;
import com.google.gdata.client.Query;
import com.google.gdata.client.Service;
import com.google.gdata.client.contacts.ContactsService;
import com.google.gdata.data.Link;
import com.google.gdata.data.contacts.ContactEntry;
import com.google.gdata.data.contacts.ContactFeed;
import com.google.gdata.util.AuthenticationException;
import com.google.gdata.util.NoLongerAvailableException;
import com.google.gdata.util.ServiceException;
public class MainActivity extends Activity {
private URL feedUrl;
private static final String username="yourUsername";
private static final String pwd="yourPassword";
private ContactsService service;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String url = "https://www.google.com/m8/feeds/contacts/default/full";
try {
this.feedUrl = new URL(url);
} catch (MalformedURLException e) {
e.printStackTrace();
}
new GetTask().execute();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
private class GetTask extends AsyncTask<Void, Void, Void>{
#Override
protected Void doInBackground(Void... params) {
service = new ContactsService("ContactsSample");
try {
service.setUserCredentials(username, pwd);
} catch (AuthenticationException e) {
e.printStackTrace();
}
try {
queryEntries();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
private void queryEntries() throws IOException, ServiceException{
Query myQuery = new Query(feedUrl);
myQuery.setMaxResults(50);
myQuery.setStartIndex(1);
myQuery.setStringCustomParameter("showdeleted", "false");
myQuery.setStringCustomParameter("requirealldeleted", "false");
// myQuery.setStringCustomParameter("sortorder", "ascending");
// myQuery.setStringCustomParameter("orderby", "");
try{
ContactFeed resultFeed = (ContactFeed)this.service.query(myQuery, ContactFeed.class);
for (ContactEntry entry : resultFeed.getEntries()) {
printContact(entry);
}
System.err.println("Total: " + resultFeed.getEntries().size() + " entries found");
}
catch (NoLongerAvailableException ex) {
System.err.println("Not all placehorders of deleted entries are available");
}
}
private void printContact(ContactEntry contact) throws IOException, ServiceException{
System.err.println("Id: " + contact.getId());
if (contact.getTitle() != null)
System.err.println("Contact name: " + contact.getTitle().getPlainText());
else {
System.err.println("Contact has no name");
}
System.err.println("Last updated: " + contact.getUpdated().toUiString());
if (contact.hasDeleted()) {
System.err.println("Deleted:");
}
// ElementHelper.printContact(System.err, contact);
Link photoLink = contact.getLink("http://schemas.google.com/contacts/2008/rel#photo", "image/*");
if (photoLink.getEtag() != null) {
Service.GDataRequest request = service.createLinkQueryRequest(photoLink);
request.execute();
InputStream in = request.getResponseStream();
ByteArrayOutputStream out = new ByteArrayOutputStream();
RandomAccessFile file = new RandomAccessFile("/tmp/" + contact.getSelfLink().getHref().substring(contact.getSelfLink().getHref().lastIndexOf('/') + 1), "rw");
byte[] buffer = new byte[4096];
for (int read = 0; (read = in.read(buffer)) != -1; )
out.write(buffer, 0, read);
file.write(out.toByteArray());
file.close();
in.close();
request.end();
}
System.err.println("Photo link: " + photoLink.getHref());
String photoEtag = photoLink.getEtag();
System.err.println(" Photo ETag: " + (photoEtag != null ? photoEtag : "(No contact photo uploaded)"));
System.err.println("Self link: " + contact.getSelfLink().getHref());
System.err.println("Edit link: " + contact.getEditLink().getHref());
System.err.println("ETag: " + contact.getEtag());
System.err.println("-------------------------------------------\n");
}
}
Required library files: you can get these jars from here
gdata-client-1.0.jar
gdata-client-meta-1.0.jar
gdata-contacts-3.0.jar
gdata-contacts-meta-3.0.jar
gdata-core-1.0.jar
guava-11.0.2.jar
Note: Add internet permission in AndroidManifest file.
<uses-permission android:name="android.permission.INTERNET"/>