I'm looking for the answer since the begining of the day and I still don't know what to do!
To explain the problem:
My application uses GTasks API. To do this, the user MUST have a google account. I have two ways to check that:
I instantiate an Account manager and look for "com.google" accounts if there are no such accounts
I launch a webview on which the user logs on Google and accepts to use the API.
Then, I have to synchronize my application with GTask.
From the information extracted from the webview, I tried to create a "com.google" account using the AccountManager but I always have the error "Caller UID is different...". I know that the problem should come from "account-authenticator" because the "accountType" has to be the same in the XML file and in the app but I think that's not the case because when I use a different account type, it perfectly works (but I can't obtain an authToken from GTask).
So, here is my code:
Authenticator.xml:
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="com.google"
android:icon="#drawable/miniicon"
android:smallIcon="#drawable/miniicon"
android:label="#string/app_name"
android:accountPreferences="#xml/preferences"
/>
Definition of the account type:
public class AuthenticationService extends Service {
public static final String ACCOUNT_TYPE = "com.google";
How I use it in my class:
AccountManager manager = AccountManager.get(activity);
final Account account = new Account(login, AuthenticationService.ACCOUNT_TYPE);
manager.addAccountExplicitly(account, password, new Bundle());
When I use "com.plop" or anaything else instead of "com.google", it works really fine.
So, my two questions are:
1. Can I use the "com.google" account type?
2. Is that normal that I can't obtain my authToken using something else than "com.google" (knowing that the account used is still a Google account, it's just not set like that on Android) ?
Thx in advance for your help and, please, forgive me if the answer was on the site!
I answered my own question: that's not possible the way I wanted to. You have to use the "addAccount" function using a "com.google" account type. This will result in popping the account creation activity used by the android system. Then, everything is fine using GTasks API.
Related
For example, user is navigating to google.com in WebView.
Is it possible to authorize him there via Google Account Picker (something like described here https://developers.google.com/android/guides/http-auth) to simplify authorization instead of manually logging in via web form?
Android Web browsers (for example, Google Chrome) are authorizing user via this method).
Part I: Using the Google Plus Services API
If I understand your question correctly, you may be able to achieve what you are trying to do using the Google Plus Services API.
You create your GoogleSignInOptions and then create your GoogleApiClient using these sign-in options. From there, you use the Auth.GoogleSignInApi.getSignInIntent with your GoogleApiClient as the parameter.
This intent should launch a SignInIntent that presents the Google account picker (that will include accounts that have been accessed on the device previously, and the ability to add another account).
Once you get back the GoogleSignInResult, you can verify that the user was authenticated and then create the authentication flow as you would otherwise.
Even included in the Android SDK is the Google SignInButton, which you can use right in your layout instead of having to create a custom button for the sign-in.
Part II: Using WebViewClient
Now, if you are trying to use a WebView to authenticate them, your best bet is to extend the WebViewClient class.
Things you will need: clientId, clientSecret, and clientScope (all of these details will be given for you when you create your application in the Google Developer Console)
First things first, your URL to authorize will probably be as follows: https://accounts.google.com/o/oauth2/auth?response_type=code&clientId={your client id}&state={SOMESTATEINFO}&access_type=offline (access type if you want offline access). This should be the initial URL of your WebView
Next, you will want to modify your extended WebViewClient class. What you will want to do is override the shouldOverrideUrlLoading(WebView webView, String url) method to listen for your redirectURL. Probably the easiest thing to do is to use url.startsWith(<your redirect URL>) to detect this. You can then parse the response. If your response contains error, then it means something went wrong. Otherwise, you should get back two fields in the URL: code and state. If you do not get error back, then return true for shouldOverrideUrlLoading.
Once you get your code, you can create a new GoogleAuthorizationCodeFlow, using your client, scopes, and secrets.
Once you have your flow, you will need a GoogleTokenResponse, which you will be able to get using the code obtained above for your authorization code, using GoogleTokenResponse response = flow.newTokenResponse(<code>).setRedirectUri(<redirectUri>).execute().
Once you have done this, and you have your response, you can get your Credential using flow.createAndStoreCredential(response, null).
And voila, using this Credential, you can authenticate your calls.
Caveats I have not been able to get the WebView to recognize accounts that have been signed into on other web browsers, so the account picker may only show the accounts that have been signed into on the app-specific WebView.
tl;dr It is possible to do this with a WebView and WebViewClient, but it's messy and a little bit more roundabout than using the Google Plus Services API.
This example better illustrates the authorization flow/credential stuff once you get the authorization code and such.
And here's some documentation on the WebViewClient that may be useful as well.
Hope this helps point you in the right direction!
I'm in a situation where I need to request access tokens for two scopes (from my android application), https://www.googleapis.com/auth/userinfo.email and https://www.googleapis.com/auth/userinfo.userinfo
I would like to get both permissions on a single call to getAuthToken, but can't figure out the string to pass in the authTokenType parameter. I tried several reasonable combinations with no positive results :(
Has anyone solved this issue? Is it possible?
I was having the same issue. Shah is almost right, but his scope string is wrong. It should be
"oauth2:<scope_url> <scope_url>"
not
"oauth2:<scope_url> oauth2:<scope_url>"
If you need multiple OAuth 2.0 scopes, use a space-separated list.
oauth2:https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.userinfo
You asked for sample code, so have a look at the Google Docs Upload Sample application, and in this application have look at the authentication flow done in this sample Android screen (ignore that it's about Google Docs, it still authorizes first). You can get the whole application and run it in an emulator with Google APIs present or run it on your phone. The authorization workflow starts with the buttonAuthorize click, Authorize() and you are specifically interested in this method:
private void gotAccount(Account account)
{
Bundle options = new Bundle();
accountManager.getAuthToken(
account, // Account retrieved using getAccountsByType()
"oauth2:https://www.googleapis.com/auth/userinfo.email oauth2:https://www.googleapis.com/auth/userinfo.userinfo", // Auth scope
//"writely", // Auth scope, doesn't work :(
options, // Authenticator-specific options
this, // Your activity
new OnTokenAcquired(), // Callback called when a token is successfully acquired
null); // Callback called if an error occurs
}
The user gets this access request screen:
Note that this is using the 'local' OAuth2 mechanism, not opening a web browser, but using the authentication provided when you first activated the Android phone.
Also note that the user sees the full URL of the scope instead of a friendly name, I haven't found a way around this and if you do find out it would be great if you could share the answer.
While following this tutorial on using OAuth 2 with the Android AccountManager, I'm told that when specifying the AUTH_TOKEN_TYPE, instead of specifying the scope like this:
String AUTH_TOKEN_TYPE = "oauth2:https://www.googleapis.com/auth/tasks";
you can use its alias:
String AUTH_TOKEN_TYPE = "Manage your tasks";
This allows for a user to understand what the permissions are about instead of being given a URL. I am trying to figure out what the alias would be for the Google Documents List API.
The document then points to a list of auth token aliases, which unfortunately does not exist. There is also a related Youtube video in which the developer mentions a Google Buzz alias and also mentions that a list of aliases will be set up at some point.
What can I use instead of the Google Docs scope?
String AUTH_TOKEN_TYPE = "oauth2:https://docs.google.com/feeds/"
I found this list which seems to contain possible aliases for the auth token type for various Google services: http://code.google.com/intl/ja/apis/gdata/faq.html#clientlogin
Although it is not particular user-friendly, it seems like you may be able to use 'writely' as the alias for retrieving the documents list. A Google search for AUTH_TOKEN_TYPE = 'writely'; returns a few results, which looks promising!
I'm attempting to access Google Drive, not Docs, but here's what I entered for an Authorization Token Type:
String AUTH_TOKEN_TYPE = "oauth2:" + DriveScopes.DRIVE;
DriveScopes is a list of possible authorization scopes.
DriveScopes.DRIVE is the specific string from the list that gets me the particular scope I want. (It returns "https://www.googleapis.com/auth/drive".)
I append "oauth2:" to the front to specify that I want an Oauth2 token.
This code causes the user to be asked permission for "Your Drive to be Managed" or something like that. It didn't pop up revealing the address.
Hope this helps someone else find the similar code they need to find their own AUTH_TOKEN_TYPE. (Maybe in your case try DocScopes or DocsScopes, insert a period and see what Eclipse suggests as autocomplete?)
Supposedly this has been fixed in Ice Cream Sandwich (haven't been able to test it yet):
https://plus.google.com/u/0/112215288642007559493/posts/Zme5LNLwCDP
I suppose that you could use the short names in Gingerbread, and plain names in ICS.
I'm trying to create a SyncAdapter for my Android app to show YouTube videos from one specific channel. The videos are public domain so I don't want the user to login, create an account, authenticate themselves, upload data, or use the contacts database. I simply want the SyncAdapter to periodically update my app's database with the newest video metadata from that channel. I already built a ContentProvider to access my database. I do like the fact that the SyncProvider will handle the ability to turn off syncing, scheduling, and retry backoff mechanisms for updating.
I asked earlier if a SyncAdapter was a good choice for my use case, and I was told it was. I watched the Google I/O video, read the docs, read blogs... (see list below). I've been unable to get anything to work. The best I've gotten was to have the SyncAdapter account show up in the global "Accounts & sync settings" but be non-functional. Even if this worked, it would be less than ideal because I prefer the user to not see the account except from inside my app. This would be acceptable if there was no other option, so long as they don't need to access it to set it up as everything would default to automatic once a day syncing.
I even tried to use the SampleSyncAdapter as-is and put breakpoints in the Authentication code sections. Not a single breakpoint is hit so I can't see what triggers the calls or what data is contained. I would have thought I'd at least get that much.
I'm starting to think using a SyncAdapter is a bad idea despite the recommendation. I've yet to find an example that is close to what I want, let alone a tutorial or complete, organized and clear docs. This seems like it should be a common task many apps would want to do.
Please add to this post any good documentation on this use case. I can find none.
Without this, I think it's fair to recommend to everyone to not use SyncAdapters for this use case. I'm not speaking for other use cases here so don't jump on with how it worked for your use case if it's not like mine.
It would also be helpful to know what version of the API level is considered ready for primetime. There's a number of issues posted regarding version numbers. I'm trying to stay as low as possible to get the most users. My current API target is 7.
Here's list of links I've tried to no avail, others may find these more helpful:
http://developer.android.com/resources/samples/SampleSyncAdapter/index.html
http://www.google.com/events/io/2010/sessions/developing-RESTful-android-apps.html
http://naked-code.blogspot.com/2011/05/revenge-of-syncadapter-synchronizing.html
http://www.c99.org/2010/01/23/writing-an-android-sync-provider-part-1/
http://www.c99.org/2010/01/23/writing-an-android-sync-provider-part-2/
http://www.finalconcept.com.au/article/view/android-account-manager-step-by-step
http://www.finalconcept.com.au/article/view/android-account-manager-step-by-step-1
http://www.finalconcept.com.au/article/view/android-account-manager-step-by-step-2
Android SyncAdapter without Authentication vs. Android Service
Why does ContentResolver.requestSync not trigger a sync?
In short the answer is: ContentProvider, AccountManager and SyncAdapter go together. You must have these three pieces, even if they are "dumb".
As stated above, "ContentProvider, AccountManager and SyncAdapter go together".
For your application you can call the following activity the first time your app is loaded to authenticate and start synching automatically:
public class LoginActivity extends AccountAuthenticatorActivity {
private final static String DUMMY_ACCOUNT_NAME = "some_name";
private final static String DUMMY_ACCOUNT_PASS = "some_pass";
private final static String AUTHORITY = "com.android.contacts"; // for example
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Account account = new Account(DUMMY_ACCOUNT_NAME, Constants.ACCOUNT_TYPE);
AccountManager am = AccountManager.get(this);
if (am.addAccountExplicitly(account, DUMMY_ACCOUNT_PASS, null)) {
Bundle result = new Bundle();
result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
setAccountAuthenticatorResult(result);
ContentResolver.setSyncAutomatically(account, AUTHORITY, true);
}
finish();
}
}
This works in Android API 5+.
I need to fetch some data over the cloud from my app. I've watched the google IO video on RESTful android apps # http://www.youtube.com/watch?v=xHXn3Kg2IQE&t=43m58s It recommends in the final slides to use a SyncAdapter to integrate with the Android System.
Later I learned that one has to use an Account to implement SyncAdapter. My app does not use an account. Users can freely download data without registration. Can I still use SyncAdapter? Is there a stock dummy account that I could use?
Edit: I do have a content provider for my app so that's not a problem
Edit2: I've just looked at the Weather and Stock apps under Settings -> Accounts & Sync. You can see that they allow syncing, but don't have a remove account button. On the other hand, Google, Facebook and Skype apps allow syncing PLUS they have a remove account button. This means Weather and Stock don't use accounts, whereas Google, Facebook and Skype do.
The tutorials I found # http://ericmiles.wordpress.com/2010/09/22/connecting-the-dots-with-android-syncadapter/ and # http://www.c99.org/2010/01/23/writing-an-android-sync-provider-part-1/ say that one MUST have an account to use Sync Adapter. :S ???
As the Android Developer docs say
Even if your app doesn't use accounts, you still need to provide an authenticator component. If you don't use accounts or server login, the information handled by the authenticator is ignored, so you can provide an authenticator component that contains stub method implementations. You also need to provide a bound Service that allows the sync adapter framework to call the authenticator's methods.
There is an entire article on Creating a Stub Authenticator. I realise that this question is old and an answer was accepted long ago, but I felt that a recent addition to the official docs should be included here.
I have created a contact sync adapter where I don't have a account authorization and or configuration screens. It wasn't that hard. I don't think having to deal with the Android Account stuff was that much of a deal.
Quote from your tutorial link:
The bad news is that there is no
“stock” functionality to give you an
easy way to provide an Account to the
system. However, in the same Sync
Adapter Example that comes with the
SDK there is a lot of code you can
borrow to give you Account
functionality. Unless you desire a
custom credentials screen, you can
heist all the code in the
com.example.android.samplesync.authenticator
package with only a few minor changes.
So it's basically just a copy and paste from the example, that's pretty much what I did and it worked fine.
I don't know for sure but all the adapters that don't have "Remove Account" seems to be built-in ROM adapters on all the devices I've looked at. I'm not sure you have to worried about it.
I keep getting lots of notifications from this question, so I thought I'll share this info. This is how you add SyncAdapter without Account. You can put this in onCreate of MyApplication extends Application class. This assumes you already have a SyncAdapter and ContentProvider implemented. You can do that by following the tutorials listed in the question.
final String ACCOUNT_NAME = "MyApp";
final String ACCOUNT_TYPE = "com.myapp.account";
final String PROVIDER = "com.myapp.provider";
Account appAccount = new Account(ACCOUNT_NAME,ACCOUNT_TYPE);
AccountManager accountManager = AccountManager.get(getApplicationContext());
if (accountManager.addAccountExplicitly(appAccount, null, null)) {
ContentResolver.setIsSyncable(appAccount, PROVIDER, 1);
ContentResolver.setMasterSyncAutomatically(true);
ContentResolver.setSyncAutomatically(appAccount, PROVIDER, true);
}
res/xml/syncadapter.xml
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
android:contentAuthority="#string/provider"
android:accountType="#string/account_type"
android:userVisible="true"
android:supportsUploading="true"
/>
res/xml/authenticator.xml
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="#string/account_type"
android:icon="#drawable/app_icon"
android:smallIcon="#drawable/app_icon"
android:label="#string/app_label"
/>