ClassCastException while trying to use com.google.api.client.http.HttpTransport - android

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.

Related

AWS SDK Kotlin couldn't find credentials

I am using AWS SDK for Kotlin in my Android project and my app always crashes with error
aws.sdk.kotlin.runtime.auth.credentials.CredentialsProviderException: No credentials could be loaded from the chain: CredentialsProviderChain -> EnvironmentCredentialsProvider -> ProfileCredentialsProvider -> StsWebIdentityProvider -> EcsCredentialsProvider -> ImdsCredentialsProvider
I am using AWS secret manager and implementing the SDK in gradle as -
implementation 'aws.sdk.kotlin:secretsmanager:0.17.5-beta'
My Kotlin code looks like -
SecretsManagerClient { region = "ap-south-1" }.use { secretsClient ->
CoroutineScope(Dispatchers.IO).launch {
val secretValueRequest = GetSecretValueRequest {
secretId = "prod"
}
val response = secretsClient.getSecretValue(secretValueRequest)
val secret = response.secretString
print("Secret: $secret")
}
}
I tried to setup the credential in my Mac computer as mentioned in this article, for both environment variable and also having a file named credentials .
Can anyone please help me to fix the issue
Your problem here is the Android app will not read creds from the file system of your machine. To get around this, you can use the StaticCredentialsProvider shown in this example that is referenced from the Kotlin DEV Guide.
https://docs.aws.amazon.com/sdk-for-kotlin/latest/developer-guide/cross_SnsPublishSubscription_kotlin_topic.html
Github URL
https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/kotlin/usecases/subpub_app_android
Once you follow this -- you can successfully make AWS Service calls from the Android app.
UPDATE
After talking with the Kotlin team, you can setup your creds as environment variables then use this provider - which is mentioned in the docs here:
https://docs.aws.amazon.com/sdk-for-kotlin/latest/developer-guide/credential-providers.html
Code that works....
SnsClient { credentialsProvider = EnvironmentCredentialsProvider(); region = "us-east-1" }.use { snsClient ->
val result = snsClient.subscribe(request)
showToast(result.subscriptionArn.toString())
}
This Kotlin Android example will be updated and published soon as well.

NoClassDefFoundError dependency issues calling Gmail API

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.

Okhttp4, Cannot access 'body': it is package-private in 'Response'

i'm migrating from okhttp 3.14 to okhttp4, accordings to migration guide, when replacing "response.body()?" call to "response.body?" IDE shows error " Cannot access 'body': it is package-private in 'Response' " at last line of code
val request = Request.Builder().url(url).build()
val response = coreNetwork.getOkHttp().newCall(request).execute()
val stream = response.body?.source()?.inputStream()
if i'm trying "response.body()?" call, shows error " Using 'body(): ResponseBody?' is an error. moved to val " as expected.
Solved, i'm start watching to what version of okhttp linked every import, and one interface linked to okhttp-3.12.0.jar. This interface belongs to separate gradle module, and i found that i forgot add a dependency ' implementation "com.squareup.okhttp3:okhttp:4.2.2" ' to .gradle file of that module. (in that case class should not found okhttp dependency at all, but somehow found old version and depends on it)
This is an expected error message looking at the code for Response.kt in the 4.2.x version of the branch:
#JvmName("-deprecated_body")
#Deprecated(
message = "moved to val",
replaceWith = ReplaceWith(expression = "body"),
level = DeprecationLevel.ERROR)
fun body(): ResponseBody? = body
so the solution would be to call response.body? which it looks like you are already doing...
see: https://github.com/square/okhttp/blob/okhttp_4.2.x/okhttp/src/main/java/okhttp3/Response.kt#L202 for details

io.grpc.StatusRuntimeException: UNAUTHENTICATED: Credentials require channel with PRIVACY_AND_INTEGRIY

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.

Google Authentication in Android Unity Plugin

I'm working on an Android application that includes communication with the Amazon S3 servers. The app is being developed in Unity and I would like to include a system so that the users can authenticate with their Google Accounts and then use those credentials to access the S3 server through Cognito. To do that, I need to implement a Google Authenticator system in Unity, and I am having a hard time figuring out how to do it. My current approach consists in creating a Plugin with Android Studio to access the Google Sign In API, but every time I execute the program, it throws a NoClassDefFoundError exception. Here is my logcat:
03-25 20:45:34.968 25581-25610/? D/MainActivity: Authenticating...
03-25 20:45:35.086 25581-25610/? I/Unity: AndroidJavaException: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/android/gms/auth/api/signin/GoogleSignInOptions$Builder;
java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/android/gms/auth/api/signin/GoogleSignInOptions$Builder;
at com.unityplugin.MainActivity.authenticate(MainActivity.java:55)
at com.unity3d.player.UnityPlayer.nativeRender(Native Method)
at com.unity3d.player.UnityPlayer.a(Unknown Source)
at com.unity3d.player.UnityPlayer$b.run(Unknown Source)
Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.android.gms.auth.api.signin.GoogleSignInOptions$Builder" on path: DexPathList[[zip file "/data/app/com.unityplugin-2/base.apk"],nativeLibraryDirectories=[/data/app/com.unityplugin-2/lib/arm, /data/app/com.unityplugin-2/base.apk!/lib/armeabi-v7a, /vendor/lib, /system/lib]]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
at java.lang.ClassLoader.loadCla
Here is the relevant part of my Android code (UnityPlayer Activity):
public void authenticate() {
Log.d(TAG, "Authenticating...");
// Configure sign-in to request the user's ID, email address, and basic
// profile. ID and basic profile are included in DEFAULT_SIGN_IN.
//HERE IS THE ERROR (LINE 55)
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken(Constants.GOOGLE_CLIENT_ID)
.requestEmail()
.build();
// Build a GoogleApiClient with access to the Google Sign-In API and the
// options specified by gso.
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(Auth.GOOGLE_SIGN_IN_API, gso)
.addConnectionCallbacks(new ConnCallBack())
.addOnConnectionFailedListener(new FailedListener())
.build();
Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient);
startActivityForResult(signInIntent, RC_SIGN_IN);
}
The code works if I execute it in a native APK inside a Compact Activity, but when I make it into a plugin and run it with Unity, I get the error. In Unity, I call the authenticate() method with this code:
//Get Activity
AndroidJavaClass unity = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject currentActivity = unity.GetStatic<AndroidJavaObject>("currentActivity");
//Call function authenticate
currentActivity.Call("authenticate");
I have tried including the classes.jar file in the com.google.android.gms.play-services-auth-8.4.0 external library that I have in Android Studio, but it didn't work. I have also considered implementing the authentication directly in Unity instead of making a plugin, but all the information that I saw about doing something like that relates to Google Play Games, and I am not interested in including the Google Play Games API in my application, I just want to let the users log in with their Google account so that they can access the S3 server. If anyone has implemented a similar feature with Unity and knows a better way of doing this, I'm all ears. I am open to using a different way to enable Google Authentication in my app, the only requisite is that it has to be done in Unity.
Thanks in advance!
I solved my problem using this code to authenticate the user:
GooglePlayServicesUtil.isGooglePlayServicesAvailable(getApplicationContext());
AccountManager am = AccountManager.get(this);
Account[] accounts = am.getAccountsByType(GoogleAuthUtil.GOOGLE_ACCOUNT_TYPE);
String token = GoogleAuthUtil.getToken(getApplicationContext(), accounts[0].name,
"audience:server:client_id:YOUR_GOOGLE_CLIENT_ID");
Map<String, String> logins = new HashMap<String, String>();
logins.put("accounts.google.com", token);
credentialsProvider.setLogins(logins);
Also, I had to add the following libraries in form of JAR files:
android-support-v4
google-play-services

Categories

Resources