I've been working on an app that sends a small bit of data back to a Google Sheet at certain points, however I can't get it to work properly:
public void SheetUpdate() {
GoogleAccountCredential mCredential;
Sheets mService;
final String[] SCOPES = { SheetsScopes.SPREADSHEETS };
mCredential = GoogleAccountCredential.usingOAuth2(getApplicationContext(), Arrays.asList(SCOPES))
.setBackOff(new ExponentialBackOff())
.setSelectedAccountName("[email protected]");
Log.e("DEBUG", "SheetUpdate: " + mCredential.getSelectedAccountName());
HttpTransport transport = AndroidHttp.newCompatibleTransport();
JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
mService = new Sheets.Builder(transport, jsonFactory, mCredential)
.setApplicationName("Listeau")
.build();
/*
String range = "[Sheet Name]![Start]:[End]";
mService.spreadsheets().values().update(enumId, range, valueRange)
.setValueInputOption("RAW")
.execute();
*/
ValueRange values = new ValueRange();
values.set("EMAIL", "[email protected]");
values.set("AUTH", "[INSERT DATE]");
values.set("SUB", "[INSERT DATE + MONTH]");
try {
mService.spreadsheets().values().append("1meAAvjdPmghn6wl_IKc-_NJx_M85I_yqsn4Nwm_j_X0", "Users", values)
.setValueInputOption("RAW")
.execute();
} catch(IOException e) {
Log.e("BUG", "SheetUpdate: IOException");
}
}
And in my manifest I have:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
The error I receive points to mCredential
as having it's name unset (null
), which is being caught when mService.execute is called.
I have manually (just to try and pinpoint the issue) allowed the permissions in the Settings-Apps menu, as well as tried it on Jellybean, but all claim the same issue.
[EDIT] Here's the full error output: `
E/AndroidRuntime: FATAL EXCEPTION: main
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.pybuspr.listeriafoodusa/com.pybuspr.listeau.Master}: java.lang.IllegalArgumentException: the name must not be empty: null
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2059)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2084)
at android.app.ActivityThread.access$600(ActivityThread.java:130)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1195)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4745)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.IllegalArgumentException: the name must not be empty: null
at android.accounts.Account.<init>(Account.java:48)
at com.google.android.gms.auth.zze.getToken(Unknown Source)
at com.google.android.gms.auth.GoogleAuthUtil.getToken(Unknown Source)
at com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential.getToken(GoogleAccountCredential.java:269)
at com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential$RequestHandler.intercept(GoogleAccountCredential.java:294)
at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:868)
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)
at com.pybuspr.listeau.Master.SheetUpdate(Master.java:167)
at com.pybuspr.listeau.Master.onCreate(Master.java:48)
at android.app.Activity.performCreate(Activity.java:5008)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1079)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2023)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2084)
at android.app.ActivityThread.access$600(ActivityThread.java:130)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1195)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4745)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
at dalvik.system.NativeStart.main(Native Method)
`
Your
execute()
call goes via theGoogleAccountCredential
object you created, and that is failing internally.Looking at the code for Account.java, which is open source, you can see that the name being passed in is null. The caller,
GoogleAuthUtil
, is a closed source part of the Play Services library but we can have a good guess at what's going on. It tries to get the Google account from the phone's settings and fails to find a match.As you already suspect, permissions are a factor here, and I think this is the cause of your failure. When targeting Android 6 and above, declaring permissions in the manifest can be insufficient, and in your case,
GET_ACCOUNTS
is affected by that. See here.You need to request permissions at runtime (and cleanly handle the case where they are refused by the user). See here for how to do that. Alternatively provided you don't have any relevant dependencies, you could decrease your target level to 5.1 or lower, and for now at least, it will use the older permission model.