I'm trying to access my Gmail from an Android app and am running into dependency issues. I've been following a combination of guides, including the Java Quickstart (https://developers.google.com/gmail/api/quickstart/java) and a few others on StackOverflow and GitHub.
Whenever I run the emulator, I get a "NoClassDefFoundError", always from a Google API call to AuthorizationCodeInstalledApp.authorize(). Here's one of them:
java.lang.NoClassDefFoundError: Failed resolution of: Lsun/security/action/GetLongAction;
at sun.net.httpserver.ServerConfig.<clinit>(ServerConfig.java:43)
at sun.net.httpserver.ServerConfig.getClockTick(ServerConfig.java:100)
at sun.net.httpserver.ServerImpl.<clinit>(ServerImpl.java:50)
at sun.net.httpserver.HttpServerImpl.<init>(HttpServerImpl.java:32)
at sun.net.httpserver.DefaultHttpServerProvider.createHttpServer(DefaultHttpServerProvider.java:17)
at com.sun.net.httpserver.HttpServer.create(HttpServer.java:111)
at com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver.getRedirectUri(LocalServerReceiver.java:127)
at com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp.authorize(AuthorizationCodeInstalledApp.java:108)
at com.example.gmailtemplates.ui.templates.TemplateListAdapter.getCredentials(TemplateListAdapter.java:141)
Here's my code that calls it, which I literally copied and pasted from a guide:
// Build flow and trigger user authorization request.
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES)
.setDataStoreFactory(new FileDataStoreFactory(tokenFolder))
.setAccessType("offline")
.build();
LocalServerReceiver receiver = new LocalServerReceiver.Builder().setPort(8888).build();
Credential credential = new AuthorizationCodeInstalledApp(flow, receiver).authorize("user");
return credential;
My gradle build file includes:
repositories {
mavenCentral()
}
dependencies {
implementation 'com.google.api-client:google-api-client:1.32.2'
implementation 'com.google.oauth-client:google-oauth-client-jetty:1.32.1'
implementation 'com.google.apis:google-api-services-gmail:v1-rev20211108-1.32.1'
implementation 'com.google.android.gms:play-services-auth:20.0.0'
....
}
The thing is that I'm pretty sure that sun.net.httpserver is pretty old and no longer used. Am I missing something or using some weird old version of Google API? I'm using Android Studio for the first time, so it's possible that I made some wrong configuration.
Related
I have the following setup in my grade files:
project:
classpath 'com.google.gms:google-services:4.3.10'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1'
app:
implementation platform('com.google.firebase:firebase-bom:29.0.0')
// When using the BoM, you don't specify versions in Firebase library dependencies
implementation 'com.google.firebase:firebase-crashlytics'
implementation 'com.google.firebase:firebase-analytics'
implementation 'com.google.firebase:firebase-messaging'
After the users logs in, I'm doing
FirebaseMessaging.getInstance().getToken().addOnCompleteListener(task -> {
String refreshToken = task.getResult();
....
}
which is fine for most users, but for some (not only a few) get errors like FIS_AUTH_ERROR, MISSING_INSTANCEID_SERVICE, SERVICE_NOT_AVAILABLE.
I guess (!), MISSING_INSTANCEID_SERVICE is no Google API on the device, SERVICE_NOT_AVAILABLE missing network (?) but I'm not sure at all. And I have absolutely no clue about the FIS_AUTH_ERROR. Is there some documentation about this?
MISSING_INSTANCEID_SERVICE
Tokens can't be generated.
SERVICE_NOT_AVAILABLE
The device cannot read the response, or there was a server error.
FIS_AUTH_ERROR
you need to check the documentation get token and get id methods had changed
I think it has to do with firebase dependencies versions mismatch. Try to import the BoM for the Firebase platform. By using BoM your app will always use compatible versions of the Firebase Android libraries.
dependencies {
// Import the BoM for the Firebase platform
implementation platform('com.google.firebase:firebase-bom:30.0.2')
// .... }
FIS_AUTH_ERROR suggests, that the local installation cannot be authenticated:
Firebase Cloud Messaging uses Firebase installation IDs to target devices for message delivery.
Source: Manage Firebase installations
It's a little difficult, unless having a device available, which would show this issue - or somehow being able to narrow this down - by hardware vendor, Android version or package version - because "some (not only a few)" is relatively meaningless, from a technical perspective.
It could well be, that the SHA1/SHA265 key fingerprint might have been changed (you should know) and "some (not only a few)" might still haven't updated to the latest version of the APK/AAB package, signed with the new signing key. Crashlytics would show the package version. You could add the old SHA1/SHA265 key fingerprint back in, so that their instances could authenticate again.
Basically, I can't find an implementation of com.google.api.client.http.HttpTransport that works in Android SDK 31.
I'm trying to get started with the google-signin API, and I'm getting a ClassCastException.
I'm using code taken nearly verbatim from the google api examples.
val credentialStream = resources.openRawResource(R.raw.credentials)
?: throw FileNotFoundException("Resource not found: $CREDENTIALS_FILE_PATH")
val clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, InputStreamReader(credentialStream))
// Build flow and trigger user authorization request.
val flow = GoogleAuthorizationCodeFlow.Builder(NetHttpTransport(), JSON_FACTORY, clientSecrets, SCOPES)
.setDataStoreFactory(FileDataStoreFactory(requireContext().filesDir?.resolve(File(TOKENS_DIRECTORY_PATH))))
.setAccessType("offline")
.build()
val receiver = LocalServerReceiver.Builder().setPort(8888).build()
//returns an authorized Credential object.
return AuthorizationCodeInstalledApp(flow, receiver).authorize("user#gmail.com")
Every test results in an exception at the last line.
java.lang.NoClassDefFoundError: Failed resolution of: Lcom/sun/net/httpserver/HttpServer;
...
Caused by: java.lang.ClassNotFoundException: Didn't find class "com.sun.net.httpserver.HttpServer" on path: DexPathList[[dex file "/data/d ...
Any ideas? I'm pretty novice to android development.
One solution I found was to add rt.jar from the desktop JVM as a dependency, because it includes that missing class. Check here:
Is it possible to use com.sun.net.httpserver package in android program?
The solution I ended up using was to using Google Play Services instead of GoogleAuthorizationCodeFlow.
In gradle:
implementation 'com.google.android.gms:play-services-auth:20.1.0'
implementation ('com.google.api-client:google-api-client-android:1.34.0')
In my ViewModel:
val applicationName = "My Application"
val scopes = listOf( ... some scopes go here ...)
val credential = GoogleAccountCredential.usingOAuth2(application, scopes)
.setBackOff(ExponentialBackOff())
.setSelectedAccountName(accountName)
credential can then be used to access other parts of the Google API.
I'm trying to understand how I can integrate Google Drive API in my Android App(using Android Studio). I have tried the example app that I found here(https://github.com/googleworkspace/android-samples/tree/master/drive/deprecation) but after Google API console configuration my app stucks in an infinite loop when I choose an Account. Why? Where is the problem?
I tried also with this(https://youtu.be/5bSEIQYJzKs) tutorial but I have the same problem.
Here my build.gradle specific lines:
implementation "androidx.multidex:multidex:2.0.1"
implementation 'com.google.android.gms:play-services-auth:19.0.0'
implementation ('com.google.apis:google-api-services-drive:v3-rev136-1.25.0')
{
exclude group: 'org.apache.httpcomponents'
}
implementation ('com.google.api-client:google-api-client-android:1.26.0')
{
exclude group: 'org.apache.httpcomponents'
}
implementation 'com.google.http-client:google-http-client-gson:1.26.0'
and here is the version that I use
compileSdkVersion 30
buildToolsVersion "30.0.3"
The problem is in this code lines, and occurs only if I use ".requestScopes(new Scope(DriveScopes.DRIVE_FILE))":
if i donot use .requestScopes(new Scope(DriveScopes.DRIVE_FILE))", then code works fine. and user do get logedin
GoogleSignInOptions signInOptions = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestScopes(new Scope(DriveScopes.DRIVE_FILE))
.requestEmail()
.build();
GoogleSignInClient client= GoogleSignIn.getClient(this,signInOptions);
Intent i= client.getSignInIntent();
startActivityForResult(i,400);
(it seems like the activity never returns any value).
Without ".requestScopes(new Scope(DriveScopes.DRIVE_FILE))" the auth request is successful.
Here my Google console configuration:
The client ID
(sorry, maybe it will appear as a link but stackOverflow tell me that I can't put img)
and
".../auth/drive.file" is the authorization that I request in google Consent window that appears to the user.
I don't know if it is important to say but I use a real phone and not the Android Studio emulator to test my app.
I just solved it. For all those who encounter this problem: from the Google API console, remove the consent screen from the test state and publish it.
Take a look at https://issuetracker.google.com/issues/178183308 where the issue was reported to Google.
I am using Dialogflow Java Client library in my android app, to run the detect intent API as given in below link.
https://github.com/dialogflow/dialogflow-java-client-v2/blob/master/samples/src/main/java/com/example/dialogflow/DetectIntentTexts.java
I modified the code given in above link slightly to authenticate the client first before sending the detect intent request. My sample code is as follows:
SessionsSettings sessionsSettings = SessionsSettings.newBuilder().setCredentialsProvider(credentialsProvider).build();
SessionsClient sessionsClient = SessionsClient.create(sessionsSettings);
SessionName session = SessionName.of(PROJECT_ID, sessionId);
System.out.println("Session Path: " + session.toString());
TextInput.Builder textInput = TextInput.newBuilder().setText(text).setLanguageCode(langCode);
QueryInput queryInput=QueryInput.newBuilder().setText(textInput).build();
DetectIntentResponse response = sessionsClient.detectIntent(session, queryInput);
where
CredentialsProvider credentialsProvider = new CredentialsProvider() {
#Override
public Credentials getCredentials() throws IOException {
InputStream fileStream = appContext.getApplicationContext().getAssets().open("MyDialogflowProject-4cxxxxx.json");
return ServiceAccountCredentials.fromStream(fileStream);
}
};
But I get the following error
com.google.api.gax.rpc.UnauthenticatedException:
io.grpc.StatusRuntimeException: UNAUTHENTICATED: Credentials require
channel with PRIVACY_AND_INTEGRITY security level. Observed security
level: NONE
at com.google.api.gax.rpc.ApiExceptionFactory.createException(ApiExceptionFactory.java:73)
Can anyone please tell how to set the Security Level for SessionSettings in this case ?
Try to update library versions you are using. On transport layer Security Level should be setup.
For example gradle dependencies from my hello world project which is working with dialogflow v2:
dependencies {
compile ("com.google.api.grpc:proto-google-common-protos:1.12.0")
compile ("io.grpc:grpc-netty:1.14.0")
compile ("io.grpc:grpc-protobuf:1.14.0")
compile ("io.grpc:grpc-stub:1.14.0")
compile ("com.google.auth:google-auth-library-oauth2-http:0.10.0")
compile ("com.google.cloud:google-cloud-storage:1.38.0")
compile ("io.netty:netty-tcnative-boringssl-static:2.0.12.Final")
compile ("com.google.cloud:google-cloud-dialogflow:0.55.1-alpha")
}
In my case, I have found out that I have set the IntelliJ environment variable to FIRESTORE_EMULATOR_HOST=localhost:8080 to use the Firebase emulators in the past.
Later I tried to push data to Cloud Firestore without removing that variable, but the attempts were unsuccessful with the error PRIVACY_AND_INTEGRITY.
After some time, I found the localhost setting and removed it. Once removed, I was able to push the data to the cloud without any issues.
I have code for an Android app that uses S3. I had set it up almost a year ago following this example, where, in the app dependencies, it declares:
dependencies {
compile 'com.amazonaws:aws-android-sdk-core:2.2.+'
compile 'com.amazonaws:aws-android-sdk-s3:2.2.+'
}
The current version at the time of writing this is 2.6.7.
Now, my problem is:
//This code came from the Mobile Hub sample application
//Obtain a reference to the mobile client. It is created in the Application class.
final AWSMobileClient awsMobileClient = AWSMobileClient.defaultMobileClient();
//Obtain a reference to the identity manager.
IdentityManager identityManager = awsMobileClient.getIdentityManager();
AmazonS3 s3Client = new AmazonS3Client(identityManager.getCredentialsProvider());
try{
//This used to return TRUE but now returns FALSE
boolean objectExists = s3Client.doesObjectExist(BUCKET_NAME, key);
...
//catch, etc.
Consequently, I am unable to download objects. I assume that this is somehow an authentication-related issue, and in following security principles is just telling me the object doesn't exist.
Nothing about the bucket name or keys has changed on either end, and I verified that the issue can be triggered and resolved by changing:
compile 'com.amazonaws:aws-android-sdk-s3:2.2.+' //works
to
compile 'com.amazonaws:aws-android-sdk-s3:2.6.7' //does not work
and rebuilding/running the app.
I'm unable to find anything in the documentation or anything about changes in versions that would cause this. Does anyone know? I've yet to see how high I can go in version without this problem occurring.
EDIT: 2.3.9 works fine, 2.4.0 is where the problem arises. I can't ascertain which of those changes could cause the issue.
If you are using version >= 2.6.0,
dependencies {
compile ('com.amazonaws:aws-android-sdk-auth-core:2.6.0#aar') { transitive = true; }
compile 'com.amazonaws:aws-android-sdk-core:2.6.+'
compile 'com.amazonaws:aws-android-sdk-s3:2.6.0'
}
1) Create an instance of IdentityManager:
import com.amazonaws.mobile.config.AWSConfiguration;
import com.amazonaws.mobile.auth.core.IdentityManager;
IdentityManager idm = new IdentityManager(getApplicationContext(), new AWSConfiguration(getApplicationContext()));
IdentityManager.setDefaultIdentityManager(idm);
2) Use it with S3Client.
AmazonS3 s3Client = new AmazonS3Client(IdentityManager.getDefaultIdentityManager().getCredentialsProvider());
try{
//This used to return TRUE but now returns FALSE
boolean objectExists = s3Client.doesObjectExist(BUCKET_NAME, key);
...
//catch, etc.