I am trying to remove a custom account in AccountManager.
This is my code :
final Handler handler = new Handler ();
AccountManagerCallback<Boolean> callback = new AccountManagerCallback<Boolean>()
{
#Override
public void run(AccountManagerFuture<Boolean> arg0)
{
String test = "test";
}
};
AccountManagerFuture<Boolean> bool = am.removeAccount(account, callback, handler);
Permissions I'm using :
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS"></uses-permission>
<uses-permission android:name="android.permission.GET_ACCOUNTS"></uses-permission>
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"></uses-permission>
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS"></uses-permission>
The account is never removed and the callback never called, any idea ? No trace in logs
Try this it will work
// Global Variables
public static final String AUTHORITY = "com.example.package";
public static final String ACCOUNT_TYPE = "com.example.package";
public static final String ACCOUNT = "my_custom_account_name";
// Account Manager definition
AccountManager accountManager = (AccountManager) this.getSystemService(ACCOUNT_SERVICE);
// loop through all accounts to remove them
Account[] accounts = accountManager.getAccounts();
for (int index = 0; index < accounts.length; index++) {
if (accounts[index].type.intern() == AUTHORITY)
accountManager.removeAccount(accounts[index], null, null);
}
requires
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
You need to override the following method in the Authenticator class from AbstractAccountAuthenticator.
public Bundle getAccountRemovalAllowed(AccountAuthenticatorResponse response, Account account) {
Bundle result = new Bundle();
boolean allowed = true; // or whatever logic you want here
result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, allowed);
return result;
}
Had the same problem
if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP_MR1) {
accountManager.removeAccount(account, {}, AContext.app.mainHandler)
} else {
accountManager.removeAccountExplicitly(account)
}
For API 22 and higher works perfectly, but on API 19 didn't work at all.
Finally found the problem in my implementation of AbstractAccountAuthenticator:
override fun getAccountRemovalAllowed(response: AccountAuthenticatorResponse?, account: Account?): Bundle {
AccountHelper.removeAccount()
return super.getAccountRemovalAllowed(response, account)
}
It became to work after deleting "AccountHelper.removeAccount()"
I don't know - maybe it'll help
You have to call 2 below methods before removeAccount method and the system will allow you to remove the account in account manager.
clearPassword
invalidateAuthToken
Based on the description on the removeAccount method:
"The authenticator may have its own policies preventing account deletion, in which case the account will not be deleted."
Have fun.
This Code works like a charm to me.
You will need the WRITE_SYNC_SETTINGS,also need to add android.permission.MANAGE_ACCOUNTS works for me with same code pattern. permission. So if you use AccountManager and Account correctly you will manage to remove the account successfully.
I had some issues using the account manager in the Android Simulator, so try to test on a real device...
AccountManager accMgr = AccountManager.get(this);
final Account account = new Account(username, accountType);
removeCaxtonAccount(accMgr, account);
public void removeCaxtonAccount(AccountManager accMgr, Account account){
accMgr.removeAccount(account, null,null);
}
Here is my solution. The previous solutions I found don't explicitly wait for the removal of accounts to finish so they randomly fail.
final AccountManager accountManager = AccountManager.get(getContext());
final String accountType = AuthenticatorService.ACCOUNT_TYPE;
final Account[] availableAccounts = accountManager.getAccountsByType(accountType);
for (final Account availableAccount : availableAccounts) {
final AccountManagerFuture<Boolean> booleanAccountManagerFuture = accountManager.removeAccount(availableAccount, null, null);
assertTrue("Impossible to delete existing account for this application", booleanAccountManagerFuture.getResult(1, TimeUnit.SECONDS));
}
Note: You still need the permissions mentioned before.
I my case for api before 22 adding Authenticator class helped. Just inspire yourself from this source https://www.programcreek.com/java-api-examples/?code=MLNO/airgram/airgram-master/TMessagesProj/src/main/java/ir/hamzad/telegram/ContactsController.java#
for api 22+ this works without Authenticator:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
Timber.e(String.valueOf(accountManager.removeAccountExplicitly(account)));
}
Related
With the following code that retrieves the user's Google account, gmail, I was wondering why I get null from devices like mine (that of course runs on my gmail), whereas it works on my mom's devices:
public class MainActivity extends AppCompatActivity {
final String TAG = "Sample2";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String test = getEmail(getApplicationContext());
Log.d(TAG, "Email is: " + test);
TextView emailTxt = (TextView)findViewById(R.id.emailTxt);
emailTxt.setText(test);
}
private String getEmail(Context context) {
AccountManager accountManager = AccountManager.get(context);
Account account = getAccount(accountManager);
if (account == null) {
return null;
} else {
return account.name;
}
}
private static Account getAccount(AccountManager accountManager) {
Account[] accounts = accountManager.getAccountsByType("com.google");
Account account;
if (accounts.length > 0) {
account = accounts[0];
} else {
account = null;
}
return account;
}
}
I also included the following permission into my Manifest file:
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
Clearly, it's because of my device... But it can't be the only device that's returning null, so I don't know if this is a good approach for a unique string token when verifying payload with in-app billing.
Oh, and here's a screenshot of what I see in my Accounts & Sync settings:
... Is there anything I'm missing here?
Based on the document Set the developer payload string. When making purchase requests, you should not use the user's email address in the payload string, since the address may change.
You should pass in a string token that helps your application to identify the user who made the purchase, so that you can later verify that this is a legitimate purchase by that user. For consumable items, you can use a randomly generated string, but for non- consumable items you should use a string that uniquely identifies the user.
https://developer.android.com/about/versions/oreo/android-8.0-changes.html
Account access and discoverability
In Android 8.0 (API level 26), apps can no longer get access to user accounts unless the authenticator owns the accounts or the user grants that access. The GET_ACCOUNTS permission is no longer sufficient. To be granted access to an account, apps should either use AccountManager.newChooseAccountIntent() or an authenticator-specific method. After getting access to accounts, an app can can call AccountManager.getAccounts() to access them.
Android 8.0 deprecates LOGIN_ACCOUNTS_CHANGED_ACTION. Apps should instead use addOnAccountsUpdatedListener() to get updates about accounts during runtime.
For information about new APIs and methods added for account access and discoverability, see Account Access and Discoverability in the New APIs section of this document
Maybe you can try this
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private static final int PICK_ACCOUNT_REQUEST = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent googlePicker = AccountManager.newChooseAccountIntent(null, null,
new String[] { "com.google"}, true, null, null, null, null);
startActivityForResult(googlePicker, PICK_ACCOUNT_REQUEST);
}
#Override
protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
if (requestCode == PICK_ACCOUNT_REQUEST && resultCode == RESULT_OK) {
String accountName = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
Log.d(TAG, "Account Name=" + accountName);
String accountType = data.getStringExtra(AccountManager.KEY_ACCOUNT_TYPE);
Log.d(TAG, "Account type=" + accountType);
AccountManager accountManager = AccountManager.get(this);
Account[] accounts = accountManager.getAccounts();
for (Account a :
accounts) {
Log.d(TAG, "type--- " + a.type + " ---- name---- " + a.name);
}
}
}
}
I want to restrict my API endpoints access only to my android app, but without google_account/password.
I've the choice of those methods : https://developers.google.com/accounts/docs/OAuth2
For test, I succeeded to authenticate my android app to my API with this method: https://cloud.google.com/appengine/docs/python/endpoints/consume_android
==> This method allow you to authenticate your app with combo:
Login/password (Google account)
SHA1 and package name of your android APP
So if someone get my code (Decompiling apk) and modify my android code, they can't access to my API because SHA1 fingerprint of my app will change. (I tested it, and it works =) )
This method works fine, but I don't want Google login/password for authentication..
So I tried this method: https://developers.google.com/accounts/docs/OAuth2ServiceAccount
I successfully authenticate my android app, BUT, if my android code is modified by someone else(So the SHA1 changed), my android app can still connect to my API !! So if someone get my package and decompile it, he'll changed freely code and successfully access to my API..
Here is my API Code:
#ApiMethod( name = "ListCampagnes", httpMethod = ApiMethod.HttpMethod.GET, path="list", clientIds = {CONSTANTES.ANDROID_CLIENT_ID, CONSTANTES.WEB_CLIENT_ID, CONSTANTES.SERVICE_CLIENT_ID, com.google.api.server.spi.Constant.API_EXPLORER_CLIENT_ID}, audiences = {CONSTANTES.ANDROID_AUDIENCE})
public Collection<Campagne> getCampagnes(#Named("NumPortable")String NumPortable, User user) throws UnauthorizedException {
if (user == null) throw new UnauthorizedException("User is Not Valid");
return CampagneCRUD.getInstance().findCampagne(NumPortable);
}
Here is my android code:
GoogleCredential credentialToAppengine;
try {
String p12Password = "notasecret";
KeyStore keystore = KeyStore.getInstance("PKCS12");
InputStream keyFileStream = getAssets().open("59ce5a08e110.p12");
keystore.load(keyFileStream, p12Password.toCharArray());
PrivateKey key = (PrivateKey)keystore.getKey("privatekey", p12Password.toCharArray());
credentialToAppengine = new GoogleCredential.Builder().setTransport(AndroidHttp.newCompatibleTransport()).setJsonFactory(new JacksonFactory()).setServiceAccountId("301991144702-3v9ikfp4lsmokee1utkucj35847eddvg#developer.gserviceaccount.com").setServiceAccountPrivateKey(key).setServiceAccountScopes(Collections.singleton("https://www.googleapis.com/auth/userinfo.email")).build();
} catch (GeneralSecurityException e) {
e.printStackTrace();
return null;
} catch (Exception e) {
e.printStackTrace();
return null;
}
Do I try an other method for authenticate my android App ? Or did I missing something in my API code ?
Thanks a looot in advance,
Authenticate Android End point without Google User Account is just impossible ! I tried every ways but still doesn't works !
So here is my way to resolv this problem, without any user interaction (Maybe not the right but that works, and you've got strong authentication (SHA1 + Google Account)):
HERE IS MY ANDROID CODE
Get and Build Valid Credential
//Get all accounts from my Android Phone
String validGoogleAccount = null;
Pattern emailPattern = Patterns.EMAIL_ADDRESS; // API level 8+
Account[] accounts = AccountManager.get(context).getAccounts();
for (Account account : accounts) {
if (emailPattern.matcher(account.name).matches()) {
//Just store mail if countain gmail.com
if (account.name.toString().contains("gmail.com")&&account.type.toString().contains("com.google")){
validGoogleAccount=account.name.toString();
}
}
}
//Build Credential with valid google account
GoogleAccountCredential credential = GoogleAccountCredential.usingAudience(this,"server:client_id:301991144702-5qkqclsogd0b4fnkhrja7hppshrvp4kh.apps.googleusercontent.com");
credential.setSelectedAccountName(validGoogleAccount);
Use this credential for secure calls
Campagneendpoint.Builder endpointBuilder = new Campagneendpoint.Builder(AndroidHttp.newCompatibleTransport(), new JacksonFactory(), credential);
HERE IS MY API BACKEND CODE:
API Annotation
#Api(
scopes=CONSTANTES.EMAIL_SCOPE,
clientIds = {CONSTANTES.ANDROID_CLIENT_ID,
CONSTANTES.WEB_CLIENT_ID,
com.google.api.server.spi.Constant.API_EXPLORER_CLIENT_ID},
audiences = {CONSTANTES.ANDROID_AUDIENCE},
name = "campagneendpoint",
version = "v1"
)
Method code:
public Collection<Campagne> getCampagnes(#Named("NumPortable")String NumPortable, User user) throws UnauthorizedException {
if (user == null) throw new UnauthorizedException("User is Not Valid");
return CampagneCRUD.getInstance().findCampagne(NumPortable);
}
For the moment, it only works on Android (I don't know how we gonna do on IOS..)..
Hope It will help you !
I am working in an application where I am fetch all the application installed my device using this code :
final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
mainIntent.addCategory("com.myapp.MY_CATEGORY");
final List<ResolveInfo> pkgAppsList =getPackageManager().queryIntentActivities( mainIntent, 0);
Now I want to categories the apps differently According to their Categories. Please Suggest me.
To know the category of an application you need to get the data from google play. you can check android-market-api. it is a third party api. according to their info
You can browse market with any carrier or locale you want.
Search for apps using keywords or package name.
Retrieve an app info using an app ID.
Retrieve comments using an app ID.
Get PNG screenshots and icon
So you better check if you can parse the category info using this api.
I realized an AsyncTask to collect categories for some apps, using these libraries:
android-market-api-0.6
com.google.protobuf 2.4.1
you can find them on this link:
https://code.google.com/archive/p/android-market-api/downloads
http://mvnrepository.com/artifact/com.google.protobuf/protobuf-java
Here's the code in doInBackground() Method:
final ArrayList<MarketApplication> results = new ArrayList<>();
AccountManager am = AccountManager.get(MainActivity.this.getBaseContext());
Account[] accounts = am.getAccountsByType("com.google");
if (accounts.length > 0) {
try {
AccountManagerFuture<Bundle> accountManagerFuture =
am.getAuthToken(accounts[0], "android", null, MainActivity.this, null,
null);
Bundle authTokenBundle = accountManagerFuture.getResult();
String authToken =
authTokenBundle.getString(AccountManager.KEY_AUTHTOKEN).toString();
MarketSession session = new MarketSession();
session.setAuthSubToken(authToken);
Market.AppsRequest appsRequest = Market.AppsRequest.newBuilder()
.setQuery(params[0])
.setStartIndex(0).setEntriesCount(10)
.setWithExtendedInfo(true)
.build();
session.append(appsRequest, new MarketSession.Callback<Market.AppsResponse>() {
public void onResult(Market.ResponseContext context, Market.AppsResponse
response) {
for (int i = 0; i < response.getEntriesCount(); i++) {
MarketApplication marketApplication = new MarketApplication();
Market.App app = response.getApp(i);
marketApplication.setName(app.getTitle());
Market.App.ExtendedInfo extendedInfo = app.getExtendedInfo();
marketApplication.setCategory(extendedInfo.getCategory());
results.add(marketApplication);
}
}
});
session.flush();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
return results;
Category information, got from ExtendedInfo, and name of application are added to a custom class MarketApplication. params[0] is a query String, like app name of interest.
There is a wiki page that helps developer to make a specific query:
https://code.google.com/archive/p/android-market-api/wikis/HowToSearchApps.wiki
take notice that this service requires to add these permissions in the Android manifest:
<uses-permission android:name="android.permission.GET_ACCOUNTS"></uses-permission>
<uses-permission android:name="android.permission.USE_CREDENTIALS"></uses-permission>
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
I'm trying to figure out how to use Google Api for accessing/editing Google SpreadSheet.
I want to have a connection always with the same spreadsheet from many devices. I got examples using the AccountManager, but i should not use the user account. There is any good turorial?
Right now i've got the following..is that right?
AccountManager accountManager = AccountManager.get(this);
ArrayList googleAccounts = new ArrayList();
// Just for the example, I am using the first google account returned.
Account account = new Account("email#gmail.com", "com.google");
// "wise" = Google Spreadheets
AccountManagerFuture<Bundle> amf = accountManager.getAuthToken(account, "wise", null, this, null, null);
try {
Bundle authTokenBundle = amf.getResult();
String authToken = authTokenBundle.getString(AccountManager.KEY_AUTHTOKEN);
// do something with the token
//InputStream response = sgc.getFeedAsStream(feedUrl, authToken, null, "2.1");
}
catch (Exception e) {
// TODO: handle exception
}
Required permissions:
<uses-permission android:name="android.permission.ACCOUNT_MANAGER"/>
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<uses-permission android:name="android.permission.USE_CREDENTIALS"/>
Choose needed outh token type from the table:
http://code.google.com/intl/ja/apis/spreadsheets/faq_gdata.html#Authentication
Spreadsheets Data API wise
Code sample:
public class OuthTokenActivity extends Activity {
String tag = "DEBUG";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
AccountManager mAccountManager = AccountManager.get(this);
for (Account account : mAccountManager.getAccountsByType("com.google")) {
mAccountManager.getAuthToken(account, "wise", savedInstanceState,
this, resultCallback, null);
}
}
AccountManagerCallback<Bundle> resultCallback = new AccountManagerCallback<Bundle>() {
public void run(AccountManagerFuture<Bundle> future) {
try {
Bundle result = future.getResult();
String token = (String) result.get(AccountManager.KEY_AUTHTOKEN);
String name = (String) result.get(AccountManager.KEY_ACCOUNT_NAME);
Log.d(tag, String.format("name: %s, token: %s", name, token));
} catch (Exception e) {
e.printStackTrace();
}
}
};
}
There is an API released now, available for java script, which could be run in your app. And they show how to integrate this into an Android app in a video here.
I'm trying to retrieve the AuthToken for Facebook (saved by Facebook for Android) by using the following piece of code.
AccountManager am = AccountManager.get(this);
Account[] accounts = am.getAccountsByType("com.facebook.auth.login");
if (accounts.length > 0) {
for(int j = 0; j < accounts.length; j++) {
Account account = accounts[j];
if(account.type != null && account.type.equals("com.facebook.auth.login")) {
Log.e(RuntimeVars.MY_NAME, "FACEBOOK-TYPE FOUND");
am.getAuthToken(account, "com.facebook.auth.login", null, ConversationList.this,
new AccountManagerCallback<Bundle>() {
public void run(AccountManagerFuture<Bundle> arg0) {
try {
Bundle b = arg0.getResult();
Log.e(RuntimeVars.MY_NAME, "THIS AUTHTOKEN: " + b.getString(AccountManager.KEY_AUTHTOKEN));
}
catch (Exception e) {
Log.e(RuntimeVars.MY_NAME, "EXCEPTION#AUTHTOKEN");
}
}
}, null);
}
}
}
The login credentials are found and FACEBOOK-TYPE FOUND is written into LogCat, but neither THIS AUTHTOKEN: [...] nor EXCEPTION#AUTHTOKEN is logged. So I suppose am.getAuthToken is never called.
What am I missing?
In general, if there is a better (and at least working) approach to retrieve the Facebook authtoken from the Android accounts please let me know.
Thanks a lot for your help!
Best regards
S.
Why not use the Facebook SDK?
The Facebook class in it has a member to get the OAuth 2.0 access token (if that is what you need), getAccessToken().
To explain the fact that neither of your logging statements are being reached, consider:
Line ~8:
am.getAuthToken(account, "com.facebook.auth.login", null, ConversationList.this,
... can return a token if it's immediately available. Maybe that's the answer you're looking for? Quoting the AccountManager documentation:
If a previously generated auth token
is cached for this account and type,
then it is returned. Otherwise, if a
saved password is available, it is
sent to the server to generate a new
auth token. Otherwise, the user is
prompted to enter a password.
Try calling AccountManager.blockingGetAuthToken instead. If that works, then there's something more interesting at fault here...
Also, make sure your manifest has the USE_CREDENTIALS permission set correctly.
add try { before am.getAuthToken and catch Exception where this method declaration ends.This will give you why and where excepption is happening