In my app, I need to known if there is any Google account or any Samsung account.
Up to Android 7 it was easy to get this information with something like:
Account[] accounts = AccountManager.get(getContext())
.getAccountsByType("com.google")
But with the event of Oreo this does not work anymore.
EDIT: see official information on this subject:
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
I spent half a day to find a solution to my need, without success.
I've found information claiming that now the only way to access to accounts is to use AccountPicker like this:
AccountPicker.newChooseAccountIntent(null, null, new String[]{"com.google"},true, null, null, null, null);
But this does respond to my problem. To be clear I only need to know if an account exists for a certain type (Google, Samsung...) I do not need to know how much if so and do not need accounts information.
Using "android.permission.READ_CONTACTS" permission, and
Account[] accounts = AccountManager.get(getContext())
.getAccountsByType("com.google")
working again in android Oreo
As you already said, there's no way to read other accounts if the user didn't give you the permission to do so. The permission now is provided not only with the run-time permission but even with the account picker, i.e. an account is visible to your app only if the user selected the account after you called the account picker. This new restriction is exactly to avoid what you are trying to do: read all user accounts. There's no solution to your problem, the only thing you can do is to present the picker to the user and let him select all the accounts, not the best user experience however.
Edit: starting from Google Play Services 11.6 there's now a new method requestGoogleAccountsAccess() to get all Google accounts.
To get the installed google accounts on a device running Oreo+ (8+) with this code
Account[] accounts = AccountManager.get(getContext()).getAccountsByType("com.google")
You need to first call
https://developers.google.com/android/reference/com/google/android/gms/auth/GoogleAuthUtil.html#requestGoogleAccountsAccess(android.content.Context)
Please add the following dependency first
com.google.android.gms:play-services-auth:16.0.0
The call requestGoogleAccountsAccess() throws an exception which you can cast (after checking) to UserRecoverableAuthException and get an intent from it to start with startActivityForResult
Here is some example code, working on Android Oreo
// call this on a background thread!
private void requestGoogleAccountAccess() throws Exception
{
googleAccountAccessGranted = GoogleAuthUtil.requestGoogleAccountsAccess(this);
Log.i(TAG, "googleAccountAccessGranted: " + googleAccountAccessGranted);
}
// exception handler after calling method above
private void handleAuthResult(Throwable e)
{
if (e instanceof UserRecoverableAuthException)
{
UserRecoverableAuthException authException = (UserRecoverableAuthException) e;
startActivityForResult(authException.getIntent(), AUTH_PERMISSION_REQUEST);
}
else
{
Log.e(TAG, "Cannot request Google Account Access", e);
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == AUTH_PERMISSION_REQUEST)
{
Log.i(TAG, "Google Auth Permission Result");
if (resultCode == Activity.RESULT_CANCELED)
{
Log.w(TAG, "User Cancelled Play Services Auth Request.")
}
else if (resultCode == Activity.RESULT_OK)
{
Log.d(TAG, "User accepted Play Services Auth Request.");
// call the following line again on a background thread. the call now returns a boolean instead of throwing an exception
// googleAccountAccessGranted = GoogleAuthUtil.requestGoogleAccountsAccess(this);
}
}
}
It's a bit strange why Google decided themselves for this "architecture". Why not return a Task, etc.
But this is how you get it working.
Of course this code needs proper exception handling which I left out for readability.
Related
I have an android native application using MSAL library to authenticate. We are facing issues to logout from application after login. While logout, it displays a screen where the already logged in email displays, tapping on that allows the user to login to the application with out a password. The application is configured as MultiAccount mode. Below is the code for logout.
removeAccountButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
if (mMultipleAccountApp == null) {
return;
}
/**
* Removes the selected account and cached tokens from this app (or device, if the device is in shared mode).
*/
mMultipleAccountApp.removeAccount(accountList.get(accountListSpinner.getSelectedItemPosition()),
new IMultipleAccountPublicClientApplication.RemoveAccountCallback() {
#Override
public void onRemoved() {
Toast.makeText(getContext(), "Account removed.", Toast.LENGTH_SHORT)
.show();
/* Reload account asynchronously to get the up-to-date list. */
loadAccounts();
}
#Override
public void onError(#NonNull MsalException exception) {
displayError(exception);
}
});
}
});
It always display the toast "Account removed", but it is actually not. Any help is appreciated!
Edit 1 - 12/12/2022
#Tinjzz This Answer is almost your exact scenario.
Question Description
"accounts are removed successfully, but when signing in again and the microsoft sign in intent is opened, the accounts can just be clicked to sign in without password"
#Rutha answer "This is happening because MSAL automatically refreshes your token after expiration. When user opens your app it checks if that token is already present and valid."
"you need to remove the cache as well to remove the account from the cache, find the account that need to be removed and then call PublicClientApplication.removeAccount()"
In a later answer, #Rutha notes "On Android we basically don't have any control on the cookies" "If you want the user to enter the password again then you should do this: AcquireTokenInteractive(scopes).WithPrompt(Prompt.ForceLogin);
Old Response
From the code posted, it looks like you are using an approach similar to this site with a separate loadAccounts() method. "Step 5.2: Load accounts"
However, in the MS MSAL Single and Multi-Account page, it notes:
"If your app is configured to use a broker, and a broker is installed on the device, the account won't be removed from the broker when you call removeAccount. Only tokens associated with your client are removed."
MS specifically recommends using "Call getAccounts to get a list of accounts currently known to the app."
So, the current setup may be using loadAccounts() per the first link, yet MS actually recommends getAccounts that specifically addresses only the internal MSAL token system.
I need to scan all the available wifi networks but the method "wifiManager.startScan()" is deprecated and I don't find an alternative.
Does anyone know a real alternative?
I tried to look for more info in the developers Android portal however, it doesn't provide any alternative, or at least I couldn't find them.
I already checked:
https://developer.android.com/reference/android/net/wifi/WifiManager.html#startScan%28%29
https://developer.android.com/guide/topics/connectivity/wifi-scan#java
I just need a list of the available networks and the information that we could get using "wifiManager.startScan()".
What do you recommend me?
It is marked deprecated with description: "The ability for apps to trigger scan requests will be removed in a future release."
Currently this method works with some restrictions. In fact if you take a closer look at Wi-Fi scanning overview restrictions
you can see that you can achieve your goal by meeting the conditions explained under Restrictions.
One more thing, if you are developing a system-privileged app or wondering how those apps get a wifi list even with location service turned off, they use android.Manifest.permission.NETWORK_SETUP_WIZARD or android.Manifest.permission.NETWORK_SETTINGS which are system|signature level permissions. Read WifiPermissionsUtil.java:
/**
* API to determine if the caller has permissions to get scan results. Throws SecurityException
* if the caller has no permission.
* #param pkgName package name of the application requesting access
* #param uid The uid of the package
*/
public void enforceCanAccessScanResults(String pkgName, int uid) throws SecurityException {
mAppOps.checkPackage(uid, pkgName);
// Apps with NETWORK_SETTINGS & NETWORK_SETUP_WIZARD are granted a bypass.
if (checkNetworkSettingsPermission(uid) || checkNetworkSetupWizardPermission(uid)) {
return;
}
// Location mode must be enabled
if (!isLocationModeEnabled()) {
// Location mode is disabled, scan results cannot be returned
throw new SecurityException("Location mode is disabled for the device");
}
// Check if the calling Uid has CAN_READ_PEER_MAC_ADDRESS permission.
boolean canCallingUidAccessLocation = checkCallerHasPeersMacAddressPermission(uid);
// LocationAccess by App: caller must have
// Coarse Location permission to have access to location information.
boolean canAppPackageUseLocation = checkCallersLocationPermission(pkgName, uid);
// If neither caller or app has location access, there is no need to check
// any other permissions. Deny access to scan results.
if (!canCallingUidAccessLocation && !canAppPackageUseLocation) {
throw new SecurityException("UID " + uid + " has no location permission");
}
// Check if Wifi Scan request is an operation allowed for this App.
if (!isScanAllowedbyApps(pkgName, uid)) {
throw new SecurityException("UID " + uid + " has no wifi scan permission");
}
// If the User or profile is current, permission is granted
// Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission.
if (!isCurrentProfile(uid) && !checkInteractAcrossUsersFull(uid)) {
throw new SecurityException("UID " + uid + " profile not permitted");
}
}
I am trying to clear a data from within the app and my app is device owner, hence I am getting and error
java.lang.SecurityExeception :Clearing DeviceOwner data is forbidden.
Code I am using is
public void onClearData(View view) {
try {
boolean isCleared = ((ActivityManager) getSystemService(ACTIVITY_SERVICE)).clearApplicationUserData();
if (!isCleared) {
Toast.makeText(this, "Not able to clear the data", Toast.LENGTH_SHORT).show();
}
} catch (Exception e) {
e.printStackTrace();
}
}
Now, my question is that how it will be possible to clear a data of device owner app from within the app? Would appreciate a help.
The way you're doing it is how it's done, according to the docs.
But since you're getting that security exception, your app is probably set as a device owner app, and you're not allowed to deactivate it, remove its data nor uninstall it while it is on this state.
If that's really the case I'd suggest you to unset it as a Device Owner App. Try to use dpm remove-active-admin for that.
Take a look at those questions for more info:
How to make my app a device owner?
How to remove set-device-owner in Android DPM?
Disable a device owner app from android terminal
I am integrating Android In-App purchase with Android Native application. I used following code to start In-App purchase intent!
Bundle buyIntentBundle = mService.getBuyIntent(3, getPackageName(),”product_sku”, "inapp",”Some Developer Payload”);
PendingIntent pendingIntent = buyIntentBundle.getParcelable("BUY_INTENT");
startIntentSenderForResult(pendingIntent.getIntentSender(),REQUEST_CODE, new Intent(),Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(0));
Above code is working with and open Google Play for In-app purchase. I overridden the onActivityResult for getting the result of In-App Purchase (following is code)
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 1001) {
if (resultCode == RESULT_OK) {
try {
final JSONObject jo = new JSONObject(purchaseData);
String orderId = jo.getString("orderId");
String productId = jo.getString("productId");
String state = String.valueOf(jo.getInt("purchaseState"));
//POST purchase result to my server for my record!
//Finally Consume the product
} catch (JSONException e) {
e.printStackTrace();
}
}
}
}
Following is response we received from InApp purchase
'{
"orderId":"12999763169054705758.1371079406387615",
"packageName":"com.example.app",
"productId":"exampleSku",
"purchaseTime":1345678900000,
"purchaseState":0,
"developerPayload":"bGoa+V7g/yqDXvKRqq+JTFn4uQZbPiQJo4pf9RzJ",
"purchaseToken":"rojeslcdyyiapnqcynkjyyjh"
}'
My questions is that how to identify that the in-app purchase transaction is in “Pending” status because "purchaseState" (Integer value in JSON) having only three values “purchased”,” canceled” and “refunded”.
I am from india and google wallet is accepting any transaction immediately after inapp purchase completed.
It is always in Pending status first and then takes upto 48 hours to accept or reject the transaction!
I want only allow my customer to use In app purchased product only after successfully payment in Google waltet.
I faced that My one customer purchased inapp product from my application but somehow their data is not posted on server. Customer told me that that purchase transaction was in pending status initially and completed after two days, even I got that payment in my Merchant account so I concluded that Android is not responding resultCode == RESULT_OK in onActivityResult is purchased is in pending.
So If anyone faced same problem then please explain me the solution or flow to handle the pending status! Should I have to use APN for pending status? But I need that pending status order details to store on my server!
Thanks in Advance!
I know this question was asked a long time ago. But I am facing the very same issue (with an App for the Brazilian market).
I noticed that purchases which got processed right away, everything works fine.
But, some purchases get into the 'pending payment' status and it can take up to 48 hours to change status. But, my server never gets called - as OP stated, I suspect Google play server returns a different status (other than RESULT_OK) and my app doesn't capture that.
Then, my user gets charged, but has no access to the PREMIUM features - since my server never got notified.
So, as of 2018, I read that we might have an alternative. The following link has some information on how to deal with subscriptions
https://developer.android.com/google/play/billing/billing_subscriptions
.. and points to the following:
https://developer.android.com/google/play/billing/realtime_developer_notifications
"Realtime Developer Notifications"
this allows Google play to send your server a notification, every time a user subscription status changes. So, if your user's payment took 48h to complete, your server would receive a notification and you could make the user PREMIUM or whatever logic you need.
Hope it helps.
I am using the facebook android sdk to access the user's facebook albums and intern get the photos.
But when I do a "https://graph.facebook.com/"+wallAlbumID+"/photos?access_token="+facebook.getAccessToken() it returns blank data. { "data": [] }
I read in stackoverflow question and also in Graph API for Photo that I need to give user_photospermission while creating the facebook object.
I am not sure how to do so.I read a lot of forums and also checked on SOF but could not find the solution.
This is how I am creating the facebook object
facebook = new Facebook(APP_ID);
Can anyone please help me with this.
You need to create an ArrayList of permissions you want to access. For example, when I did it, when declaring variables.
private static final String[] PERMISSIONS = { "user_photos" };
Then, when implementing the user login area:
if (!facebook.isSessionValid()) {
facebook.authorize(this, PERMISSIONS, new LoginDialogListener());
}
Hope this helps!
You need to use Facebook SDK 3.0+. Also, enable the OAuth client login flow in the developer settings on the FB site. Then you can use the following code:
First, log the user into FB inside of your app to get a Session object, then request permissions for user_photos if not granted yet:
// start Facebook Login
Session.openActiveSession(this, true, new Session.StatusCallback() {
// callback when session changes state
#Override
public void call(Session session, SessionState state, Exception exception) {
if (session.isOpened()) {
// check for permission to see user photos
if (!hasPhotoPermissions())
session.requestNewPublishPermissions(new Session.NewPermissionsRequest(MyActivity.this, "user_photos"));
else {
// photos permission granted, fetch user's albums
fetchAlbumsFromFB(session);
}
}
}
});
Here is what hasPhotoPermissions() method looks like:
private boolean hasPhotoPermissions() {
Session session = Session.getActiveSession();
return session.getPermissions().contains("user_photos");
}
Your app will then present the photos permissions screen to the user for approval.